/** * @dataProvider expressions * @covers \Pinq\Expressions\ExpressionVisitor */ public function testExpressionVisitorVisitsTheCorrectMethod(O\Expression $expression) { $method = 'visit' . $expression->getExpressionTypeName(); $expressionVisitorMock = $this->getMock('\\Pinq\\Expressions\\ExpressionVisitor', [$method]); $expressionVisitorMock->expects($this->once())->method($method)->with($this->equalTo($expression)); $expressionVisitorMock->walk($expression); }
protected function getMetadata(O\Expression $expression) { if (!isset($this->metadata[$expression])) { throw new TypeException('Cannot get metadata for expression of type \'%s\': the expression has no associated metadata', $expression->getType()); } return $this->metadata[$expression]; }
public function __construct(O\Expression $expression, IParameterHasher $hasher, IFunction $function = null, $data = null) { parent::__construct($hasher, $data); if ($function !== null) { $this->contextFactory = $function->getEvaluationContextFactory(); $this->evaluator = $expression->asEvaluator($this->contextFactory->getEvaluationContext()); } else { $this->evaluator = $expression->asEvaluator(); } $this->data = $data; }
public function __construct(Providers\IQueryProvider $provider, Queries\ISourceInfo $sourceInfo, O\TraversalExpression $queryExpression = null, IIteratorScheme $scheme = null) { parent::__construct($provider); $this->sourceInfo = $sourceInfo; $this->expression = $queryExpression ?: O\Expression::value($this); $this->scheme = $scheme ?: Iterators\SchemeProvider::getDefault(); }
public function appendFunction(IFunction $function) { if ($function->isInternal()) { throw new Exception('Internal functions are not supported'); } $this->append('{'); if ($function->countBodyExpressions() === 0) { $this->append('}'); } elseif ($function->countBodyExpressions() === 1) { $this->append(' '); $this->append($function->getBodyExpressions()[0]->compile() . ';'); $this->append(' }'); } else { $this->appendLine(); $this->append(implode(';' . PHP_EOL, Expression::compileAll($function->getBodyExpressions())) . ';'); $this->appendLine(); $this->append('}'); } $parameterMap = $function->getParameterScopedVariableMap(); if (!empty($parameterMap)) { $this->append(' with parameters: ['); $parameters = []; foreach ($parameterMap as $variableName) { $parameters[] = "\${$variableName}"; } $this->append(implode(', ', $parameters)); $this->append(']'); } }
/** * @dataProvider interpreters */ public function testParameterExpressions() { $this->assertParametersAre(function ($i) { }, [O\Expression::parameter('i')]); $this->assertParametersAre(function ($i, $foo) { }, [O\Expression::parameter('i'), O\Expression::parameter('foo')]); $this->assertParametersAre(function ($i = null) { }, [O\Expression::parameter('i', null, O\Expression::value(null))]); $this->assertParametersAre(function ($i = 'bar') { }, [O\Expression::parameter('i', null, O\Expression::value('bar'))]); $this->assertParametersAre(function (\DateTime $i) { }, [O\Expression::parameter('i', '\\DateTime')]); $this->assertParametersAre(function (self $i) { }, [O\Expression::parameter('i', '\\' . __CLASS__)]); $this->assertParametersAre(function (parent $i) { }, [O\Expression::parameter('i', '\\' . get_parent_class())]); $this->assertParametersAre(function (MiscInterpreterTest $i) { }, [O\Expression::parameter('i', '\\' . __CLASS__)]); $this->assertParametersAre(function (ParameterClassTest $i) { }, [O\Expression::parameter('i', '\\' . __NAMESPACE__ . '\\ParameterClassTest')]); $this->assertParametersAre(function (namespace\ParameterClassTest $i) { }, [O\Expression::parameter('i', '\\' . __NAMESPACE__ . '\\ParameterClassTest')]); $this->assertParametersAre(function (&$i) { }, [O\Expression::parameter('i', null, null, true)]); $this->assertParametersAre(function (\stdClass &$i = null, array $array = ['foo']) { }, [O\Expression::parameter('i', '\\stdClass', O\Expression::value(null), true), O\Expression::parameter('array', 'array', O\Expression::value(['foo']))]); $this->assertParametersAre(function (callable &$v = null) { }, [O\Expression::parameter('v', 'callable', O\Expression::value(null), true)]); $this->assertParametersAre(function ($v = [1, 2, 3, 'test' => 'foo', [2 => 'boo', '']]) { }, [O\Expression::parameter('v', null, O\Expression::value([1, 2, 3, 'test' => 'foo', [2 => 'boo', '']]))]); }
public function walkVariable(VariableExpression $expression) { $name = $expression->getName(); if ($name instanceof O\ValueExpression) { return $expression->update(O\Expression::value($this->prefix . $name->getValue())); } return $expression->update(O\Expression::binaryOperation(O\Expression::value($this->prefix), O\Operators\Binary::CONCATENATION, $name)); }
public function testAssignmentToBinaryOperatorEquivalent() { foreach ([O\Operators\Assignment::EQUAL, O\Operators\Assignment::EQUAL_REFERENCE] as $operatorThatShouldNotChange) { $assignment = O\Expression::assign(O\Expression::variable(O\Expression::value('foo')), $operatorThatShouldNotChange, O\Expression::variable(O\Expression::value('bar'))); $this->assertSame($assignment, $assignment->toBinaryOperationEquivalent()); } $assignment = O\Expression::assign(O\Expression::variable(O\Expression::value('foo')), O\Operators\Assignment::ADDITION, O\Expression::variable(O\Expression::value('bar'))); $this->assertEquals(O\Expression::assign(O\Expression::variable(O\Expression::value('foo')), O\Operators\Assignment::EQUAL, O\Expression::binaryOperation(O\Expression::variable(O\Expression::value('foo')), O\Operators\Binary::ADDITION, O\Expression::variable(O\Expression::value('bar')))), $assignment->toBinaryOperationEquivalent()); }
public function testFunctionGetsReturnWithoutValueExpression() { /** @var $function Functions\ProjectionBase */ $function = $this->functionWithEmptyReturnStatement(); $this->assertSame(true, $function->hasReturnExpression()); $this->assertEquals(O\Expression::returnExpression(), $function->getReturnExpression()); $this->assertSame(false, $function->hasReturnValueExpression()); $this->assertEquals(null, $function->getReturnValueExpression()); }
private function recompile(Parsing\IFunctionReflection $reflection, Parsing\IFunctionStructure $structure, &$closureExpression = null) { $signature = $reflection->getSignature(); $usedVariables = array_map(function ($name) { return O\Expression::closureUsedVariable($name); }, $signature->getScopedVariableNames() ?: []); $closureExpression = O\Expression::closure($signature->returnsReference(), $reflection->getInnerReflection()->getClosureScopeClass() === null, $signature->getParameterExpressions(), $usedVariables, $structure->getBodyExpressions()); return $closureExpression->evaluate($reflection->asEvaluationContext()); }
public function testExpressionWalkerWorks() { $expression = O\Expression::variable(O\Expression::value('foo')); $expressionWalker = new O\DynamicExpressionWalker([O\ValueExpression::getType() => function (O\ValueExpression $expression) { return O\Expression::value('bar'); }]); $newExpression = $expressionWalker->walk($expression); $this->assertNotEquals($expression, $newExpression); $this->assertSame($newExpression->getName()->getValue(), 'bar'); }
public function testWalksBodyAndParameterExpressions() { $processor = $this->getMock('Pinq\\Providers\\DSL\\Compilation\\Processors\\Expression\\ExpressionProcessor', ['walkValue']); $processor->expects($this->exactly(2))->method('walkValue')->will($this->returnValue(O\Expression::value('updated'))); /** @var ExpressionProcessor $processor */ $function = new Functions\ElementProjection('', null, null, [], [O\Expression::parameter('param', 'abc', O\Expression::value('default'))], [O\Expression::value('body')]); $processedFunction = $processor->processFunction($function); $this->assertEquals([O\Expression::value('updated')], $processedFunction->getBodyExpressions()); $this->assertEquals(O\Expression::parameter('param', 'abc', O\Expression::value('updated')), $processedFunction->getParameters()->getValue()); }
public function testFullParameters() { /** @var $function Functions\Aggregator */ $function = $this->buildFunction('', null, null, [], [O\Expression::parameter('aggregate'), O\Expression::parameter('value')]); $this->assertSame(true, $function->getParameters()->hasAggregateValue()); $this->assertSame('aggregate', $function->getParameters()->getAggregateValue()->getName()); $this->assertSame(true, $function->getParameters()->hasValue()); $this->assertSame('value', $function->getParameters()->getValue()->getName()); $this->assertSame(false, $function->getParameters()->hasRequiredUnusedParameters()); $this->assertSame([], $function->getParameters()->getRequiredUnusedParameters()); $this->assertSame([], $function->getParameters()->getUnused()); $this->assertSame([], $function->getParameters()->getUnusedParameterDefaultMap()); }
protected final function visit(O\Expression $expression) { if ($expression instanceof O\ValueExpression) { $queryable = $expression->getValue(); if (!$queryable instanceof IQueryable) { throw new PinqException('Invalid scope expression: must originate from %s, %s given', IQueryable::IQUERYABLE_TYPE, Utilities::getTypeOrClass($queryable)); } if ($queryable->isSource()) { $this->addSegment(function () use($queryable) { $this->interpretation->interpretScopeSource($queryable); }); return; } $expression = $queryable->getExpression(); } $methodName = $this->getMethodName($expression); $this->segmentCounter++; $this->segmentId = "{$this->segmentCounter}-{$methodName}"; if (!method_exists($this, "visit{$methodName}")) { throw new PinqException('Cannot interpret query scope with method call \'%s\'', $methodName); } $this->{"visit{$methodName}"}($expression); }
public function testFullParameters() { /** @var $function Functions\ConnectorProjection */ $function = $this->buildFunction('', null, null, [], [O\Expression::parameter('outer-value'), O\Expression::parameter('inner-value'), O\Expression::parameter('outer-key'), O\Expression::parameter('inner-key')]); $this->assertSame(true, $function->getParameters()->hasOuterKey()); $this->assertSame('outer-value', $function->getParameters()->getOuterValue()->getName()); $this->assertSame(true, $function->getParameters()->hasInnerValue()); $this->assertSame('inner-value', $function->getParameters()->getInnerValue()->getName()); $this->assertSame(true, $function->getParameters()->hasOuterKey()); $this->assertSame('outer-key', $function->getParameters()->getOuterKey()->getName()); $this->assertSame(true, $function->getParameters()->hasInnerKey()); $this->assertSame('inner-key', $function->getParameters()->getInnerKey()->getName()); $this->assertSame(false, $function->getParameters()->hasRequiredUnusedParameters()); $this->assertSame([], $function->getParameters()->getRequiredUnusedParameters()); $this->assertSame([], $function->getParameters()->getUnused()); $this->assertSame([], $function->getParameters()->getUnusedParameterDefaultMap()); }
public function testFunctionWithDefaultRelativeConstant() { /** @var $function Functions\ElementProjection */ $function = $this->buildFunction('', null, __NAMESPACE__, [], [O\Expression::parameter('value'), O\Expression::parameter('key'), O\Expression::parameter('excessive1', null, O\Expression::constant('__RELATIVE_CONSTANT')), O\Expression::parameter('excessive2', null, O\Expression::value([false]))]); $this->assertSame(false, $function->getParameters()->hasRequiredUnusedParameters()); $this->assertEquals([], $function->getParameters()->getRequiredUnusedParameters()); $this->assertEquals([O\Expression::parameter('excessive1', null, O\Expression::constant('__RELATIVE_CONSTANT')), O\Expression::parameter('excessive2', null, O\Expression::value([false]))], $function->getParameters()->getUnused()); $this->assertEquals(['excessive1' => O\Expression::constant('__RELATIVE_CONSTANT'), 'excessive2' => O\Expression::value([false])], $function->getParameters()->getUnusedParameterDefaultMap()); //This fails in PHP due to strange behaviour with closures and relative constants. //Bug reported: https://bugs.php.net/bug.php?id=67897 if (defined('HHVM_VERSION')) { define('__RELATIVE_CONSTANT', 'in global namespace'); $this->assertEquals(['excessive1' => 'in global namespace', 'excessive2' => [false]], $function->getEvaluationContextFactory()->getEvaluationContext()->getVariableTable()); //Should take precedence define(__NAMESPACE__ . '\\__RELATIVE_CONSTANT', 'in relative namespace'); $this->assertEquals(['excessive1' => 'in relative namespace', 'excessive2' => [false]], $function->getEvaluationContextFactory()->getEvaluationContext()->getVariableTable()); } }
public function testFunctionWithReturnStatement() { $function = $this->functionWithReturnStatement(); $this->assertSame('', $function->getCallableId()); $this->assertSame(1, $function->getParameters()->count()); $this->assertEquals([O\Expression::parameter('foo')], $function->getParameters()->getAll()); $this->assertEquals(['param' => 'scope'], $function->getParameterScopedVariableMap()); $this->assertEquals(['', 'param'], $function->getParameterIds()); $this->assertSame(true, $function->hasScopeType()); $this->assertSame(__CLASS__, $function->getScopeType()); $this->assertSame(true, $function->hasNamespace()); $this->assertSame(__NAMESPACE__, $function->getNamespace()); $this->assertSame(4, $function->countBodyExpressions()); $this->assertSame(2, $function->countBodyExpressionsUntilReturn()); $this->assertEquals([O\Expression::value('before'), O\Expression::returnExpression(O\Expression::value('return')), O\Expression::value('after'), O\Expression::functionCall(O\Expression::value('boom'))], $function->getBodyExpressions()); $this->assertEquals([O\Expression::value('before'), O\Expression::returnExpression(O\Expression::value('return'))], $function->getBodyExpressionsUntilReturn()); $this->assertEquals(new O\EvaluationContext(__NAMESPACE__, __CLASS__, null, ['scope' => 'value']), $function->getEvaluationContextFactory()->getEvaluationContext(new ResolvedParameterRegistry(['param' => 'value']))); }
public function interpretSource(O\Expression $expression) { $isQueryScope = false; $queryableQueryResolver = new O\DynamicExpressionWalker([O\TraversalExpression::getType() => function (O\TraversalExpression $expression, O\ExpressionWalker $self) use(&$isQueryScope) { $expression = $expression->updateValue($self->walk($expression->getValue())); if ($isQueryScope) { return $expression; } else { return $self->walk(O\Expression::value($expression->evaluate($this->evaluationContext))); } }, O\ValueExpression::getType() => function (O\ValueExpression $expression) use(&$isQueryScope) { if ($expression->getValue() instanceof IQueryable) { $isQueryScope = true; } return $expression; }]); $expression = $queryableQueryResolver->walk($expression); if ($isQueryScope) { $this->scopeInterpreter->interpretScope($expression); $this->interpretation->interpretQueryScope($this->getId('source-scope'), $this->scopeInterpreter->getInterpretation()); } else { $this->interpretation->interpretArrayOrIterator($this->getId('source-iterator'), $expression->evaluate($this->evaluationContext)); } }
public function inline(IFunction $function, O\Expression $expression, Parameters\ResolvedParameterRegistry $parameters) { /** @var $expression O\VariableExpression */ return $expression->update($this->getResolvedValueExpression($parameters, $expression->getName())); }
public function tryComputeResults(O\Expression $queryExpression, &$results) { if (isset($this->storage[$queryExpression])) { $results = $this->storage[$queryExpression]; return true; } $foundApplicableResults = false; //Searches the query expression tree and checks if any parent expressions have saved results //If so, the expression tree is updated with a Traversable implementation with the saved results $applicableScopeFinder = function (O\Expression $expression, O\ExpressionWalker $self) use(&$foundApplicableResults) { if (isset($this->storage[$expression])) { $foundApplicableResults = true; return O\Expression::value($this->newTraversable($this->storage[$expression])); } if ($expression instanceof O\ValueExpression) { return $expression; } /** @var $expression O\TraversalExpression */ return $expression->updateValue($self->walk($expression->getValue())); }; $traversalWalker = new O\DynamicExpressionWalker([O\TraversalExpression::getType() => $applicableScopeFinder, O\ValueExpression::getType() => $applicableScopeFinder]); $remainingQueryExpression = $traversalWalker->walk($queryExpression); //If found applicable results, execute the updated expression tree against the Traversable //implementation to compute the result of the query. $results = $foundApplicableResults ? $remainingQueryExpression->evaluate() : null; return $foundApplicableResults; }
protected function validateStaticClassName(O\Expression $expression, $type) { if ($expression instanceof O\ValueExpression) { return $expression->getValue(); } else { throw new TypeException('Invalid %s expression: dynamic class types are not supported', $type); } }
/** * @dataProvider parsers */ public function testArgumentUnpacking() { $this->assertParsedAs([$this->parameters, 'argumentUnpacking'], [O\Expression::functionCall(O\Expression::value('func'), [O\Expression::argument(O\Expression::arrayExpression([]), true)])]); }
public function walkConstant(O\ConstantExpression $expression) { $resolvedMagicConstant = $this->resolveMagicConstantValue($expression->getName()); if ($resolvedMagicConstant !== null) { return O\Expression::value($resolvedMagicConstant); } else { return $expression; } }
protected function compileCode(&$code) { $this->assignTo->compileCode($code); $code .= ' ' . $this->operator . ' '; $this->assignmentValue->compileCode($code); }
private function parseOperatorNode(Node\Expr $node) { $nodeType = str_replace('Expr_', '', $node->getType()); switch (true) { case isset(self::$assignOperatorsMap[$nodeType]): return Expression::assign($this->parseNode($node->var), self::$assignOperatorsMap[$nodeType], $this->parseNode($node->expr)); case $node instanceof Node\Expr\Instanceof_: return Expression::binaryOperation($this->parseNode($node->expr), Operators\Binary::IS_INSTANCE_OF, $this->parseNameNode($node->class)); case isset(self::$binaryOperatorsMap[$nodeType]): return Expression::binaryOperation($this->parseNode($node->left), self::$binaryOperatorsMap[$nodeType], $this->parseNode($node->right)); case isset(self::$unaryOperatorsMap[$nodeType]): return Expression::unaryOperation(self::$unaryOperatorsMap[$nodeType], $this->parseNode(isset($node->expr) ? $node->expr : $node->var)); case isset(self::$castOperatorMap[$nodeType]): return Expression::cast(self::$castOperatorMap[$nodeType], $this->parseNode($node->expr)); default: return null; } }
protected function visitEmpty(O\EmptyExpression $expression) { $this->walk(O\Expression::unaryOperation(Operators\Unary::NOT, $expression->getValue())); }
private static function getParameterExpression(\ReflectionParameter $parameter) { $typeHint = null; if ($parameter->isArray()) { $typeHint = 'array'; } elseif ($parameter->isCallable()) { $typeHint = 'callable'; } elseif ($parameter->getClass() !== null) { $typeHint = $parameter->getClass()->getName(); $typeHint = $typeHint[0] === '\\' ? $typeHint : '\\' . $typeHint; } return O\Expression::parameter($parameter->getName(), $typeHint, $parameter->isDefaultValueAvailable() ? O\Expression::value($parameter->getDefaultValue()) : null, $parameter->isPassedByReference(), method_exists($parameter, 'isVariadic') && $parameter->isVariadic()); }
public function createReference(O\Expression $expression, O\Expression $referencedExpression) { $this->expressionTypes[$expression->hash()] =& $this->expressionTypes[$referencedExpression->hash()]; }
protected function parseQueryExpression(callable $queryFunction, O\IEvaluationContext &$evaluationContext = null) { $reflection = $this->functionInterpreter->getReflection($queryFunction); $evaluationContext = $reflection->asEvaluationContext(); $function = $this->functionInterpreter->getStructure($reflection); $expressions = $function->getBodyExpressions(); $this->assertCount(1, $expressions); //Resolve the parameter variable with the queryable value and $this $parameterName = $reflection->getSignature()->getParameterExpressions()[0]->getName(); $expression = $expressions[0]; foreach ([$parameterName => $this->queryable, 'this' => $this] as $variable => $value) { $variableReplacer = new O\DynamicExpressionWalker([O\VariableExpression::getType() => function (O\VariableExpression $expression) use($variable, &$value) { if ($expression->getName() instanceof O\ValueExpression && $expression->getName()->getValue() === $variable) { return O\Expression::value($value); } else { return $expression; } }, O\ClosureExpression::getType() => function ($closure) { return $closure; }]); $expression = $variableReplacer->walk($expression); } if ($expression instanceof O\ReturnExpression) { return $expression->getValue(); } else { return $expression; } }
/** * @param string $name * @param mixed[] $arguments * * @return O\MethodCallExpression */ protected function newMethod($name, array $arguments = []) { return O\Expression::methodCall($this->expression, O\Expression::value($name), array_map([O\Expression::getType(), 'argument'], array_map([O\Expression::getType(), 'value'], $arguments))); }