/** * $a &= $b; * * @param \PhpParser\Node\Expr\AssignRef $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { $compiler = $context->getExpressionCompiler(); if ($expr->var instanceof VariableNode) { $name = $expr->var->name; $compiledExpression = $compiler->compile($expr->expr); $symbol = $context->getSymbol($name); if ($symbol) { $symbol->modify($compiledExpression->getType(), $compiledExpression->getValue()); } else { $symbol = new \PHPSA\Variable($name, $compiledExpression->getValue(), $compiledExpression->getType(), $context->getCurrentBranch()); $context->addVariable($symbol); } if ($expr->expr instanceof VariableNode) { $rightVarName = $expr->expr->name; $rightSymbol = $context->getSymbol($rightVarName); if ($rightSymbol) { $rightSymbol->incUse(); $symbol->setReferencedTo($rightSymbol); } else { $context->debug('Cannot fetch variable by name: ' . $rightVarName); } } $symbol->incSets(); return $compiledExpression; } $context->debug('Unknown how to pass symbol by ref'); return new CompiledExpression(); }
/** * @param Node\Scalar $scalar * @return CompiledExpression */ public function compile(Node\Scalar $scalar) { try { $this->eventManager->fire(Event\ScalarBeforeCompile::EVENT_NAME, new Event\ScalarBeforeCompile($scalar, $this->context)); return $this->factory($scalar); } catch (\Exception $e) { $this->context->debug('ScalarCompiler is not implemented for ' . get_class($scalar)); } }
/** * @param \PhpParser\Node\Expr\MethodCall $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { if ($expr->var instanceof Variable) { $symbol = $context->getSymbol($expr->var->name); if ($symbol) { switch ($symbol->getType()) { case CompiledExpression::OBJECT: case CompiledExpression::DYNAMIC: $symbol->incUse(); /** @var ClassDefinition $calledObject */ $calledObject = $symbol->getValue(); if ($calledObject instanceof ClassDefinition) { $methodName = is_string($expr->name) ? $expr->name : false; if ($expr->name instanceof Variable) { /** * @todo implement fetch from symbol table */ //$methodName = $expr->name->name; } if ($methodName) { if (!$calledObject->hasMethod($methodName, true)) { $context->notice('undefined-mcall', sprintf('Method %s() does not exist in %s scope', $methodName, $expr->var->name), $expr); //it's needed to exit return new CompiledExpression(); } $method = $calledObject->getMethod($methodName); if (!$method) { $context->debug('getMethod is not working'); return new CompiledExpression(); } if ($method->isStatic()) { $context->notice('undefined-mcall', sprintf('Method %s() is a static function but called like class method in $%s variable', $methodName, $expr->var->name), $expr); } return new CompiledExpression(); } return new CompiledExpression(); } /** * It's a wrong type or value, maybe it's implemented and We need to fix it in another compilers */ $context->debug('Unknown $calledObject - is ' . gettype($calledObject)); return new CompiledExpression(); } $context->notice('variable-wrongtype.mcall', sprintf('Variable $%s is not object\\callable and cannot be called like this', $expr->var->name), $expr); return new CompiledExpression(); } else { $context->notice('undefined-variable.mcall', sprintf('Variable $%s is not defined in this scope', $expr->var->name), $expr); return new CompiledExpression(); } } $expression = new Expression($context); $expression->compile($expr->var); $context->debug('Unknown method call'); return new CompiledExpression(); }
public function __construct(Node\Stmt $stmt, Context $context) { $this->context = $context; switch (get_class($stmt)) { case 'PhpParser\\Node\\Stmt\\Return_': $this->passReturn($stmt); break; case 'PhpParser\\Node\\Stmt\\If_': $this->passIf($stmt); break; default: $this->context->debug('Unknown statement: ' . get_class($stmt)); break; } }
/** * {expr}::{expr}(); * * @param \PhpParser\Node\Expr\StaticCall $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { if ($expr->class instanceof \PhpParser\Node\Name) { $scope = $expr->class->parts[0]; $name = $expr->name; if ($scope == 'self') { if ($context->scope instanceof ClassDefinition) { $context->notice('scall-self-not-context', sprintf('No scope. You cannot call from %s out from class scope', $name, $scope), $expr); return new CompiledExpression(); } /** @var ClassDefinition $classDefinition */ $classDefinition = $context->scope; if (!$classDefinition->hasMethod($name, true)) { $context->notice('undefined-scall', sprintf('Static method %s() does not exist in %s scope', $name, $scope), $expr); return new CompiledExpression(); } $method = $classDefinition->getMethod($name); if (!$method->isStatic()) { $context->notice('undefined-scall', sprintf('Method %s() is not static but it was called as static way', $name), $expr); return new CompiledExpression(); } } return new CompiledExpression(); } $context->debug('Unknown static function call'); return new CompiledExpression(); }
/** * Compile Array_ expression to CompiledExpression * * @param Node\Expr\Array_ $expr * @return CompiledExpression */ protected function getArray(Node\Expr\Array_ $expr) { if ($expr->items === array()) { return new CompiledExpression(CompiledExpression::ARR, array()); } $resultArray = array(); foreach ($expr->items as $item) { $compiledValueResult = $this->compile($item->value); if ($item->key) { $compiledKeyResult = $this->compile($item->key); switch ($compiledKeyResult->getType()) { case CompiledExpression::INTEGER: case CompiledExpression::DOUBLE: case CompiledExpression::BOOLEAN: case CompiledExpression::NULL: case CompiledExpression::STRING: $resultArray[$compiledKeyResult->getValue()] = $compiledValueResult->getValue(); break; default: $this->context->debug("Type {$compiledKeyResult->getType()} is not supported for key value"); return new CompiledExpression(CompiledExpression::ARR); break; } } else { $resultArray[] = $compiledValueResult->getValue(); } } return new CompiledExpression(CompiledExpression::ARR, $resultArray); }
/** * @param Node\Stmt $stmt * @param Context $context */ public function __construct(Node\Stmt $stmt, Context $context) { try { $compiler = $this->factory($stmt); } catch (\Exception $e) { $context->debug('StatementCompiler is not implemented for ' . get_class($stmt)); return; } $compiler->pass($stmt, $context); }
/** * @param FuncCall $funcCall * @param Context $context * @return string|bool */ public function resolveFunctionName(FuncCall $funcCall, Context $context) { $funcNameCompiledExpression = $context->getExpressionCompiler()->compile($funcCall->name); if ($funcNameCompiledExpression->isString() && $funcNameCompiledExpression->isCorrectValue()) { return $funcNameCompiledExpression->getValue(); } else { if (!$funcNameCompiledExpression->isCallable()) { $context->debug('Unexpected function name type ' . $funcNameCompiledExpression->getTypeName(), $funcCall->name); } } return false; }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $output->writeln(''); if (extension_loaded('xdebug')) { /** * This will disable only showing stack traces on error conditions. */ if (function_exists('xdebug_disable')) { xdebug_disable(); } $output->writeln('<error>It is highly recommended to disable the XDebug extension before invoking this command.</error>'); } $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new \PhpParser\Lexer\Emulative(['usedAttributes' => ['comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos']])); /** @var Application $application */ $application = $this->getApplication(); $application->compiler = new Compiler(); $em = EventManager::getInstance(); $context = new Context($output, $application, $em); $fileParser = new FileParser($parser, $this->getCompiler()); $path = $input->getArgument('path'); if (is_dir($path)) { $directoryIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)); $output->writeln('Scanning directory <info>' . $path . '</info>'); $count = 0; /** @var SplFileInfo $file */ foreach ($directoryIterator as $file) { if ($file->getExtension() !== 'php') { continue; } $context->debug($file->getPathname()); $count++; } $output->writeln("Found <info>{$count} files</info>"); if ($count > 100) { $output->writeln('<comment>Caution: You are trying to scan a lot of files; this might be slow. For bigger libraries, consider setting up a dedicated platform or using ci.lowl.io.</comment>'); } $output->writeln(''); /** @var SplFileInfo $file */ foreach ($directoryIterator as $file) { if ($file->getExtension() !== 'php') { continue; } $fileParser->parserFile($file->getPathname(), $context); } } elseif (is_file($path)) { $fileParser->parserFile($path, $context); } /** * Step 2 Recursive check ... */ $application->compiler->compile($context); }
/** * @param Node\Stmt $stmt * @param Context $context */ public function __construct(Node\Stmt $stmt, Context $context) { try { $context->getEventManager()->fire(Event\StatementBeforeCompile::EVENT_NAME, new Event\StatementBeforeCompile($stmt, $context)); if ($stmt instanceof Stmt\Goto_ || $stmt instanceof Stmt\Label || $stmt instanceof Stmt\InlineHTML || $stmt instanceof Stmt\Nop) { return; } $compiler = $this->factory($stmt); } catch (\Exception $e) { $context->debug('StatementCompiler is not implemented for ' . get_class($stmt)); return; } $compiler->pass($stmt, $context); }
/** * @param \PhpParser\Node\Expr\New_ $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { if ($expr->class instanceof Node\Name) { $name = $expr->class->parts[0]; if (count($expr->args) > 0) { return new CompiledExpression(CompiledExpression::OBJECT); } if (class_exists($name, true)) { return new CompiledExpression(CompiledExpression::OBJECT, new $name()); } return new CompiledExpression(CompiledExpression::OBJECT); } $context->debug('Unknown how to pass new'); return new CompiledExpression(); }
/** * @param string $filepath * @param Context $context * @throws RuntimeException when filepath is not readable */ public function parserFile($filepath, Context $context) { $context->setFilepath($filepath); try { if (!is_readable($filepath)) { throw new RuntimeException('File ' . $filepath . ' is not readable'); } $context->debug('<comment>Precompile: ' . $filepath . '.</comment>'); $code = file_get_contents($filepath); $astTree = $this->parser->parse($code); $this->nodeTraverser->traverse($astTree); $context->aliasManager = new AliasManager(); $namespace = null; /** * Step 1 Precompile */ foreach ($astTree as $topStatement) { if ($topStatement instanceof Node\Stmt\Namespace_) { /** * Namespace block can be created without NS name */ if ($topStatement->name) { $namespace = $topStatement->name->toString(); $context->aliasManager->setNamespace($namespace); } if ($topStatement->stmts) { $this->parseTopDefinitions($topStatement->stmts, $context->aliasManager, $filepath); } } else { if (is_array($topStatement)) { $this->parseTopDefinitions($topStatement, $context->aliasManager, $filepath); } else { $this->parseTopDefinitions($astTree, $context->aliasManager, $filepath); } } } $context->clear(); } catch (\PhpParser\Error $e) { $context->syntaxError($e, $filepath); } catch (Exception $e) { $context->output->writeln("<error>{$e->getMessage()}</error>"); } }
protected function compileVariableDeclaration(CompiledExpression $variableName, CompiledExpression $value, Context $context) { switch ($variableName->getType()) { case CompiledExpression::STRING: break; default: $context->debug('Unexpected type of Variable name after compile'); return new CompiledExpression(); } $symbol = $context->getSymbol($variableName->getValue()); if ($symbol) { $symbol->modify($value->getType(), $value->getValue()); $context->modifyReferencedVariables($symbol, $value->getType(), $value->getValue()); } else { $symbol = new \PHPSA\Variable($variableName->getValue(), $value->getValue(), $value->getType(), $context->getCurrentBranch()); $context->addVariable($symbol); } $symbol->incSets(); }
/** * @param \PhpParser\Node\Expr\New_ $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { if ($expr->class instanceof Node\Name) { $name = $expr->class->parts[0]; $arguments = []; if (count($expr->args) > 0) { foreach ($expr->args as $argument) { $arguments[] = $context->getExpressionCompiler()->compile($argument->value); } } else { if (class_exists($name, true)) { return new CompiledExpression(CompiledExpression::OBJECT, new $name()); } } return new CompiledExpression(CompiledExpression::OBJECT); } $context->debug('Unknown how to pass new', $expr); return new CompiledExpression(); }
/** * {expr}::{expr}(); * * @param \PhpParser\Node\Expr\StaticCall $expr * @param Context $context * @return CompiledExpression */ public function compile($expr, Context $context) { if ($expr->class instanceof \PhpParser\Node\Name) { $scope = $expr->class->parts[0]; $name = $expr->name; if ($scope == 'self') { if (!$context->scope->hasMethod($name)) { $context->notice('undefined-scall', sprintf('Static method %s() does not exist in %s scope', $name, $scope), $expr); } else { $method = $context->scope->getMethod($name); if (!$method->isStatic()) { $context->notice('undefined-scall', sprintf('Method %s() is not static but it was called as static way', $name), $expr); } } } return new CompiledExpression(); } $context->debug('Unknown static function call'); return new CompiledExpression(); }
/** * {expr}::${expr}; * * @param \PhpParser\Node\Expr\StaticPropertyFetch $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { $expressionCompiler = $context->getExpressionCompiler(); $leftCE = $expressionCompiler->compile($expr->class); if ($leftCE->isObject()) { $name = $expr->name; /** @var ClassDefinition $classDefinition */ $classDefinition = $context->scope; if (!$classDefinition->hasProperty($name, true)) { $context->notice('language_error', sprintf('Static property $%s does not exist in %s scope', $name, $expr->class), $expr); return new CompiledExpression(); } $property = $classDefinition->getPropertyStatement($name, true); if (!$property->isStatic()) { $context->notice('language_error', sprintf('Property $%s is not static but was called in a static way', $name), $expr); } return $expressionCompiler->compile($property); } $context->debug('Unknown static property fetch', $expr); return new CompiledExpression(); }
/** * classname::class, classname::CONSTANTNAME, ... * * @param \PhpParser\Node\Expr\ClassConstFetch $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { $compiler = $context->getExpressionCompiler(); if ($expr->name == "class") { // @todo return fully qualified classname return new CompiledExpression(); } $leftCE = $compiler->compile($expr->class); if ($leftCE->isObject()) { $leftCEValue = $leftCE->getValue(); if ($leftCEValue instanceof ClassDefinition) { if (!$leftCEValue->hasConst($expr->name, true)) { $context->notice('language_error', sprintf('Constant %s does not exist in %s scope', $expr->name, $expr->class), $expr); return new CompiledExpression(CompiledExpression::UNKNOWN); } return new CompiledExpression(); } } $context->debug('Unknown const fetch', $expr); return new CompiledExpression(); }
/** * {expr}::{expr}(); * * @param \PhpParser\Node\Expr\StaticCall $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { $expressionCompiler = $context->getExpressionCompiler(); $leftCE = $expressionCompiler->compile($expr->class); if ($leftCE->isObject()) { $name = $expr->name; /** @var ClassDefinition $classDefinition */ $classDefinition = $context->scope; if (!$classDefinition->hasMethod($name, true)) { $context->notice('language_error', sprintf('Static method %s() does not exist in %s scope', $name, $expr->class), $expr); return new CompiledExpression(); } $method = $classDefinition->getMethod($name, true); if ($expr->class->parts[0] !== 'parent' && !$method->isStatic()) { $context->notice('language_error', sprintf('Method %s() is not static but was called in a static way', $name), $expr); } return new CompiledExpression(); } $context->debug('Unknown static function call', $expr); return new CompiledExpression(); }
/** * @param Node\Expr\Assign $expr * @return CompiledExpression|Expression */ protected function passSymbol(Node\Expr\Assign $expr) { if ($expr->var instanceof \PhpParser\Node\Expr\List_) { return new CompiledExpression(); } if ($expr->var instanceof Node\Expr\Variable) { $name = $expr->var->name; $compiledExpression = new Expression($this->context); $result = $compiledExpression->compile($expr->expr); $symbol = $this->context->getSymbol($name); if ($symbol) { $symbol->modify($result->getType(), $result->getValue()); } else { $symbol = new Variable($name, $result->getValue(), $result->getType()); $this->context->addVariable($symbol); } $symbol->incSets(); return $compiledExpression; } $this->context->debug('Unknown how to pass symbol'); return new CompiledExpression(); }
/** * @param string $filepath * @param Parser $parser * @param Context $context */ protected function parserFile($filepath, Parser $parser, Context $context) { $context->setFilepath($filepath); $astTraverser = new \PhpParser\NodeTraverser(); $astTraverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); try { if (!is_readable($filepath)) { throw new RuntimeException('File ' . $filepath . ' is not readable'); } $context->debug('<comment>Precompile: ' . $filepath . '.</comment>'); $code = file_get_contents($filepath); $astTree = $parser->parse($code); $astTraverser->traverse($astTree); $aliasManager = new AliasManager(); $namespace = null; /** * Step 1 Precompile */ foreach ($astTree as $topStatement) { if ($topStatement instanceof Node\Stmt\Namespace_) { /** * Namespace block can be created without NS name */ if ($topStatement->name) { $namespace = $topStatement->name->toString(); $aliasManager->setNamespace($namespace); } if ($topStatement->stmts) { $this->parseTopDefinitions($topStatement->stmts, $aliasManager, $filepath); } } else { $this->parseTopDefinitions($topStatement, $aliasManager, $filepath); } } /** * Another Traverser to handler Analyzer Passe(s) */ $analyzeTraverser = new AstTraverser([new \PHPSA\Node\Visitor\FunctionCall()], $context); $analyzeTraverser->traverse($astTree); $context->clear(); } catch (\PhpParser\Error $e) { $context->sytaxError($e, $filepath); } catch (Exception $e) { $context->output->writeln("<error>{$e->getMessage()}</error>"); } }
protected function execute(InputInterface $input, OutputInterface $output) { $output->writeln(''); $lexer = new \PhpParser\Lexer(array('usedAttributes' => array('comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos'))); if (extension_loaded('xdebug')) { $output->writeln('<error>It is highly recommended to disable the XDebug extension before invoking this command.</error>'); } $parser = new Parser(new \PhpParser\Lexer\Emulative()); /** @var Application $application */ $application = $this->getApplication(); $context = new Context($output, $application); /** * Store option's in application's configuration */ $application->getConfiguration()->setValue('blame', $input->getOption('blame')); $path = $input->getArgument('path'); if (is_dir($path)) { $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)); /** * @todo Uncomment after PHP >=5.4 */ // $it = new CallbackFilterIterator($it, function (SplFileInfo $file) { // return $file->getExtension() == 'php'; // }); $output->writeln('Scanning directory <info>' . $path . '</info>'); $count = 0; /** @var SplFileInfo $file */ foreach ($it as $file) { if ($file->getExtension() != 'php') { continue; } $context->debug($file->getPathname()); $count++; } $output->writeln(sprintf('found <info>%d files</info>', $count)); if ($count > 100) { $output->writeln('<comment>Caution: You are trying to scan a lot of files; this might be slow. For bigger libraries, consider setting up a dedicated platform or using owl-ci.dmtry.me.</comment>'); } $output->writeln(''); /** @var SplFileInfo $file */ foreach ($it as $file) { if ($file->getExtension() != 'php') { continue; } $this->parserFile($file->getPathname(), $parser, $context); } } elseif (is_file($path)) { $this->parserFile($path, $parser, $context); } /** * Step 2 Recursive check ... */ /** * @var $class ClassDefinition */ foreach ($this->classes as $class) { $class->compile($context); } $output->writeln(''); $output->writeln('Memory usage: ' . $this->getMemoryUsage(false) . ' (peak: ' . $this->getMemoryUsage(true) . ') MB'); }
/** * @param Node\Name\FullyQualified $expr * @return CompiledExpression */ public function getFullyQualifiedNodeName(Node\Name\FullyQualified $expr) { $this->context->debug('Unimplemented FullyQualified', $expr); return new CompiledExpression(); }