Add partial group use support

Supported via Stmt\GroupUse which has Name $prefix in addition to
the usual.

Still missing: Mixed group uses.
This commit is contained in:
Nikita Popov 2015-06-12 23:05:28 +02:00
parent 583b560f71
commit 9620f79cdc
9 changed files with 2079 additions and 1798 deletions

View File

@ -150,11 +150,24 @@ top_statement:
| T_NAMESPACE namespace_name '{' top_statement_list '}' { $$ = Stmt\Namespace_[$2, $4]; }
| T_NAMESPACE '{' top_statement_list '}' { $$ = Stmt\Namespace_[null, $3]; }
| T_USE use_declarations ';' { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
| T_USE T_FUNCTION use_declarations ';' { $$ = Stmt\Use_[$3, Stmt\Use_::TYPE_FUNCTION]; }
| T_USE T_CONST use_declarations ';' { $$ = Stmt\Use_[$3, Stmt\Use_::TYPE_CONSTANT]; }
| T_USE use_type use_declarations ';' { $$ = Stmt\Use_[$3, $2]; }
| group_use_declaration { $$ = $1; }
| T_CONST constant_declaration_list ';' { $$ = Stmt\Const_[$2]; }
;
use_type:
T_FUNCTION { $$ = Stmt\Use_::TYPE_FUNCTION; }
| T_CONST { $$ = Stmt\Use_::TYPE_CONSTANT; }
;
/* Using namespace_name_parts here to avoid s/r conflict on T_NS_SEPARATOR */
group_use_declaration:
T_USE namespace_name_parts T_NS_SEPARATOR '{' use_declarations '}'
{ $$ = Stmt\GroupUse[Name[$2], $5, Stmt\Use_::TYPE_NORMAL]; }
| T_USE use_type namespace_name_parts T_NS_SEPARATOR '{' use_declarations '}'
{ $$ = Stmt\GroupUse[Name[$3], $6, $2]; }
;
use_declarations:
use_declarations ',' use_declaration { push($1, $3); }
| use_declaration { init($1); }

View File

@ -109,7 +109,7 @@ class Name extends NodeAbstract
* @param string|array|self $name The name to set the whole name to
*/
public function set($name) {
$this->parts = $this->prepareName($name);
$this->parts = self::prepareName($name);
}
/**
@ -118,7 +118,7 @@ class Name extends NodeAbstract
* @param string|array|self $name Name to prepend
*/
public function prepend($name) {
$this->parts = array_merge($this->prepareName($name), $this->parts);
$this->parts = array_merge(self::prepareName($name), $this->parts);
}
/**
@ -127,7 +127,7 @@ class Name extends NodeAbstract
* @param string|array|self $name Name to append
*/
public function append($name) {
$this->parts = array_merge($this->parts, $this->prepareName($name));
$this->parts = array_merge($this->parts, self::prepareName($name));
}
/**
@ -136,7 +136,7 @@ class Name extends NodeAbstract
* @param string|array|self $name The name to set the first part to
*/
public function setFirst($name) {
array_splice($this->parts, 0, 1, $this->prepareName($name));
array_splice($this->parts, 0, 1, self::prepareName($name));
}
/**
@ -145,7 +145,18 @@ class Name extends NodeAbstract
* @param string|array|self $name The name to set the last part to
*/
public function setLast($name) {
array_splice($this->parts, -1, 1, $this->prepareName($name));
array_splice($this->parts, -1, 1, self::prepareName($name));
}
/**
* Concatenate two names, yielding a new Name instance
*
* @param string|array|self The first name
* @param string|array|self The second name
* @return Name Concatenated name
*/
public static function concat($name1, $name2) {
return new Name(array_merge(self::prepareName($name1), self::prepareName($name2)));
}
/**
@ -156,7 +167,7 @@ class Name extends NodeAbstract
*
* @return array Prepared name
*/
protected function prepareName($name) {
private static function prepareName($name) {
if (is_string($name)) {
return explode('\\', $name);
} elseif (is_array($name)) {

View File

@ -0,0 +1,35 @@
<?php
namespace PhpParser\Node\Stmt;
use PhpParser\Node\Stmt;
use PhpParser\Node\Name;
class GroupUse extends Stmt
{
/** @var int Type of group use */
public $type;
/** @var Name Prefix for uses */
public $prefix;
/** @var UseUse[] Uses */
public $uses;
/**
* Constructs a group use node.
*
* @param Name $prefix Prefix for uses
* @param UseUse[] $uses Uses
* @param int $type Type of group use
* @param array $attributes Additional attributes
*/
public function __construct(Name $prefix, array $uses, $type = Use_::TYPE_NORMAL, array $attributes = array()) {
parent::__construct($attributes);
$this->type = $type;
$this->prefix = $prefix;
$this->uses = $uses;
}
public function getSubNodeNames() {
return array('type', 'prefix', 'uses');
}
}

View File

@ -26,7 +26,11 @@ class NameResolver extends NodeVisitorAbstract
$this->resetState($node->name);
} elseif ($node instanceof Stmt\Use_) {
foreach ($node->uses as $use) {
$this->addAlias($use, $node->type);
$this->addAlias($use, $node->type, null);
}
} elseif ($node instanceof Stmt\GroupUse) {
foreach ($node->uses as $use) {
$this->addAlias($use, $node->type, $node->prefix);
}
} elseif ($node instanceof Stmt\Class_) {
if (null !== $node->extends) {
@ -105,7 +109,7 @@ class NameResolver extends NodeVisitorAbstract
);
}
protected function addAlias(Stmt\UseUse $use, $type) {
protected function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) {
// Constant names are case sensitive, everything else case insensitive
if ($type === Stmt\Use_::TYPE_CONSTANT) {
$aliasName = $use->alias;
@ -113,6 +117,9 @@ class NameResolver extends NodeVisitorAbstract
$aliasName = strtolower($use->alias);
}
// Add prefix for group uses
$name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
if (isset($this->aliases[$type][$aliasName])) {
$typeStringMap = array(
Stmt\Use_::TYPE_NORMAL => '',
@ -123,13 +130,13 @@ class NameResolver extends NodeVisitorAbstract
throw new Error(
sprintf(
'Cannot use %s%s as %s because the name is already in use',
$typeStringMap[$type], $use->name, $use->alias
$typeStringMap[$type], $name, $use->alias
),
$use->getLine()
);
}
$this->aliases[$type][$aliasName] = $use->name;
$this->aliases[$type][$aliasName] = $name;
}
/** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */

File diff suppressed because it is too large Load Diff

View File

@ -516,17 +516,25 @@ class Standard extends PrettyPrinterAbstract
}
public function pStmt_Use(Stmt\Use_ $node) {
return 'use '
. ($node->type === Stmt\Use_::TYPE_FUNCTION ? 'function ' : '')
. ($node->type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : '')
return 'use ' . $this->pUseType($node->type)
. $this->pCommaSeparated($node->uses) . ';';
}
public function pStmt_GroupUse(Stmt\GroupUse $node) {
return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix)
. '\{' . $this->pCommaSeparated($node->uses) . '};';
}
public function pStmt_UseUse(Stmt\UseUse $node) {
return $this->p($node->name)
. ($node->name->getLast() !== $node->alias ? ' as ' . $node->alias : '');
}
private function pUseType($type) {
return ($type === Stmt\Use_::TYPE_FUNCTION ? 'function ' : '')
. ($type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : '');
}
public function pStmt_Interface(Stmt\Interface_ $node) {
return 'interface ' . $node->name
. (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '')

View File

@ -76,6 +76,20 @@ namespace Bar {
BAR\FOO;
BAZ\FOO;
}
namespace Baz {
use A\T\{B\C, D\E};
use function X\T\{b\c, d\e};
use const Y\T\{B\C, D\E};
new C;
new E;
new C\D;
new E\F;
c();
e();
C;
E;
}
EOC;
$expectedCode = <<<'EOC'
namespace Foo {
@ -127,6 +141,19 @@ namespace Bar {
\foo\FOO;
\Bar\BAZ\FOO;
}
namespace Baz {
use A\T\{B\C, D\E};
use function X\T\{b\c, d\e};
use const Y\T\{B\C, D\E};
new \A\T\B\C();
new \A\T\D\E();
new \A\T\B\C\D();
new \A\T\D\E\F();
\X\T\b\c();
\X\T\d\e();
\Y\T\B\C;
\Y\T\D\E;
}
EOC;
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);

View File

@ -0,0 +1,138 @@
Group use declarations
-----
<?php
use A\{B};
use A\{B\C, D};
use A\B\{C\D, E};
use function A\{b\c, d};
use const A\{B\C, D};
-----
array(
0: Stmt_GroupUse(
type: 1
prefix: Name(
parts: array(
0: A
)
)
uses: array(
0: Stmt_UseUse(
name: Name(
parts: array(
0: B
)
)
alias: B
)
)
)
1: Stmt_GroupUse(
type: 1
prefix: Name(
parts: array(
0: A
)
)
uses: array(
0: Stmt_UseUse(
name: Name(
parts: array(
0: B
1: C
)
)
alias: C
)
1: Stmt_UseUse(
name: Name(
parts: array(
0: D
)
)
alias: D
)
)
)
2: Stmt_GroupUse(
type: 1
prefix: Name(
parts: array(
0: A
1: B
)
)
uses: array(
0: Stmt_UseUse(
name: Name(
parts: array(
0: C
1: D
)
)
alias: D
)
1: Stmt_UseUse(
name: Name(
parts: array(
0: E
)
)
alias: E
)
)
)
3: Stmt_GroupUse(
type: 2
prefix: Name(
parts: array(
0: A
)
)
uses: array(
0: Stmt_UseUse(
name: Name(
parts: array(
0: b
1: c
)
)
alias: c
)
1: Stmt_UseUse(
name: Name(
parts: array(
0: d
)
)
alias: d
)
)
)
4: Stmt_GroupUse(
type: 3
prefix: Name(
parts: array(
0: A
)
)
uses: array(
0: Stmt_UseUse(
name: Name(
parts: array(
0: B
1: C
)
)
alias: C
)
1: Stmt_UseUse(
name: Name(
parts: array(
0: D
)
)
alias: D
)
)
)
)

View File

@ -0,0 +1,14 @@
Group use declaration
-----
<?php
use A\{B};
use A\{B\C, D};
use A\B\{C\D, E};
use function A\{b\c, d};
use const A\{B\C, D};
-----
use A\{B};
use A\{B\C, D};
use A\B\{C\D, E};
use function A\{b\c, d};
use const A\{B\C, D};