From fa7357b48398698a7463334488081318d108914a Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 22 Oct 2016 17:02:38 +0200 Subject: [PATCH] Represent empty Name::slice() using null Instead of a Name([]) dummy value, that is invalid in other contexts. --- lib/PhpParser/Node/Name.php | 38 ++++++++++++++++------ lib/PhpParser/NodeVisitor/NameResolver.php | 22 +++---------- test/PhpParser/Node/NameTest.php | 13 ++++---- 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/lib/PhpParser/Node/Name.php b/lib/PhpParser/Node/Name.php index 4e2f99a..cc0aece 100644 --- a/lib/PhpParser/Node/Name.php +++ b/lib/PhpParser/Node/Name.php @@ -104,15 +104,15 @@ class Name extends NodeAbstract * This method returns a new instance of the same type as the original and with the same * attributes. * - * If the slice is empty, a Name with an empty parts array is returned. While this is - * meaningless in itself, it works correctly in conjunction with concat(). + * If the slice is empty, null is returned. The null value will be correctly handled in + * concatenations using concat(). * * Offset and length have the same meaning as in array_slice(). * * @param int $offset Offset to start the slice at (may be negative) * @param int|null $length Length of the slice (may be negative) * - * @return static Sliced name + * @return static|null Sliced name */ public function slice($offset, $length = null) { $numParts = count($this->parts); @@ -131,6 +131,11 @@ class Name extends NodeAbstract } } + if ($realLength === 0) { + // Empty slice is represented as null + return null; + } + return new static(array_slice($this->parts, $realOffset, $realLength), $this->attributes); } @@ -140,16 +145,29 @@ class Name extends NodeAbstract * The type of the generated instance depends on which class this method is called on, for * example Name\FullyQualified::concat() will yield a Name\FullyQualified instance. * - * @param string|array|self $name1 The first name - * @param string|array|self $name2 The second name - * @param array $attributes Attributes to assign to concatenated name + * If one of the arguments is null, a new instance of the other name will be returned. If both + * arguments are null, null will be returned. As such, writing + * Name::concat($namespace, $shortName) + * where $namespace is a Name node or null will work as expected. * - * @return static Concatenated name + * @param string|array|self|null $name1 The first name + * @param string|array|self|null $name2 The second name + * @param array $attributes Attributes to assign to concatenated name + * + * @return static|null Concatenated name */ public static function concat($name1, $name2, array $attributes = []) { - return new static( - array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes - ); + if (null === $name1 && null === $name2) { + return null; + } elseif (null === $name1) { + return new static(self::prepareName($name2), $attributes); + } else if (null === $name2) { + return new static(self::prepareName($name1), $attributes); + } else { + return new static( + array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes + ); + } } /** diff --git a/lib/PhpParser/NodeVisitor/NameResolver.php b/lib/PhpParser/NodeVisitor/NameResolver.php index 08d8516..1e350c3 100644 --- a/lib/PhpParser/NodeVisitor/NameResolver.php +++ b/lib/PhpParser/NodeVisitor/NameResolver.php @@ -195,12 +195,8 @@ class NameResolver extends NodeVisitorAbstract return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes()); } - if (null !== $this->namespace) { - // if no alias exists prepend current namespace - return FullyQualified::concat($this->namespace, $name, $name->getAttributes()); - } - - return new FullyQualified($name, $name->getAttributes()); + // if no alias exists prepend current namespace + return FullyQualified::concat($this->namespace, $name, $name->getAttributes()); } protected function resolveOtherName(Name $name, $type) { @@ -239,19 +235,11 @@ class NameResolver extends NodeVisitorAbstract return $name; } - if (null !== $this->namespace) { - // if no alias exists prepend current namespace - return FullyQualified::concat($this->namespace, $name, $name->getAttributes()); - } - - return new FullyQualified($name, $name->getAttributes()); + // if no alias exists prepend current namespace + return FullyQualified::concat($this->namespace, $name, $name->getAttributes()); } protected function addNamespacedName(Node $node) { - if (null !== $this->namespace) { - $node->namespacedName = Name::concat($this->namespace, $node->name); - } else { - $node->namespacedName = new Name($node->name); - } + $node->namespacedName = Name::concat($this->namespace, $node->name); } } diff --git a/test/PhpParser/Node/NameTest.php b/test/PhpParser/Node/NameTest.php index d0d3616..76a89c3 100644 --- a/test/PhpParser/Node/NameTest.php +++ b/test/PhpParser/Node/NameTest.php @@ -36,16 +36,16 @@ class NameTest extends \PHPUnit_Framework_TestCase $name = new Name('foo\bar\baz'); $this->assertEquals(new Name('foo\bar\baz'), $name->slice(0)); $this->assertEquals(new Name('bar\baz'), $name->slice(1)); - $this->assertEquals(new Name([]), $name->slice(3)); + $this->assertNull($name->slice(3)); $this->assertEquals(new Name('foo\bar\baz'), $name->slice(-3)); $this->assertEquals(new Name('bar\baz'), $name->slice(-2)); $this->assertEquals(new Name('foo\bar'), $name->slice(0, -1)); - $this->assertEquals(new Name([]), $name->slice(0, -3)); + $this->assertNull($name->slice(0, -3)); $this->assertEquals(new Name('bar'), $name->slice(1, -1)); - $this->assertEquals(new Name([]), $name->slice(1, -2)); + $this->assertNull($name->slice(1, -2)); $this->assertEquals(new Name('bar'), $name->slice(-2, 1)); $this->assertEquals(new Name('bar'), $name->slice(-2, -1)); - $this->assertEquals(new Name([]), $name->slice(-2, -2)); + $this->assertNull($name->slice(-2, -2)); } /** @@ -93,8 +93,9 @@ class NameTest extends \PHPUnit_Framework_TestCase Name\Relative::concat(new Name\FullyQualified('foo\bar'), 'baz', $attributes) ); - $this->assertEquals(new Name('foo'), Name::concat([], 'foo')); - $this->assertEquals(new Name([]), Name::concat([], [])); + $this->assertEquals(new Name('foo'), Name::concat(null, 'foo')); + $this->assertEquals(new Name('foo'), Name::concat('foo', null)); + $this->assertNull(Name::concat(null, null)); } public function testIs() {