Skip to content

Commit

Permalink
Store string instead of array for Name
Browse files Browse the repository at this point in the history
Name::$parts array is replaced by Name::$name string.
  • Loading branch information
nikic committed Oct 22, 2016
1 parent 7672b97 commit 9b0b3df
Show file tree
Hide file tree
Showing 64 changed files with 336 additions and 727 deletions.
24 changes: 12 additions & 12 deletions grammar/php5.y
Expand Up @@ -40,13 +40,13 @@ identifier:
| semi_reserved { $$ = $1; }
;

namespace_name_parts:
T_STRING { init($1); }
| namespace_name_parts T_NS_SEPARATOR T_STRING { push($1, $3); }
raw_namespace_name:
T_STRING { $$ = $1; }
| raw_namespace_name T_NS_SEPARATOR T_STRING { $$ = $1 . '\\' . $3; }
;

namespace_name:
namespace_name_parts { $$ = Name[$1]; }
raw_namespace_name { $$ = Name[$1]; }
;

top_statement:
Expand All @@ -72,15 +72,15 @@ use_type:
| T_CONST { $$ = Stmt\Use_::TYPE_CONSTANT; }
;

/* Using namespace_name_parts here to avoid s/r conflict on T_NS_SEPARATOR */
/* Using raw_namespace_name here to avoid s/r conflict on T_NS_SEPARATOR */
group_use_declaration:
T_USE use_type namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
T_USE use_type raw_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
{ $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, $2]; }
| T_USE use_type T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
| T_USE use_type T_NS_SEPARATOR raw_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
{ $$ = Stmt\GroupUse[new Name($4, stackAttributes(#4)), $7, $2]; }
| T_USE namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
| T_USE raw_namespace_name T_NS_SEPARATOR '{' inline_use_declarations '}'
{ $$ = Stmt\GroupUse[new Name($2, stackAttributes(#2)), $5, Stmt\Use_::TYPE_UNKNOWN]; }
| T_USE T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
| T_USE T_NS_SEPARATOR raw_namespace_name T_NS_SEPARATOR '{' inline_use_declarations '}'
{ $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, Stmt\Use_::TYPE_UNKNOWN]; }
;

Expand Down Expand Up @@ -705,9 +705,9 @@ class_name:
;

name:
namespace_name_parts { $$ = Name[$1]; }
| T_NS_SEPARATOR namespace_name_parts { $$ = Name\FullyQualified[$2]; }
| T_NAMESPACE T_NS_SEPARATOR namespace_name_parts { $$ = Name\Relative[$3]; }
raw_namespace_name { $$ = Name[$1]; }
| T_NS_SEPARATOR raw_namespace_name { $$ = Name\FullyQualified[$2]; }
| T_NAMESPACE T_NS_SEPARATOR raw_namespace_name { $$ = Name\Relative[$3]; }
;

class_name_reference:
Expand Down
24 changes: 12 additions & 12 deletions grammar/php7.y
Expand Up @@ -40,13 +40,13 @@ identifier:
| semi_reserved { $$ = $1; }
;

namespace_name_parts:
T_STRING { init($1); }
| namespace_name_parts T_NS_SEPARATOR T_STRING { push($1, $3); }
raw_namespace_name:
T_STRING { $$ = $1; }
| raw_namespace_name T_NS_SEPARATOR T_STRING { $$ = $1 . '\\' . $3; }
;

namespace_name:
namespace_name_parts { $$ = Name[$1]; }
raw_namespace_name { $$ = Name[$1]; }
;

top_statement:
Expand All @@ -72,15 +72,15 @@ use_type:
| T_CONST { $$ = Stmt\Use_::TYPE_CONSTANT; }
;

/* Using namespace_name_parts here to avoid s/r conflict on T_NS_SEPARATOR */
/* Using raw_namespace_name here to avoid s/r conflict on T_NS_SEPARATOR */
group_use_declaration:
T_USE use_type namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
T_USE use_type raw_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
{ $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, $2]; }
| T_USE use_type T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
| T_USE use_type T_NS_SEPARATOR raw_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
{ $$ = Stmt\GroupUse[new Name($4, stackAttributes(#4)), $7, $2]; }
| T_USE namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
| T_USE raw_namespace_name T_NS_SEPARATOR '{' inline_use_declarations '}'
{ $$ = Stmt\GroupUse[new Name($2, stackAttributes(#2)), $5, Stmt\Use_::TYPE_UNKNOWN]; }
| T_USE T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
| T_USE T_NS_SEPARATOR raw_namespace_name T_NS_SEPARATOR '{' inline_use_declarations '}'
{ $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, Stmt\Use_::TYPE_UNKNOWN]; }
;

Expand Down Expand Up @@ -659,9 +659,9 @@ class_name:
;

name:
namespace_name_parts { $$ = Name[$1]; }
| T_NS_SEPARATOR namespace_name_parts { $$ = Name\FullyQualified[$2]; }
| T_NAMESPACE T_NS_SEPARATOR namespace_name_parts { $$ = Name\Relative[$3]; }
raw_namespace_name { $$ = Name[$1]; }
| T_NS_SEPARATOR raw_namespace_name { $$ = Name\FullyQualified[$2]; }
| T_NAMESPACE T_NS_SEPARATOR raw_namespace_name { $$ = Name\Relative[$3]; }
;

class_name_reference:
Expand Down
91 changes: 59 additions & 32 deletions lib/PhpParser/Node/Name.php
Expand Up @@ -6,26 +6,22 @@

class Name extends NodeAbstract
{
/** @var string[] Parts of the name */
public $parts;
/** @var string Name */
public $name;

/**
* Constructs a name node.
*
* @param string|array $parts Parts of the name (or name as string)
* @param string|array $name Name or parts of name
* @param array $attributes Additional attributes
*/
public function __construct($parts, array $attributes = array()) {
if (!is_array($parts)) {
$parts = explode('\\', $parts);
}

public function __construct($name, array $attributes = array()) {
parent::__construct($attributes);
$this->parts = $parts;
$this->name = self::prepareName($name);
}

public function getSubNodeNames() {
return array('parts');
return array('name');
}

/**
Expand All @@ -34,7 +30,10 @@ public function getSubNodeNames() {
* @return string First part of the name
*/
public function getFirst() {
return $this->parts[0];
if (false !== $first = strpos($this->name, '\\')) {
return substr($this->name, 0, $first);
}
return $this->name;
}

/**
Expand All @@ -43,7 +42,10 @@ public function getFirst() {
* @return string Last part of the name
*/
public function getLast() {
return $this->parts[count($this->parts) - 1];
if (false !== $last = strrpos($this->name, '\\')) {
return substr($this->name, $last + 1);
}
return $this->name;
}

/**
Expand All @@ -52,7 +54,7 @@ public function getLast() {
* @return bool Whether the name is unqualified
*/
public function isUnqualified() {
return 1 == count($this->parts);
return false === strpos($this->name, '\\');
}

/**
Expand All @@ -61,7 +63,7 @@ public function isUnqualified() {
* @return bool Whether the name is qualified
*/
public function isQualified() {
return 1 < count($this->parts);
return false !== strpos($this->name, '\\');
}

/**
Expand Down Expand Up @@ -89,7 +91,7 @@ public function isRelative() {
* @return string String representation
*/
public function toString() {
return implode('\\', $this->parts);
return $this->name;
}

/**
Expand All @@ -99,7 +101,7 @@ public function toString() {
* @return string String representation
*/
public function __toString() {
return implode('\\', $this->parts);
return $this->name;
}

/**
Expand All @@ -108,18 +110,27 @@ public function __toString() {
* 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 is handled correctly in conjunction
* with 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, or null for empty slices
*/
public function slice($offset, $length = null) {
$numParts = count($this->parts);
if ($offset === 1 && $length === null) {
// Short-circuit the common case
if (false !== $first = strpos($this->name, '\\')) {
return new static(substr($this->name, $first + 1));
}
return null;
}

$parts = explode('\\', $this->name);
$numParts = count($parts);

$realOffset = $offset < 0 ? $offset + $numParts : $offset;
if ($realOffset < 0 || $realOffset > $numParts) {
Expand All @@ -135,7 +146,11 @@ public function slice($offset, $length = null) {
}
}

return new static(array_slice($this->parts, $realOffset, $realLength), $this->attributes);
$slice = array_slice($parts, $realOffset, $realLength);
if (empty($slice)) {
return null;
}
return new static(implode('\\', $slice), $this->attributes);
}

/**
Expand All @@ -144,37 +159,49 @@ public function slice($offset, $length = null) {
* 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
* Concatenation with null returns a new instance of the other name (or null if both are null).
*
* @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 Concatenated name
*/
public static function concat($name1, $name2, array $attributes = []) {
if (null === $name1 && null === $name2) {
return null;
} else if (null === $name1) {
return new static(self::prepareName($name2), $attributes);
} else if (null === $name2) {
return new static(self::prepareName($name1), $attributes);
}
return new static(
array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes
self::prepareName($name1) . '\\' . self::prepareName($name2), $attributes
);
}

/**
* Prepares a (string, array or Name node) name for use in name changing methods by converting
* it to an array.
* it into a string.
*
* @param string|array|self $name Name to prepare
*
* @return array Prepared name
* @return string Prepared name
*/
private static function prepareName($name) {
if (is_string($name)) {
return explode('\\', $name);
} elseif (is_array($name)) {
if (\is_string($name)) {
return $name;
} elseif ($name instanceof self) {
return $name->parts;
return $name->name;
} elseif (\is_array($name)) {
if (empty($name)) {
throw new \InvalidArgumentException('Name part array cannot be empty');
}
return implode('\\', $name);
}

throw new \InvalidArgumentException(
'When changing a name you need to pass either a string, an array or a Name node'
'Expected string or Name node (or array -- deprecated)'
);
}
}
6 changes: 3 additions & 3 deletions lib/PhpParser/NodeVisitor/NameResolver.php
Expand Up @@ -200,7 +200,7 @@ protected function resolveClassName(Name $name) {
return FullyQualified::concat($this->namespace, $name, $name->getAttributes());
}

return new FullyQualified($name->parts, $name->getAttributes());
return new FullyQualified($name, $name->getAttributes());
}

protected function resolveOtherName(Name $name, $type) {
Expand Down Expand Up @@ -229,7 +229,7 @@ protected function resolveOtherName(Name $name, $type) {

if (null === $this->namespace) {
// outside of a namespace unaliased unqualified is same as fully qualified
return new FullyQualified($name->parts, $name->getAttributes());
return new FullyQualified($name, $name->getAttributes());
}

// unqualified names inside a namespace cannot be resolved at compile-time
Expand All @@ -244,7 +244,7 @@ protected function resolveOtherName(Name $name, $type) {
return FullyQualified::concat($this->namespace, $name, $name->getAttributes());
}

return new FullyQualified($name->parts, $name->getAttributes());
return new FullyQualified($name, $name->getAttributes());
}

protected function addNamespacedName(Node $node) {
Expand Down
4 changes: 2 additions & 2 deletions lib/PhpParser/Parser/Php5.php
Expand Up @@ -1241,11 +1241,11 @@ protected function reduceRule81() {
}

protected function reduceRule82() {
$this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
$this->semValue = $this->semStack[$this->stackPos-(1-1)];
}

protected function reduceRule83() {
$this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
$this->semValue = $this->semStack[$this->stackPos-(3-1)] . '\\' . $this->semStack[$this->stackPos-(3-3)];
}

protected function reduceRule84() {
Expand Down
4 changes: 2 additions & 2 deletions lib/PhpParser/Parser/Php7.php
Expand Up @@ -1124,11 +1124,11 @@ protected function reduceRule81() {
}

protected function reduceRule82() {
$this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
$this->semValue = $this->semStack[$this->stackPos-(1-1)];
}

protected function reduceRule83() {
$this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
$this->semValue = $this->semStack[$this->stackPos-(3-1)] . '\\' . $this->semStack[$this->stackPos-(3-3)];
}

protected function reduceRule84() {
Expand Down
6 changes: 3 additions & 3 deletions lib/PhpParser/PrettyPrinter/Standard.php
Expand Up @@ -40,15 +40,15 @@ protected function pNullableType(Node\NullableType $node) {
// Names

protected function pName(Name $node) {
return implode('\\', $node->parts);
return $node->name;
}

protected function pName_FullyQualified(Name\FullyQualified $node) {
return '\\' . implode('\\', $node->parts);
return '\\' . $node->name;
}

protected function pName_Relative(Name\Relative $node) {
return 'namespace\\' . implode('\\', $node->parts);
return 'namespace\\' . $node->name;
}

// Magic Constants
Expand Down

0 comments on commit 9b0b3df

Please sign in to comment.