From de403b68768f7af093e81b349eae2439ea36695b Mon Sep 17 00:00:00 2001 From: Alexander Lisachenko Date: Fri, 23 Sep 2016 17:00:25 +0300 Subject: [PATCH 1/2] Add root namespace normalization, resolves #39 --- src/NodeVisitor/RootNamespaceNormalizer.php | 43 ++++++++++ src/ReflectionEngine.php | 19 ++--- src/ReflectionFile.php | 9 +-- tests/ReflectionFileTest.php | 2 +- tests/ReflectionParameterTest.php | 13 +++ tests/Stub/FileWithParameters.php | 87 +++++++++++++-------- 6 files changed, 124 insertions(+), 49 deletions(-) create mode 100644 src/NodeVisitor/RootNamespaceNormalizer.php diff --git a/src/NodeVisitor/RootNamespaceNormalizer.php b/src/NodeVisitor/RootNamespaceNormalizer.php new file mode 100644 index 00000000..dbab866b --- /dev/null +++ b/src/NodeVisitor/RootNamespaceNormalizer.php @@ -0,0 +1,43 @@ + + * + * This source file is subject to the license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Go\ParserReflection\NodeVisitor; + +use PhpParser\Node; +use PhpParser\Node\Name\FullyQualified; +use PhpParser\Node\Stmt\Namespace_; +use PhpParser\NodeVisitorAbstract; + +/** + * Visitor to normalize the root namespace for the files without the namespace (root namespace) + * + * File->Namespace->Statements + */ +class RootNamespaceNormalizer extends NodeVisitorAbstract +{ + /** + * {@inheritdoc} + */ + public function beforeTraverse(array $nodes) + { + // namespaces can be only top-level nodes, so we can scan them directly + foreach ($nodes as $topLevelNode) { + if ($topLevelNode instanceof Namespace_) { + // file has namespace in it, nothing to change, returning null + return null; + } + } + + // if we don't have a namespaces at all, this is global namespace, wrap everything in it + $globalNamespaceNode = new Namespace_(new FullyQualified(''), $nodes); + + return [$globalNamespaceNode]; + } +} diff --git a/src/ReflectionEngine.php b/src/ReflectionEngine.php index 5c5ffc05..4e975396 100644 --- a/src/ReflectionEngine.php +++ b/src/ReflectionEngine.php @@ -10,6 +10,7 @@ namespace Go\ParserReflection; +use Go\ParserReflection\NodeVisitor\RootNamespaceNormalizer; use PhpParser\Lexer; use PhpParser\Node; use PhpParser\Node\Stmt\ClassLike; @@ -67,6 +68,7 @@ public static function init(LocatorInterface $locator) self::$traverser = $traverser = new NodeTraverser(); $traverser->addVisitor(new NameResolver()); + $traverser->addVisitor(new RootNamespaceNormalizer()); self::$locator = $locator; } @@ -126,14 +128,9 @@ public static function parseClass($fullClassName) $className = array_pop($namespaceParts); $namespaceName = join('\\', $namespaceParts); - if ($namespaceName) { - // we have a namespace nodes somewhere - $namespace = self::parseFileNamespace($classFileName, $namespaceName); - $namespaceNodes = $namespace->stmts; - } else { - // global namespace - $namespaceNodes = self::parseFile($classFileName); - } + // we have a namespace node somewhere + $namespace = self::parseFileNamespace($classFileName, $namespaceName); + $namespaceNodes = $namespace->stmts; foreach ($namespaceNodes as $namespaceLevelNode) { if ($namespaceLevelNode instanceof ClassLike && $namespaceLevelNode->name == $className) { @@ -236,7 +233,11 @@ public static function parseFileNamespace($fileName, $namespaceName) $topLevelNodes = self::parseFile($fileName); // namespaces can be only top-level nodes, so we can scan them directly foreach ($topLevelNodes as $topLevelNode) { - if ($topLevelNode instanceof Namespace_ && ($topLevelNode->name->toString() == $namespaceName)) { + if (!$topLevelNode instanceof Namespace_) { + continue; + } + $topLevelNodeName = $topLevelNode->name ? $topLevelNode->name->toString() : ''; + if ($topLevelNodeName === $namespaceName) { return $topLevelNode; } } diff --git a/src/ReflectionFile.php b/src/ReflectionFile.php index f2a5f8d5..353e7244 100644 --- a/src/ReflectionFile.php +++ b/src/ReflectionFile.php @@ -12,7 +12,6 @@ use PhpParser\Node; -use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Namespace_; /** @@ -120,7 +119,7 @@ private function findFileNamespaces() // namespaces can be only top-level nodes, so we can scan them directly foreach ($this->topLevelNodes as $topLevelNode) { if ($topLevelNode instanceof Namespace_) { - $namespaceName = $topLevelNode->name ? $topLevelNode->name->toString() : '\\'; + $namespaceName = $topLevelNode->name ? $topLevelNode->name->toString() : ''; $namespaces[$namespaceName] = new ReflectionFileNamespace( $this->fileName, @@ -130,12 +129,6 @@ private function findFileNamespaces() } } - if (!$namespaces) { - // if we don't have a namespaces at all, this is global namespace - $globalNamespaceNode = new Namespace_(new FullyQualified(''), $this->topLevelNodes); - $namespaces['\\'] = new ReflectionFileNamespace($this->fileName, '\\', $globalNamespaceNode); - } - return $namespaces; } } diff --git a/tests/ReflectionFileTest.php b/tests/ReflectionFileTest.php index 948720f4..864ea2ce 100644 --- a/tests/ReflectionFileTest.php +++ b/tests/ReflectionFileTest.php @@ -55,7 +55,7 @@ public function testGetGlobalFileNamespace() $fileName = stream_resolve_include_path(__DIR__ . self::STUB_GLOBAL_FILE); $reflectionFile = new ReflectionFile($fileName); - $reflectionFileNamespace = $reflectionFile->getFileNamespace('\\'); + $reflectionFileNamespace = $reflectionFile->getFileNamespace(''); $this->assertInstanceOf(ReflectionFileNamespace::class, $reflectionFileNamespace); } } diff --git a/tests/ReflectionParameterTest.php b/tests/ReflectionParameterTest.php index af70a286..8b35d8e6 100644 --- a/tests/ReflectionParameterTest.php +++ b/tests/ReflectionParameterTest.php @@ -3,6 +3,7 @@ use Go\ParserReflection\Stub\Foo; use Go\ParserReflection\Stub\SubFoo; +use TestParametersForRootNsClass; class ReflectionParameterTest extends \PHPUnit_Framework_TestCase { @@ -105,6 +106,18 @@ public function testGetClassMethodReturnsSelfAndParent() $this->assertSame(Foo::class, $parentParam->getName()); } + public function testNonConstantsResolvedForGlobalNamespace() + { + $parsedNamespace = $this->parsedRefFile->getFileNamespace(''); + $parsedClass = $parsedNamespace->getClass(TestParametersForRootNsClass::class); + $parsedFunction = $parsedClass->getMethod('foo'); + + $parameters = $parsedFunction->getParameters(); + $this->assertSame(null, $parameters[0]->getDefaultValue()); + $this->assertSame(false, $parameters[1]->getDefaultValue()); + $this->assertSame(true, $parameters[2]->getDefaultValue()); + } + public function testGetDeclaringClassMethodReturnsObject() { $parsedNamespace = $this->parsedRefFile->getFileNamespace('Go\ParserReflection\Stub'); diff --git a/tests/Stub/FileWithParameters.php b/tests/Stub/FileWithParameters.php index bb0f348a..bf04211a 100644 --- a/tests/Stub/FileWithParameters.php +++ b/tests/Stub/FileWithParameters.php @@ -1,36 +1,61 @@ Date: Sun, 25 Sep 2016 14:18:18 +0300 Subject: [PATCH 2/2] Fix code-style issues from Nitpick --- tests/Stub/FileWithParameters.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/Stub/FileWithParameters.php b/tests/Stub/FileWithParameters.php index bf04211a..60bc9afd 100644 --- a/tests/Stub/FileWithParameters.php +++ b/tests/Stub/FileWithParameters.php @@ -1,10 +1,15 @@