From 4daccd5eae0d8a86cef9192a230abcf549056ce6 Mon Sep 17 00:00:00 2001 From: Laurent Laville Date: Fri, 3 Oct 2014 18:13:13 +0200 Subject: [PATCH 1/2] first attempt to implement solution provided by nikic (see https://github.com/nikic/PHP-Parser/issues/136) --- src/Bartlett/Reflect.php | 5 ++- src/Bartlett/Reflect/Builder.php | 39 +++++++++++++++++++ src/Bartlett/Reflect/Model/MethodModel.php | 11 ++++++ src/Bartlett/Reflect/Model/PropertyModel.php | 11 ++++++ .../Reflect/PhpParser/Lexer/TokenOffsets.php | 28 +++++++++++++ 5 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/Bartlett/Reflect/PhpParser/Lexer/TokenOffsets.php diff --git a/src/Bartlett/Reflect.php b/src/Bartlett/Reflect.php index c250d59..a49a306 100644 --- a/src/Bartlett/Reflect.php +++ b/src/Bartlett/Reflect.php @@ -20,6 +20,7 @@ use Bartlett\Reflect\ManagerInterface; use Bartlett\Reflect\ProviderManager; use Bartlett\Reflect\Builder; +use Bartlett\Reflect\PhpParser\Lexer\TokenOffsets; use PhpParser\Parser; use PhpParser\Lexer; @@ -82,7 +83,8 @@ public function parse(array $providers = null) { $this->builder = new Builder; - $parser = new Parser(new Lexer\Emulative); + $lexer = new TokenOffsets(); + $parser = new Parser($lexer); $traverser = new NodeTraverser; $traverser->addVisitor(new NameResolver); $traverser->addVisitor($this->builder); @@ -131,6 +133,7 @@ public function parse(array $providers = null) $stmts = $parser->parse( file_get_contents($file->getPathname()) ); + $this->builder->setTokens($lexer->getTokens()); $stmts = $traverser->traverse($stmts); $this->dispatch( diff --git a/src/Bartlett/Reflect/Builder.php b/src/Bartlett/Reflect/Builder.php index 8da9412..9b8b892 100644 --- a/src/Bartlett/Reflect/Builder.php +++ b/src/Bartlett/Reflect/Builder.php @@ -26,6 +26,8 @@ use PhpParser\NodeVisitorAbstract; use PhpParser\Node; +use PhpParser\Node\Stmt\Property; +use PhpParser\Node\Stmt\ClassMethod; /** * Concrete Builder. @@ -56,6 +58,34 @@ class Builder extends NodeVisitorAbstract */ private $namespace; + private $tokens; + + private function isImplicitlyPublicProperty(array $tokens, Property $prop) + { + $i = $prop->getAttribute('startOffset'); + return isset($tokens[$i]) && $tokens[$i][0] == T_VAR; + } + + private function isImplicitlyPublicFunction(array $tokens, ClassMethod $method) + { + $i = $method->getAttribute('startOffset'); + for ($c = count($tokens); $i < $c; ++$i) { + $t = $tokens[$i]; + if ($t[0] == T_PUBLIC || $t[0] == T_PROTECTED || $t[0] == T_PRIVATE) { + return false; + } + if ($t[0] == T_FUNCTION) { + break; + } + } + return true; + } + + public function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + public function setCurrentFile($path) { $this->file = $path; @@ -312,6 +342,15 @@ public function leaveNode(Node $node) if (is_string($attr = $visibility($stmt))) { $stmtAttributes['visibility'] = $attr; } + if ($stmt instanceof Property) { + $stmtAttributes['implicitlyPublic'] = + $this->isImplicitlyPublicProperty($this->tokens, $stmt); + + } + if ($stmt instanceof ClassMethod) { + $stmtAttributes['implicitlyPublic'] = + $this->isImplicitlyPublicFunction($this->tokens, $stmt); + } if ($stmt instanceof \PhpParser\Node\Stmt\ClassConst) { // @link http://www.php.net/manual/en/language.oop5.constants.php diff --git a/src/Bartlett/Reflect/Model/MethodModel.php b/src/Bartlett/Reflect/Model/MethodModel.php index dd98097..114425b 100644 --- a/src/Bartlett/Reflect/Model/MethodModel.php +++ b/src/Bartlett/Reflect/Model/MethodModel.php @@ -39,6 +39,7 @@ public function __construct($class, $name, $attributes) { $struct = array( 'modifiers' => array(), + 'implicitlyPublic' => true, ); $struct = array_merge($struct, $attributes); parent::__construct($struct); @@ -142,6 +143,16 @@ public function isPublic() return $this->struct['visibility'] === 'public'; } + /** + * Checks if the method is implicitly public (PHP4 syntax). + * + * @return bool + */ + public function isImplicitlyPublic() + { + return $this->struct['implicitlyPublic']; + } + /** * Returns the string representation of the MethodModel object. * diff --git a/src/Bartlett/Reflect/Model/PropertyModel.php b/src/Bartlett/Reflect/Model/PropertyModel.php index 4a85272..a61081d 100644 --- a/src/Bartlett/Reflect/Model/PropertyModel.php +++ b/src/Bartlett/Reflect/Model/PropertyModel.php @@ -42,6 +42,7 @@ public function __construct($class, $name, $attributes) 'compileTime' => true, 'modifiers' => array(), 'visibility' => 'public', + 'implicitlyPublic' => true, ); $struct = array_merge($struct, $attributes); parent::__construct($struct); @@ -143,6 +144,16 @@ public function isStatic() return in_array('static', $this->struct['modifiers']); } + /** + * Checks if the property is implicitly public (PHP4 syntax). + * + * @return bool + */ + public function isImplicitlyPublic() + { + return $this->struct['implicitlyPublic']; + } + /** * Returns the string representation of the PropertyModel object. * diff --git a/src/Bartlett/Reflect/PhpParser/Lexer/TokenOffsets.php b/src/Bartlett/Reflect/PhpParser/Lexer/TokenOffsets.php new file mode 100644 index 0000000..9dfd1b2 --- /dev/null +++ b/src/Bartlett/Reflect/PhpParser/Lexer/TokenOffsets.php @@ -0,0 +1,28 @@ +pos; + return $token; + } + + public function getTokens() { + return $this->tokens; + } +} From b6ad6502d63b25a178a9812c0c8a2a8f10d8da54 Mon Sep 17 00:00:00 2001 From: Laurent Laville Date: Mon, 6 Oct 2014 11:26:51 +0200 Subject: [PATCH 2/2] missing tokens when re-using cached results --- src/Bartlett/Reflect.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Bartlett/Reflect.php b/src/Bartlett/Reflect.php index a49a306..a4071bc 100644 --- a/src/Bartlett/Reflect.php +++ b/src/Bartlett/Reflect.php @@ -23,7 +23,6 @@ use Bartlett\Reflect\PhpParser\Lexer\TokenOffsets; use PhpParser\Parser; -use PhpParser\Lexer; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor\NameResolver; @@ -115,6 +114,10 @@ public function parse(array $providers = null) $this->builder->setCurrentFile($file->getPathname()); if (isset($event['notModified'])) { + $tokens = @token_get_all( + file_get_contents($file->getPathname()) + ); + $this->builder->setTokens($tokens); // uses cached response (AST built by PHP-Parser) $stmts = $traverser->traverse($event['notModified']);