Skip to content

Commit de403b6

Browse files
committed
Add root namespace normalization, resolves #39
1 parent 40032e3 commit de403b6

File tree

6 files changed

+124
-49
lines changed

6 files changed

+124
-49
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
/**
3+
* Parser Reflection API
4+
*
5+
* @copyright Copyright 2016, Lisachenko Alexander <lisachenko.it@gmail.com>
6+
*
7+
* This source file is subject to the license that is bundled
8+
* with this source code in the file LICENSE.
9+
*/
10+
11+
namespace Go\ParserReflection\NodeVisitor;
12+
13+
use PhpParser\Node;
14+
use PhpParser\Node\Name\FullyQualified;
15+
use PhpParser\Node\Stmt\Namespace_;
16+
use PhpParser\NodeVisitorAbstract;
17+
18+
/**
19+
* Visitor to normalize the root namespace for the files without the namespace (root namespace)
20+
*
21+
* File->Namespace->Statements
22+
*/
23+
class RootNamespaceNormalizer extends NodeVisitorAbstract
24+
{
25+
/**
26+
* {@inheritdoc}
27+
*/
28+
public function beforeTraverse(array $nodes)
29+
{
30+
// namespaces can be only top-level nodes, so we can scan them directly
31+
foreach ($nodes as $topLevelNode) {
32+
if ($topLevelNode instanceof Namespace_) {
33+
// file has namespace in it, nothing to change, returning null
34+
return null;
35+
}
36+
}
37+
38+
// if we don't have a namespaces at all, this is global namespace, wrap everything in it
39+
$globalNamespaceNode = new Namespace_(new FullyQualified(''), $nodes);
40+
41+
return [$globalNamespaceNode];
42+
}
43+
}

src/ReflectionEngine.php

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Go\ParserReflection;
1212

13+
use Go\ParserReflection\NodeVisitor\RootNamespaceNormalizer;
1314
use PhpParser\Lexer;
1415
use PhpParser\Node;
1516
use PhpParser\Node\Stmt\ClassLike;
@@ -67,6 +68,7 @@ public static function init(LocatorInterface $locator)
6768

6869
self::$traverser = $traverser = new NodeTraverser();
6970
$traverser->addVisitor(new NameResolver());
71+
$traverser->addVisitor(new RootNamespaceNormalizer());
7072

7173
self::$locator = $locator;
7274
}
@@ -126,14 +128,9 @@ public static function parseClass($fullClassName)
126128
$className = array_pop($namespaceParts);
127129
$namespaceName = join('\\', $namespaceParts);
128130

129-
if ($namespaceName) {
130-
// we have a namespace nodes somewhere
131-
$namespace = self::parseFileNamespace($classFileName, $namespaceName);
132-
$namespaceNodes = $namespace->stmts;
133-
} else {
134-
// global namespace
135-
$namespaceNodes = self::parseFile($classFileName);
136-
}
131+
// we have a namespace node somewhere
132+
$namespace = self::parseFileNamespace($classFileName, $namespaceName);
133+
$namespaceNodes = $namespace->stmts;
137134

