From 674e24d2b9e90b2f629dab7f30d5070e4d49d815 Mon Sep 17 00:00:00 2001 From: Jip Date: Mon, 8 Jan 2018 10:16:44 +0100 Subject: [PATCH] Add missing namespace (#138) When prefixing classes from the global namespace, the namespace statement may be missing. Closes #135. --- specs/namespace/creation-for-whitelist.php | 300 +++++++++++++++++++++ src/NodeVisitor/NamespaceStmtPrefixer.php | 90 ++++++- src/Scoper/TraverserFactory.php | 2 +- 3 files changed, 385 insertions(+), 7 deletions(-) create mode 100644 specs/namespace/creation-for-whitelist.php diff --git a/specs/namespace/creation-for-whitelist.php b/specs/namespace/creation-for-whitelist.php new file mode 100644 index 00000000..5a0c216c --- /dev/null +++ b/specs/namespace/creation-for-whitelist.php @@ -0,0 +1,300 @@ +, + * Pádraic Brady + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'meta' => [ + 'title' => 'Namespace declaration creation for whitelisted classes which belong to the global namespace.', + // Default values. If not specified will be the one used + 'prefix' => 'Humbug', + 'whitelist' => [], + ], + + 'Single class should receive namespace' => <<<'PHP' + <<<'PHP' + <<<'PHP' + <<<'PHP' + <<<'PHP' + <<<'PHP' + <<<'PHP' + <<<'PHP' + <<<'PHP' + <<<'PHP' + <<<'PHP' +prefix = $prefix; + $this->namespaceStatements = $namespaceStatements; + $this->globalWhitelister = $globalWhitelister; + } /** - * @param string $prefix - * @param NamespaceStmtCollection $namespaceStatements + * @inheritdoc */ - public function __construct(string $prefix, NamespaceStmtCollection $namespaceStatements) + public function beforeTraverse(array $nodes) { - $this->prefix = $prefix; - $this->namespaceStatements = $namespaceStatements; + $this->hasWhitelistedNode = $this->hasWhitelistedNode($nodes); } /** @@ -59,6 +71,18 @@ public function enterNode(Node $node): Node : $node; } + /** + * @inheritdoc + */ + public function leaveNode(Node $node) + { + return ( + !$this->hasWhitelistedNode + || $node instanceof Namespace_ + || AppendParentNode::hasParent($node) + ) ? $node : $this->wrapNamespace($node); + } + private function prefixNamespaceStmt(Namespace_ $namespace): Node { $originalNamespace = $namespace; @@ -74,8 +98,62 @@ private function prefixNamespaceStmt(Namespace_ $namespace): Node return $namespace; } + private function wrapNamespace(Node $node): Node + { + if ($this->isWhitelistedNode($node)) { + return new Namespace_(new Node\Name($this->prefix), [$node]); + } + + // Anything else needs to be wrapped with global namespace. + return new Namespace_(null, [$node]); + } + + /** + * @param Node[] $nodes + * + * @return bool + */ + private function hasWhitelistedNode(array $nodes): bool + { + foreach ($nodes as $node) { + if ($this->isWhitelistedNode($node)) { + return true; + } + } + + return false; + } + + private function isWhitelistedNode(Node $node) + { + if (($node instanceof Class_ || $node instanceof Interface_) + && ($this->globalWhitelister)($node->name) + ) { + return true; + } + + // Check nodes in the global namespaces. + if ($node instanceof Namespace_ && null === $node->name) { + foreach ($node->stmts as $statement) { + if ($this->isWhitelistedNode($statement)) { + return true; + } + } + } + + return false; + } + private function shouldPrefixStmt(Namespace_ $namespace): bool { - return null !== $namespace->name && $this->prefix !== $namespace->name->getFirst(); + if (null !== $namespace->name && $this->prefix !== $namespace->name->getFirst()) { + return true; + } + + if (null === $namespace->name && $this->hasWhitelistedNode([$namespace])) { + return true; + } + + return false; } } diff --git a/src/Scoper/TraverserFactory.php b/src/Scoper/TraverserFactory.php index 23e7f3d9..233cfe31 100644 --- a/src/Scoper/TraverserFactory.php +++ b/src/Scoper/TraverserFactory.php @@ -56,7 +56,7 @@ public function create(string $prefix, array $whitelist, callable $globalWhiteli $traverser->addVisitor(new NodeVisitor\AppendParentNode()); - $traverser->addVisitor(new NodeVisitor\NamespaceStmtPrefixer($prefix, $namespaceStatements)); + $traverser->addVisitor(new NodeVisitor\NamespaceStmtPrefixer($prefix, $namespaceStatements, $globalWhitelister)); $traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtCollector($namespaceStatements, $useStatements)); $traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtPrefixer($prefix, $whitelist, $globalWhitelister));