mirror of
https://github.com/danog/PHP-Parser.git
synced 2024-11-26 20:04:48 +01:00
Add an example
This commit is contained in:
parent
2ec6ae4b03
commit
3b02facf0c
@ -226,4 +226,128 @@ The `NameResolver` takes care of that and resolves names as far as possible.
|
||||
After running it most names will be fully qualified. The only names that will stay unqualified are
|
||||
unqualified function and constant names. These are resolved at runtime and thus the visitor can't
|
||||
know which function they are referring to. In most cases this is a non-issue as the global functions
|
||||
are meant.
|
||||
are meant.
|
||||
|
||||
Also the `NameResolver` adds a `namespacedName` subnode to class, function and constant declarations
|
||||
that contains the namespaced name instead of only the shortname that is available via `name`.
|
||||
|
||||
Example: Converting namespaced code to pseudo namespaces
|
||||
--------------------------------------------------------
|
||||
|
||||
A small example to understand the concept: We want to convert namespaced code to pseudo namespaces
|
||||
so it works on 5.2, i.e. name like `A\\B` should be converted to `A_B`. Note that such conversions
|
||||
are fairly complicated if you take PHP's dynamic features into account, so our conversion will
|
||||
assume that no dynamic features are used.
|
||||
|
||||
We start off with the following base code:
|
||||
|
||||
const IN_DIR = '/some/path';
|
||||
const OUT_DIR = '/some/other/path';
|
||||
|
||||
$parser = new PHPParser_Parser;
|
||||
$traverser = new PHPParser_NodeTraverser;
|
||||
$prettyPrinter = new PHPParser_PrettyPrinter_Zend;
|
||||
|
||||
$traverser->addVisitor(new PHPParser_NodeVisitor_NameResolver); // we will need resolved names
|
||||
$traverser->addVisitor(new NodeVisitor_NamespaceConverter); // our own node visitor
|
||||
|
||||
// iterate over all files in the directory
|
||||
foreach (new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator(IN_DIR),
|
||||
RecursiveIteratorIterator::LEAVES_ONLY)
|
||||
as $file) {
|
||||
// only convert .php files
|
||||
if (!preg_match('~\.php$~', $file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// read the file that should be converted
|
||||
$code = file_get_contents($file);
|
||||
|
||||
// parse
|
||||
$stmts = $parser->parse(new PHPParser_Lexer($code));
|
||||
|
||||
// traverse
|
||||
$stmts = $traverser->traverse($stmts);
|
||||
|
||||
// pretty print
|
||||
$code = '<?php ' . $prettyPrinter->prettyPrint($stmts);
|
||||
|
||||
// write the converted file to the target directory
|
||||
file_put_contents(
|
||||
substr_replace($file->getPathname(), OUT_DIR, 0, strlen(IN_DIR)),
|
||||
$code
|
||||
);
|
||||
} catch (PHPParser_Error $e) {
|
||||
echo 'Parse Error: ', $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
Now lets start with the main code, the `NodeVisitor_NamespaceConverter`. One thing it needs to do
|
||||
is convert `A\\B` style names to `A_B` style ones.
|
||||
|
||||
class NodeVisitor_NamespaceConverter extends PHPParser_NodeVisitorAbstract
|
||||
{
|
||||
public function leaveNode(PHPParser_Node $node) {
|
||||
if ($node instanceof PHPParser_Node_Name) {
|
||||
return new PHPParser_Node_Name($node->toString('_'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The above code profits from the fact that the `NameResolver` already resolved all names as far as
|
||||
possible, so we don't need to do that. All the need to create a string with the name parts separated
|
||||
by underscores instead of backslashes. This is what `$node->toString('_')` does. (If you want to
|
||||
create a name with backslashes either write `$node->toString()` or `(string) $node`.) Then we create
|
||||
a new name from the string and return it. Returning a new node replaces the old node.
|
||||
|
||||
Another thing we need to do is change the class/function/const declarations. Currently they contain
|
||||
only the shortname (i.e. the last part of the name), but they need to contain the complete class
|
||||
name:
|
||||
|
||||
class NodeVisitor_NamespaceConverter extends PHPParser_NodeVisitorAbstract
|
||||
{
|
||||
public function leaveNode(PHPParser_Node $node) {
|
||||
if ($node instanceof PHPParser_Node_Name) {
|
||||
return new PHPParser_Node_Name($node->toString('_'));
|
||||
} elseif ($node instanceof PHPParser_Node_Stmt_Class
|
||||
|| $node instanceof PHPParser_Node_Stmt_Interface
|
||||
|| $node instanceof PHPParser_Node_Stmt_Function) {
|
||||
$node->name = $node->namespacedName->toString('_');
|
||||
} elseif ($node instanceof PHPParser_Node_Stmt_Const) {
|
||||
foreach ($node->consts as $const) {
|
||||
$const->name = $const->namespacedName->toString('_');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
There is not much more to it than converting the namespaced name to string with `_` as separator.
|
||||
|
||||
The last thing we need to do is remove the `namespace` and `use` statements:
|
||||
|
||||
class NodeVisitor_NamespaceConverter extends PHPParser_NodeVisitorAbstract
|
||||
{
|
||||
public function leaveNode(PHPParser_Node $node) {
|
||||
if ($node instanceof PHPParser_Node_Name) {
|
||||
return new PHPParser_Node_Name($node->toString('_'));
|
||||
} elseif ($node instanceof PHPParser_Node_Stmt_Class
|
||||
|| $node instanceof PHPParser_Node_Stmt_Interface
|
||||
|| $node instanceof PHPParser_Node_Stmt_Function) {
|
||||
$node->name = $node->namespacedName->toString('_');
|
||||
} elseif ($node instanceof PHPParser_Node_Stmt_Const) {
|
||||
foreach ($node->consts as $const) {
|
||||
$const->name = $const->namespacedName->toString('_');
|
||||
}
|
||||
} elseif ($node instanceof PHPParser_Node_Stmt_Namespace) {
|
||||
// returning an array merges is into the parent array
|
||||
return $node->stmts;
|
||||
} elseif ($node instanceof PHPParser_Node_Stmt_Use) {
|
||||
// returning false removed the node altogether
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
That's all.
|
@ -48,6 +48,8 @@ class PHPParser_NodeVisitor_NameResolver extends PHPParser_NodeVisitorAbstract
|
||||
$interface = $this->resolveClassName($interface);
|
||||
}
|
||||
|
||||
$this->addNamespacedName($node);
|
||||
} elseif ($node instanceof PHPParser_Node_Stmt_Trait) {
|
||||
$this->addNamespacedName($node);
|
||||
} elseif ($node instanceof PHPParser_Node_Stmt_Function) {
|
||||
$this->addNamespacedName($node);
|
||||
|
@ -87,13 +87,17 @@ EOC;
|
||||
|
||||
namespace Foo {
|
||||
class A {}
|
||||
function B() {}
|
||||
const C = 'D';
|
||||
interface B {}
|
||||
trait C {}
|
||||
function D() {}
|
||||
const E = 'F';
|
||||
}
|
||||
namespace {
|
||||
class A {}
|
||||
function B() {}
|
||||
const C = 'D';
|
||||
interface B {}
|
||||
trait C {}
|
||||
function D() {}
|
||||
const E = 'F';
|
||||
}
|
||||
EOC;
|
||||
|
||||
@ -106,9 +110,13 @@ EOC;
|
||||
|
||||
$this->assertEquals('Foo\\A', (string) $stmts[0]->stmts[0]->namespacedName);
|
||||
$this->assertEquals('Foo\\B', (string) $stmts[0]->stmts[1]->namespacedName);
|
||||
$this->assertEquals('Foo\\C', (string) $stmts[0]->stmts[2]->consts[0]->namespacedName);
|
||||
$this->assertEquals('Foo\\C', (string) $stmts[0]->stmts[2]->namespacedName);
|
||||
$this->assertEquals('Foo\\D', (string) $stmts[0]->stmts[3]->namespacedName);
|
||||
$this->assertEquals('Foo\\E', (string) $stmts[0]->stmts[4]->consts[0]->namespacedName);
|
||||
$this->assertEquals('A', (string) $stmts[1]->stmts[0]->namespacedName);
|
||||
$this->assertEquals('B', (string) $stmts[1]->stmts[1]->namespacedName);
|
||||
$this->assertEquals('C', (string) $stmts[1]->stmts[2]->consts[0]->namespacedName);
|
||||
$this->assertEquals('C', (string) $stmts[1]->stmts[2]->namespacedName);
|
||||
$this->assertEquals('D', (string) $stmts[1]->stmts[3]->namespacedName);
|
||||
$this->assertEquals('E', (string) $stmts[1]->stmts[4]->consts[0]->namespacedName);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user