138135
foreach ($namespaceNodes as $namespaceLevelNode) {
139136
if ($namespaceLevelNode instanceof ClassLike && $namespaceLevelNode->name == $className) {
@@ -236,7 +233,11 @@ public static function parseFileNamespace($fileName, $namespaceName)
236233
$topLevelNodes = self::parseFile($fileName);
237234
// namespaces can be only top-level nodes, so we can scan them directly
238235
foreach ($topLevelNodes as $topLevelNode) {
239-
if ($topLevelNode instanceof Namespace_ && ($topLevelNode->name->toString() == $namespaceName)) {
236+
if (!$topLevelNode instanceof Namespace_) {
237+
continue;
238+
}
239+
$topLevelNodeName = $topLevelNode->name ? $topLevelNode->name->toString() : '';
240+
if ($topLevelNodeName === $namespaceName) {
240241
return $topLevelNode;
241242
}
242243
}

src/ReflectionFile.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313

1414
use PhpParser\Node;
15-
use PhpParser\Node\Name\FullyQualified;
1615
use PhpParser\Node\Stmt\Namespace_;
1716

1817
/**
@@ -120,7 +119,7 @@ private function findFileNamespaces()
120119
// namespaces can be only top-level nodes, so we can scan them directly
121120
foreach ($this->topLevelNodes as $topLevelNode) {
122121
if ($topLevelNode instanceof Namespace_) {
123-
$namespaceName = $topLevelNode->name ? $topLevelNode->name->toString() : '\\';
122+
$namespaceName = $topLevelNode->name ? $topLevelNode->name->toString() : '';
124123

125124
$namespaces[$namespaceName] = new ReflectionFileNamespace(
126125
$this->fileName,
@@ -130,12 +129,6 @@ private function findFileNamespaces()
130129
}
131130
}
132131

133-
if (!$namespaces) {
134-
// if we don't have a namespaces at all, this is global namespace
135-
$globalNamespaceNode = new Namespace_(new FullyQualified(''), $this->topLevelNodes);
136-
$namespaces['\\'] = new ReflectionFileNamespace($this->fileName, '\\', $globalNamespaceNode);
137-
}
138-
139132
return $namespaces;
140133
}
141134
}

tests/ReflectionFileTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public function testGetGlobalFileNamespace()
5555
$fileName = stream_resolve_include_path(__DIR__ . self::STUB_GLOBAL_FILE);
5656
$reflectionFile = new ReflectionFile($fileName);
5757

58-
$reflectionFileNamespace = $reflectionFile->getFileNamespace('\\');
58+
$reflectionFileNamespace = $reflectionFile->getFileNamespace('');
5959
$this->assertInstanceOf(ReflectionFileNamespace::class, $reflectionFileNamespace);
6060
}
6161
}

tests/ReflectionParameterTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use Go\ParserReflection\Stub\Foo;
55
use Go\ParserReflection\Stub\SubFoo;
6+
use TestParametersForRootNsClass;
67

78
class ReflectionParameterTest extends \PHPUnit_Framework_TestCase
89
{
@@ -105,6 +106,18 @@ public function testGetClassMethodReturnsSelfAndParent()
105106
$this->assertSame(Foo::class, $parentParam->getName());
106107
}
107108

109+
public function testNonConstantsResolvedForGlobalNamespace()
110+
{
111+
$parsedNamespace = $this->parsedRefFile->getFileNamespace('');
112+
$parsedClass = $parsedNamespace->getClass(TestParametersForRootNsClass::class);
113+
$parsedFunction = $parsedClass->getMethod('foo');
114+
115+
$parameters = $parsedFunction->getParameters();
116+
$this->assertSame(null, $parameters[0]->getDefaultValue());
117+
$this->assertSame(false, $parameters[1]->getDefaultValue());
118+
$this->assertSame(true, $parameters[2]->getDefaultValue());
119+
}
120+
108121
public function testGetDeclaringClassMethodReturnsObject()
109122
{
110123
$parsedNamespace = $this->parsedRefFile->getFileNamespace('Go\ParserReflection\Stub');

tests/Stub/FileWithParameters.php

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,61 @@
11
<?php
22

3-
namespace Go\ParserReflection\Stub;
4-
5-
use Go\ParserReflection\ReflectionParameter;
6-
7-
const TEST_PARAMETER = 42;
8-
9-
function noParameters() {}
10-
function singleParameter($test) {}
11-
function miscParameters(
12-
array $arrayParam,
13-
array $arrayParamWithDefault = array(1,2,3),
14-
array $arrayNullable = null,
15-
callable $callableParam,
16-
callable $callableNullable = null,
17-
\stdClass $objectParam,
18-
\stdClass $objectNullable = null,
19-
ReflectionParameter $typehintedParamWithNs,
20-
&$byReferenceParam,
21-
&$byReferenceNullable = __CLASS__,
22-
$constParam = TEST_PARAMETER,
23-
$constValueParam = __NAMESPACE__, // This line is long and should be truncated
24-
\Traversable $traversable
25-
) {}
26-
27-
class Foo {
28-
const CLASS_CONST = __CLASS__;
29-
30-
public function methodParam($firstParam, $optionalParam = null) {}
31-
public function methodParamConst($firstParam = self::CLASS_CONST, $another = __CLASS__, $ns = TEST_PARAMETER) {}
3+
namespace {
4+
function testResolveDefaults($a=null, $b=false, $c=true) {}
5+
6+
class TestParametersForRootNsClass {
7+
function foo($a=null, $b=false, $c=true) {}
8+
}
329
}
3310

34-
class SubFoo extends Foo {
35-
public function anotherMethodParam(self $selfParam, parent $parentParam) {}
11+
namespace Go\ParserReflection\Stub {
12+
13+
use Go\ParserReflection\ReflectionParameter;
14+
15+
const TEST_PARAMETER = 42;
16+
17+
function noParameters()
18+
{
19+
}
20+
21+
function singleParameter($test)
22+
{
23+
}
24+
25+
function miscParameters(
26+
array $arrayParam,
27+
array $arrayParamWithDefault = array(1, 2, 3),
28+
array $arrayNullable = null,
29+
callable $callableParam,
30+
callable $callableNullable = null,
31+
\stdClass $objectParam,
32+
\stdClass $objectNullable = null,
33+
ReflectionParameter $typehintedParamWithNs,
34+
&$byReferenceParam,
35+
&$byReferenceNullable = __CLASS__,
36+
$constParam = TEST_PARAMETER,
37+
$constValueParam = __NAMESPACE__, // This line is long and should be truncated
38+
\Traversable $traversable
39+
) {
40+
}
41+
42+
class Foo
43+
{
44+
const CLASS_CONST = __CLASS__;
45+
46+
public function methodParam($firstParam, $optionalParam = null)
47+
{
48+
}
49+
50+
public function methodParamConst($firstParam = self::CLASS_CONST, $another = __CLASS__, $ns = TEST_PARAMETER)
51+
{
52+
}
53+
}
54+
55+
class SubFoo extends Foo
56+
{
57+
public function anotherMethodParam(self $selfParam, parent $parentParam)
58+
{
59+
}
60+
}
3661
}

0 commit comments

Comments
 (0)