public function test() { eval('function hook_tokens($type, $tokens, array $data = array(), array $options = array()) {}'); $config = ['hook' => 'tokens', 'module' => 'system']; $module_handler = $this->getMock('\\Drupal\\Core\\Extension\\ModuleHandlerInterface'); $plugin = new ImplementHook($config, uniqID(), [], $module_handler); $plugin->setTarget($this->target); $plugin->execute(); $module = $this->target->getPath('.module'); $function = $this->target->open($module)->children(Filter::isFunction('foo_tokens'))->get(0); $this->assertInstanceOf('\\Pharborist\\Functions\\FunctionDeclarationNode', $function); $this->assertEquals('foo_tokens', $function->getName()->getText()); $parameters = $function->getParameters(); $this->assertCount(4, $parameters); $this->assertNull($parameters[0]->getTypeHint()); $this->assertEquals('type', $parameters[0]->getName()); $this->assertNull($parameters[0]->getValue()); $this->assertNull($parameters[1]->getTypeHint()); $this->assertEquals('tokens', $parameters[1]->getName()); $this->assertNull($parameters[1]->getValue()); $this->assertInstanceOf('\\Pharborist\\TokenNode', $parameters[2]->getTypeHint()); $this->assertSame(T_ARRAY, $parameters[2]->getTypeHint()->getType()); $this->assertEquals('data', $parameters[2]->getName()); $this->assertInstanceOf('\\Pharborist\\Types\\ArrayNode', $parameters[2]->getValue()); $this->assertInstanceOf('\\Pharborist\\TokenNode', $parameters[3]->getTypeHint()); $this->assertSame(T_ARRAY, $parameters[3]->getTypeHint()->getType()); $this->assertEquals('options', $parameters[3]->getName()); $this->assertInstanceOf('\\Pharborist\\Types\\ArrayNode', $parameters[3]->getValue()); }
/** * {@inheritdoc} */ public function analyze(TargetInterface $target) { $violations = []; $indexer = $target->getIndexer('function'); if ($indexer->has('hook_form_alter')) { $violations[] = $indexer->get('hook_form_alter'); } $id = $target->id() . '_form_%_alter'; // Until kernel tests are run in PHPUnit, we need to check for // the existence of db_like(). if (function_exists('db_like')) { $id = db_like($id); } $alter_hooks = $target->getIndexer('function')->getQuery()->condition('id', $id, 'LIKE')->execute(); foreach ($alter_hooks as $alter_hook) { $violations[] = $target->open($alter_hook->file)->find(Filter::isFunction($alter_hook->id)); } $issues = []; if ($violations) { $issue = $this->buildIssue($target); array_walk($violations, function (FunctionDeclarationNode $function) use($issue) { $issue->addViolation($function, $this); }); $issues[] = $issue; } return $issues; }
public function testUseDeclarations() { $snippet = <<<'EOF' namespace Test { use const Other\MY_CONST; use function Other\my_func; use Other\MyClass; use Other\OtherClass as Bind; class TestClass {} } EOF; /** @var NamespaceNode $namespace */ $namespace = Parser::parseSnippet($snippet); $declarations = $namespace->getUseDeclarations(); $this->assertCount(4, $declarations); $aliases = $namespace->getClassAliases(); $this->assertCount(2, $aliases); $this->assertArrayHasKey('MyClass', $aliases); $this->assertEquals('\\Other\\MyClass', $aliases['MyClass']); $this->assertArrayHasKey('Bind', $aliases); $this->assertEquals('\\Other\\OtherClass', $aliases['Bind']); $class_node = $namespace->find(Filter::isClass('TestClass'))[0]; $this->assertTrue($namespace->owns($class_node)); $class_node = ClassNode::create('Dummy'); $this->assertFalse($namespace->owns($class_node)); }
public function testViolationsAndDetectors() { $analyzer = $this->getMockBuilder('\\Drupal\\drupalmoduleupgrader\\AnalyzerBase')->disableOriginalConstructor()->getMock(); $analyzer->method('getPluginId')->willReturn('blarg'); $this->issue->addAffectedFile($this->dir->getChild('foo.info')->url(), $analyzer); $code = <<<'END' <?php /** * Implements hook_permission(). */ function foo_permission() { return array(); } END; $this->dir->getChild('foo.module')->setContent($code); $node = $this->target->open($this->dir->getChild('foo.module')->url())->children(Filter::isFunction('foo_permission'))->get(0); $this->issue->addViolation($node, $analyzer); $violations = $this->issue->getViolations(); $this->assertInternalType('array', $violations); $this->assertCount(2, $violations); $this->assertArrayHasKey('file', $violations[0]); $this->assertArrayNotHasKey('line_number', $violations[0]); $this->assertEquals($this->dir->getChild('foo.info')->url(), $violations[0]['file']); $this->assertArrayHasKey('file', $violations[1]); $this->assertArrayHasKey('line_number', $violations[1]); $this->assertEquals($this->dir->getChild('foo.module')->url(), $violations[1]['file']); $detectors = $this->issue->getDetectors(); $this->assertInternalType('array', $detectors); $this->assertCount(1, $detectors); $this->assertEquals($analyzer->getPluginId(), $detectors[0]); }
/** * {@inheritdoc} */ public function getUsages($identifier) { $files = $this->getQuery(['file'])->distinct()->condition('id', $identifier)->execute()->fetchCol(); $usages = new NodeCollection(); foreach ($files as $file) { $this->target->open($file)->find(Filter::isInstanceOf('\\Pharborist\\Constants\\ConstantNode'))->filter(function (ConstantNode $node) use($identifier) { return $node->getConstantName() == $identifier; })->addTo($usages); } return $usages; }
/** * Get the use declarations of this statement block. * * @return NodeCollection|UseDeclarationNode[] * Use declarations. */ public function getUseDeclarations() { $declarations = []; /** @var \Pharborist\Namespaces\UseDeclarationBlockNode[] $use_blocks */ $use_blocks = $this->children(Filter::isInstanceOf('\\Pharborist\\Namespaces\\UseDeclarationBlockNode')); foreach ($use_blocks as $use_block) { foreach ($use_block->getDeclarationStatements() as $use_statement) { $declarations = array_merge($declarations, $use_statement->getDeclarations()->toArray()); } } return new NodeCollection($declarations, FALSE); }
/** * Tests if a function contains logic: any branching operator, function * call, or object instantiation. * * @param \Pharborist\ParentNode $node * The node to test. * * @return boolean */ public function __invoke(ParentNode $node) { $function_calls = $node->find(Filter::isInstanceOf('\\Pharborist\\Functions\\FunctionCallNode'))->not(function (FunctionCallNode $call) { return in_array($call->getName()->getText(), $this->whitelist); }); if ($function_calls->isEmpty()) { $filter = call_user_func_array('\\Pharborist\\Filter::isInstanceOf', static::$logic); return (bool) $node->find($filter)->count(); } else { return TRUE; } }
public function testRewriteWithCacheReset() { $original = <<<'END' user_load(30, TRUE); END; $expected = <<<'END' // @FIXME // To reset the user cache, use EntityStorageInterface::resetCache(). \Drupal::entityManager()->getStorage('user')->load(30); END; $snippet = Parser::parseSnippet($original); $function_call = $snippet->children(Filter::isFunctionCall('user_load'))->get(0); $rewritten = $this->plugin->rewrite($function_call, $this->target); $function_call->replaceWith($rewritten); $this->assertEquals($expected, $snippet->getText()); }
/** * {@inheritdoc} */ public function analyze(TargetInterface $target) { $indexer = $target->getIndexer('function'); $issues = []; if ($indexer->has('hook_uninstall')) { /** @var \Pharborist\NodeCollection $variable_del */ $variable_del = $indexer->get('hook_uninstall')->find(Filter::isFunctionCall('variable_del')); if (sizeof($variable_del) > 0) { $issue = $this->buildIssue($target); $variable_del->each(function (FunctionCallNode $function_call) use($issue) { $issue->addViolation($function_call, $this); }); $issues[] = $issue; } } return $issues; }
/** * @requires PHP 5.6 */ public function testIsVariadic() { $doc = <<<'END' <?php function foo($a, $b, ...$c) { } END; /** @var \Pharborist\Functions\FunctionDeclarationNode $func */ $func = Parser::parseSource($doc)->children(Filter::isFunction('foo'))->get(0); $this->assertTrue($func->isVariadic()); $doc = <<<'END' <?php function baz($a, $b, $c) { } END; $func = Parser::parseSource($doc)->children(Filter::isFunction('baz'))->get(0); $this->assertFalse($func->isVariadic()); }
public function testForeignStringKey() { $original = <<<'END' <?php variable_set('bar_wambooli', TRUE); END; $expected = <<<'END' <?php // @FIXME // This looks like another module's variable. You'll need to rewrite this call // to ensure that it uses the correct configuration object. variable_set('bar_wambooli', TRUE); END; $snippet = Parser::parseSource($original); $function_call = $snippet->find(Filter::isFunctionCall('variable_set'))->get(0); $rewritten = $this->plugin->rewrite($function_call, $this->target); $this->assertNull($rewritten); $this->assertSame($expected, $snippet->getText()); }
public function testStatic() { /** @var ClassNode $class_node */ $class_node = Parser::parseSnippet('class Foo { public $bar; }'); /** @var ClassMemberNode $a */ $a = $class_node->getProperties()[0]; $this->assertFalse($a->isStatic()); $this->assertNull($a->getStatic()); $a->setStatic(TRUE); $this->assertTrue($a->isStatic()); $this->assertSame('public static $bar;', $a->closest(Filter::isInstanceOf('\\Pharborist\\Objects\\ClassMemberListNode'))->getText()); $class_node = Parser::parseSnippet('class Bar { protected static $baz; }'); /** @var ClassMemberNode $b */ $b = $class_node->getProperties()[0]; $this->assertTrue($b->isStatic()); $this->assertInstanceOf('\\Pharborist\\TokenNode', $b->getStatic()); $this->assertSame(T_STATIC, $b->getStatic()->getType()); $b->setStatic(FALSE); $this->assertFalse($b->isStatic()); $this->assertSame('protected $baz;', $b->closest(Filter::isInstanceOf('\\Pharborist\\Objects\\ClassMemberListNode'))->getText()); }
public function testStringKeyAndUnextractableDefaultValue() { $original = <<<'END' <?php variable_get('foo_wambooli', array()); END; $expected = <<<'END' <?php // @FIXME // Could not extract the default value because it is either indeterminate, or // not scalar. You'll need to provide a default value in // config/install/@module.settings.yml and config/schema/@module.schema.yml. variable_get('foo_wambooli', array()); END; $snippet = Parser::parseSource($original); $function_call = $snippet->find(Filter::isFunctionCall('variable_get'))->get(0); $rewritten = $this->plugin->rewrite($function_call, $this->target); $this->assertInstanceOf('\\Pharborist\\Objects\\ObjectMethodCallNode', $rewritten); $this->assertEquals('\\Drupal::config(\'foo.settings\')->get(\'foo_wambooli\')', $rewritten->getText()); $this->assertSame($expected, $snippet->getText()); }
/** * This test is failing at the moment because for whatever reason, * $snippet->children() is only fetching the first call to node_load(). */ public function _testRewriteWithCacheReset() { $original = <<<'END' node_load(30); node_load(30, TRUE); node_load(30, 32); node_load(30, 32, TRUE); END; $expected = <<<'END' \Drupal::entityManager()->getStorage('user')->load(30); // FIXME: To reset the node cache, use EntityStorageInterface::resetCache(). \Drupal::entityManager()->getStorage('user')->load(30); \Drupal::entityManager()->getStorage('user')->loadRevision(32); // FIXME: To reset the node cache, use EntityStorageInterface::resetCache(). \Drupal::entityManager()->getStorage('user')->loadRevision(32); END; $snippet = Parser::parseSnippet($original); $function_calls = $snippet->children(Filter::isFunctionCall('node_load')); foreach ($function_calls as $function_call) { $rewritten = $this->plugin->rewrite($function_call, $this->target); $function_call->replaceWith($rewritten); } $this->assertEquals($expected, $snippet->getText()); }
/** * Test block comment. */ public function testBlockComment() { $source = <<<'EOF' <?php /** ignore */ // Line one // Line two // Line three // Line four EOF; $tree = $this->parseSource($source); $comments = $tree->children(Filter::isComment(FALSE)); /** @var LineCommentBlockNode $comment_block */ $comment_block = $comments[0]; $this->assertInstanceOf('\\Pharborist\\LineCommentBlockNode', $comment_block); $this->assertEquals("Line one\nLine two\nLine three", $comment_block->getCommentText()); /** @var CommentNode $comment */ $comment = $comments[1]; $this->assertInstanceOf('\\Pharborist\\CommentNode', $comment); $this->assertEquals("Line four", $comment->getCommentText()); }
/** * @requires PHP 5.6 */ public function testVariadic() { $source = <<<'EOF' <?php function foo($args) { } EOF; $tree = Parser::parseSource($source); /** @var \Pharborist\Functions\FunctionDeclarationNode $function */ $function = $tree->children(Filter::isInstanceOf('\\Pharborist\\Functions\\FunctionDeclarationNode'))[0]; $parameter = $function->getParameter(0); $this->assertFalse($parameter->isVariadic()); $parameter->setVariadic(TRUE); $this->assertTrue($parameter->isVariadic()); $this->assertEquals('...$args', $parameter->getText()); }
/** * Add property to class. * * @param string|ClassMemberListNode $property * @return $this */ public function appendProperty($property) { if (is_string($property)) { $property = ClassMemberListNode::create($property); } $properties = $this->statements->children(Filter::isInstanceOf('\\Pharborist\\ClassMemberListNode')); if ($properties->count() === 0) { $this->statements->firstChild()->after($property); } else { $properties->last()->after($property); } FormatterFactory::format($this); return $this; }
/** * {@inheritdoc} */ public function rewrite(ParameterNode $parameter) { parent::rewrite($parameter); $function = $parameter->getFunction(); $form_state = Token::variable('$' . $parameter->getName()); $set_errors = $function->find(Filter::isFunctionCall('form_set_error', 'form_error')); /** @var \Pharborist\Functions\FunctionCallNode $set_error */ foreach ($set_errors as $set_error) { $arguments = $set_error->getArguments(); $method = $set_error->getName()->getText() == 'form_set_error' ? 'setErrorByName' : 'setError'; $rewrite = ObjectMethodCallNode::create(clone $form_state, $method)->appendArgument(clone $arguments[0])->appendArgument(clone $arguments[1]); $set_error->replaceWith($rewrite); } // form_clear_error() --> $form_state->clearErrors(). $clear_errors = $function->find(Filter::isFunctionCall('form_clear_error')); foreach ($clear_errors as $clear_error) { $clear_error->replaceWith(ObjectMethodCallNode::create(clone $form_state, 'clearErrors')); } // form_get_errors() --> $form_state->getErrors() $get_errors = $function->find(Filter::isFunctionCall('form_get_errors')); foreach ($get_errors as $get_error) { $get_error->replaceWith(ObjectMethodCallNode::create(clone $form_state, 'getErrors')); } // form_get_error() --> $form_state->getError() $get_errors = $function->find(Filter::isFunctionCall('form_get_error')); /** @var \Pharborist\Functions\FunctionCallNode $get_error */ foreach ($get_errors as $get_error) { $rewrite = ObjectMethodCallNode::create(clone $form_state, 'getError')->appendArgument($get_error->getArguments()->get(0)); $get_error->replaceWith($rewrite); } }
/** * @param ExpressionNode|NULL $node * @return $this */ public function setValue($node) { if ($node === NULL) { if (isset($this->value)) { $this->value->previousUntil(Filter::isInstanceOf('\\Pharborist\\Variables\\VariableNode'))->remove(); $this->value->remove(); } } else { if (isset($this->value)) { /** @var Node $node */ $this->value->replaceWith($node); } else { $this->append([Token::space(), Token::assign(), Token::space(), $node]); } } return $this; }
/** * Gets the fully qualified name of the method, e.g. \My\Namespaced\Class::foo. * * @return string */ public function getFullyQualifiedName() { /** @var ClassNode $class_node */ $class_node = $this->closest(Filter::isInstanceOf('\\Pharborist\\Objects\\ClassNode')); return $class_node->getName()->getAbsolutePath() . '::' . $this->getName()->getText(); }
/** * Returns if the parameter is fully reassigned anywhere in the function. * * @param \Pharborist\Functions\ParameterNode $parameter * The parameter to check. * * @return boolean */ protected function isReassigned(ParameterNode $parameter) { return (bool) $parameter->getFunction()->find(Filter::isInstanceOf('\\Pharborist\\Variables\\VariableNode'))->filter(function (VariableNode $variable) use($parameter) { return $variable->getName() == $parameter->getName(); })->filter($this->isAssigned)->count(); }
/** * Resolve an unqualified name to fully qualified name. * * @param string $name * The unqualified name to resolve. * * @return string * Fully qualified name. */ protected function resolveUnqualified($name) { if ($this->parent instanceof NamespaceNode) { return '\\' . $name; } if ($this->parent instanceof UseDeclarationNode) { return '\\' . $name; } $namespace = $this->getNamespace(); $use_declarations = array(); if ($namespace) { $use_declarations = $namespace->getBody()->getUseDeclarations(); } else { /** @var \Pharborist\RootNode $root_node */ $root_node = $this->closest(Filter::isInstanceOf('\\Pharborist\\RootNode')); if ($root_node) { $use_declarations = $root_node->getUseDeclarations(); } } if ($this->parent instanceof FunctionCallNode) { /** @var UseDeclarationNode $use_declaration */ foreach ($use_declarations as $use_declaration) { if ($use_declaration->isFunction() && $use_declaration->getBoundedName() === $name) { return '\\' . $use_declaration->getName()->getPath(); } } return $this->getParentPath() . $name; } elseif ($this->parent instanceof ConstantNode) { /** @var UseDeclarationNode $use_declaration */ foreach ($use_declarations as $use_declaration) { if ($use_declaration->isConst() && $use_declaration->getBoundedName() === $name) { return '\\' . $use_declaration->getName()->getPath(); } } return $this->getParentPath() . $name; } else { // Name is a class reference. /** @var UseDeclarationNode $use_declaration */ foreach ($use_declarations as $use_declaration) { if ($use_declaration->isClass() && $use_declaration->getBoundedName() === $name) { return '\\' . $use_declaration->getName()->getPath(); } } // No use declaration so class name refers to class in current namespace. return $this->getParentPath() . $name; } }
public function testGetTypes() { $source = <<<'EOF' <?php use MyNamespace\MyClass; use MyNamespace\SomeClass as TestClass; class Test { /** * A property using class. * * @var MyClass */ private $a; /** * Property using class alias. * * @var TestClass */ private $b; /** * An array property. * * @var array */ private $data; /** * A callable property. * * @var callable */ private $callback; /** * An integer property. * * @var int */ private $num; /** * A generic property. */ private $generic; } EOF; $tree = Parser::parseSource($source); /** @var ClassNode $class */ $class = $tree->children(Filter::isInstanceOf('\\Pharborist\\Objects\\ClassNode'))[0]; $properties = $class->getProperties(); $this->assertEquals(['\\MyNamespace\\MyClass'], $properties[0]->getTypes()); $this->assertEquals(['\\MyNamespace\\SomeClass'], $properties[1]->getTypes()); $this->assertEquals(['array'], $properties[2]->getTypes()); $this->assertEquals(['callable'], $properties[3]->getTypes()); $this->assertEquals(['int'], $properties[4]->getTypes()); $this->assertEquals(['mixed'], $properties[5]->getTypes()); }
public function testIndex() { $first = Token::identifier('hello'); $second = Token::identifier('world'); $not_found = Token::identifier('notfound'); $collection = new NodeCollection([$first, $second], FALSE); $this->assertEquals(0, $collection->indexOf(Filter::is($first))); $this->assertEquals(1, $collection->indexOf(Filter::is($second))); $this->assertEquals(-1, $collection->indexOf(Filter::is($not_found))); }
/** * Returns every namespace in this document. * * @return \Pharborist\NodeCollection */ public function getNamespaces() { return $this->children(Filter::isInstanceOf('\\Pharborist\\Namespaces\\NamespaceNode')); }
/** * Tests if the given node is on the left side of an assignment. * * @param \Pharborist\Node $node * The node to test. * * @return boolean */ public function __invoke(Node $node) { /** @var \Pharborist\Operators\AssignNode $assignment */ $assignment = $node->closest(Filter::isInstanceOf('\\Pharborist\\Operators\\AssignNode')); return $assignment ? $assignment->getLeftOperand() === $node : FALSE; }
/** * Extracts every module required by a web test by scanning its calls * to parent::setUp(). * * @param \Pharborist\Objects\ClassNode $test * * @return string[] * Array of modules set up by this module. */ private function extractModules(ClassNode $test) { $modules = []; $test->find(Filter::isClassMethodCall('parent', 'setUp'))->filter(function (ClassMethodCallNode $call) { return sizeof($call->getArguments()) > 0; })->each(function (ClassMethodCallNode $call) use(&$modules) { foreach ($call->getArguments() as $argument) { if ($argument instanceof StringNode) { $modules[] = $argument->toValue(); } } $call->clearArguments(); }); return array_unique($modules); }
public function visitNamespaceNode(NamespaceNode $node) { $first = $node->getBody()->firstToken(); $has_braces = $first->getType() === '{'; $this->indentLevel = $has_braces ? 1 : 0; if (!$has_braces) { foreach ($node->children(Filter::isTokenType(';')) as $semicolon) { $next = $semicolon->next(); $newlines = str_repeat($this->config['nl'], 2); if ($next instanceof WhitespaceNode) { $next->setText($newlines); } else { $semicolon->after(Token::whitespace($newlines)); } } } }
/** * Returns if the array has a specific key. * * @param mixed $key * Either a scalar value ('foo') or an ExpressionNode representing the key. * If $key is an ExpressionNode, the key's string representation is compared * with the string representations of the array keys. Otherwise, the actual * value is compared. * @param boolean $recursive * Whether or not to check every level of the array. * * @return boolean * * @throws \InvalidArgumentException */ public function hasKey($key, $recursive = TRUE) { if (!$key instanceof ExpressionNode && !is_scalar($key)) { throw new \InvalidArgumentException(); } $keys = $this->getKeys($recursive); if (is_scalar($key)) { return $keys->filter(Filter::isInstanceOf('\\Pharborist\\Types\\ScalarNode'))->is(function (ScalarNode $node) use($key) { return $node->toValue() === $key; }); } else { return $keys->is(function (ExpressionNode $expr) use($key) { return $expr->getText() === $key->getText(); }); } }
public function clearLexicalVariables() { if ($this->hasLexicalVariables()) { $this->lexicalUse->nextUntil(Filter::is($this->body))->remove(); $this->lexicalUse->remove(); } }