├── .gitignore
├── docs
├── logo.png
└── logo_128px.png
├── generate-docs.sh
├── .travis.yml
├── src
├── Objects
│ ├── TraitNode.php
│ ├── InterfaceStatementNode.php
│ ├── ClassStatementNode.php
│ ├── TraitAdaptationStatementNode.php
│ ├── NameExpressionNode.php
│ ├── ClassNameScalarNode.php
│ ├── InterfaceMethodNode.php
│ ├── ClassMemberLookupNode.php
│ ├── ClassConstantLookupNode.php
│ ├── TraitMethodReferenceNode.php
│ ├── NewNode.php
│ ├── TraitPrecedenceNode.php
│ ├── TraitAliasNode.php
│ ├── TraitUseNode.php
│ ├── ModifiersNode.php
│ ├── VisibilityTrait.php
│ ├── ObjectPropertyNode.php
│ ├── MethodTrait.php
│ ├── ClassMethodCallNode.php
│ └── ObjectMethodCallNode.php
├── Types
│ ├── ArrayElementNode.php
│ ├── HeredocNode.php
│ ├── StringVariableNode.php
│ ├── InterpolatedStringNode.php
│ ├── FloatNode.php
│ ├── IntegerNode.php
│ ├── ScalarNode.php
│ ├── BooleanNode.php
│ ├── NullNode.php
│ ├── TrueNode.php
│ ├── FalseNode.php
│ ├── ArrayPairNode.php
│ └── StringNode.php
├── BlankStatementNode.php
├── Functions
│ ├── EvalNode.php
│ ├── EmptyNode.php
│ ├── IssetNode.php
│ ├── UnsetNode.php
│ ├── HaltCompilerNode.php
│ ├── DefineNode.php
│ ├── ListNode.php
│ ├── LexicalVariableNode.php
│ ├── CallbackCallNode.php
│ ├── FunctionCallNode.php
│ ├── ParameterNodeCollection.php
│ ├── CallNode.php
│ ├── FunctionDeclarationNode.php
│ ├── FunctionTrait.php
│ └── ArgumentTrait.php
├── Operators
│ ├── AddNode.php
│ ├── CloneNode.php
│ ├── FloatCastNode.php
│ ├── PrintNode.php
│ ├── ArrayCastNode.php
│ ├── CastNode.php
│ ├── ConcatNode.php
│ ├── DivideNode.php
│ ├── ElvisNode.php
│ ├── EqualNode.php
│ ├── ObjectCastNode.php
│ ├── PowerNode.php
│ ├── StringCastNode.php
│ ├── UnsetCastNode.php
│ ├── AssignNode.php
│ ├── BooleanCastNode.php
│ ├── IntegerCastNode.php
│ ├── ModulusNode.php
│ ├── AddAssignNode.php
│ ├── BitwiseOrNode.php
│ ├── InstanceOfNode.php
│ ├── LogicalOrNode.php
│ ├── MultiplyNode.php
│ ├── NegateNode.php
│ ├── NotEqualNode.php
│ ├── SubtractNode.php
│ ├── BitwiseAndNode.php
│ ├── BitwiseNotNode.php
│ ├── BitwiseXorNode.php
│ ├── BooleanAndNode.php
│ ├── BooleanOrNode.php
│ ├── ConcatAssignNode.php
│ ├── DivideAssignNode.php
│ ├── IdenticalNode.php
│ ├── LessThanNode.php
│ ├── LogicalAndNode.php
│ ├── LogicalXorNode.php
│ ├── PlusNode.php
│ ├── PowerAssignNode.php
│ ├── ModulusAssignNode.php
│ ├── MultiplyAssignNode.php
│ ├── PreDecrementNode.php
│ ├── PreIncrementNode.php
│ ├── SubtractAssignNode.php
│ ├── BitwiseOrAssignNode.php
│ ├── GreaterThanNode.php
│ ├── NotIdenticalNode.php
│ ├── PostDecrementNode.php
│ ├── PostIncrementNode.php
│ ├── BitwiseAndAssignNode.php
│ ├── BitwiseXorAssignNode.php
│ ├── SuppressWarningNode.php
│ ├── BitwiseShiftLeftNode.php
│ ├── AssignReferenceNode.php
│ ├── BitwiseShiftRightNode.php
│ ├── LessThanOrEqualToNode.php
│ ├── BitwiseShiftLeftAssignNode.php
│ ├── BitwiseShiftRightAssignNode.php
│ ├── GreaterThanOrEqualToNode.php
│ ├── BooleanNotNode.php
│ ├── UnaryOperationNode.php
│ ├── BinaryOperationNode.php
│ └── TernaryOperationNode.php
├── PartialNode.php
├── ControlStructures
│ ├── IncludeNode.php
│ ├── RequireNode.php
│ ├── RequireOnceNode.php
│ ├── IncludeOnceNode.php
│ ├── GotoLabelNode.php
│ ├── GotoStatementNode.php
│ ├── ExitNode.php
│ ├── DefaultNode.php
│ ├── ImportNode.php
│ ├── ContinueStatementNode.php
│ ├── BreakStatementNode.php
│ ├── DeclareDirectiveNode.php
│ ├── CaseNode.php
│ ├── WhileNode.php
│ ├── SwitchNode.php
│ ├── ReturnStatementNode.php
│ ├── DeclareNode.php
│ ├── AltSyntaxTrait.php
│ ├── DoWhileNode.php
│ ├── ElseIfNode.php
│ ├── ForeachNode.php
│ ├── ForNode.php
│ └── IfNode.php
├── BacktickNode.php
├── TemplateNode.php
├── PartialCommentNode.php
├── Generators
│ ├── YieldStatementNode.php
│ └── YieldNode.php
├── HiddenNode.php
├── Variables
│ ├── VariableExpressionNode.php
│ ├── VariableVariableNode.php
│ ├── CompoundVariableNode.php
│ ├── ReferenceVariableNode.php
│ ├── GlobalStatementNode.php
│ ├── StaticVariableNode.php
│ ├── VariableNode.php
│ └── StaticVariableStatementNode.php
├── Constants
│ ├── DirMagicConstantNode.php
│ ├── ClassMagicConstantNode.php
│ ├── FileMagicConstantNode.php
│ ├── LineMagicConstantNode.php
│ ├── TraitMagicConstantNode.php
│ ├── MethodMagicConstantNode.php
│ ├── FunctionMagicConstantNode.php
│ ├── NamespaceMagicConstantNode.php
│ ├── ConstantDeclarationNode.php
│ ├── ConstantDeclarationStatementNode.php
│ └── ConstantNode.php
├── UnsetStatementNode.php
├── ExpressionStatementNode.php
├── Exceptions
│ ├── ThrowStatementNode.php
│ ├── CatchNode.php
│ └── TryCatchNode.php
├── ParenthesisNode.php
├── ParenTrait.php
├── VisitorInterface.php
├── UncommentTrait.php
├── SplatNode.php
├── EchoStatementNode.php
├── EchoTagStatementNode.php
├── Namespaces
│ ├── UseDeclarationBlockNode.php
│ ├── IdentifierNameTrait.php
│ ├── NamespaceNode.php
│ └── UseDeclarationStatementNode.php
├── ParserException.php
├── WhitespaceNode.php
├── ExpressionNode.php
├── FileUtil.php
├── DocCommentTrait.php
├── Operator.php
├── VisitorBase.php
├── Types.php
├── FormatterFactory.php
├── ParentNodeInterface.php
├── RootNode.php
├── StatementNode.php
├── StatementBlockNode.php
├── TokenIterator.php
├── LineCommentBlockNode.php
├── TokenNode.php
├── CommentNode.php
└── ArrayLookupNode.php
├── tests
├── files
│ └── basic.php
├── WhitespaceNodeTest.php
├── TokenNodeTest.php
├── TryCatchNodeTest.php
├── ReturnStatementNodeTest.php
├── FunctionCallNodeTest.php
├── BooleanNotNodeTest.php
├── VariableNodeTest.php
├── StatementBlockNodeTest.php
├── TokenIteratorTest.php
├── DocCommentTraitTest.php
├── StringNodeTest.php
├── LineCommentBlockNodeTest.php
├── ClassMethodNodeTest.php
├── ObjectPropertyNodeTest.php
├── BinaryOperationNodeTest.php
├── DocCommentTest.php
├── BooleanNodeTest.php
├── ObjectMethodCallNodeTest.php
├── NamespaceNodeTest.php
├── IdentifierNameTraitTest.php
├── RootNodeTest.php
├── InterfaceNodeTest.php
├── ClassMemberNodeTest.php
├── ArrayLookupNodeTest.php
├── StatementNodeTest.php
├── UseDeclarationStatementNodeTest.php
├── FunctionDeclarationNodeTest.php
├── ParameterTraitTest.php
├── ArgumentTraitTest.php
└── AnonymousFunctionNodeTest.php
├── config
├── drupal.json
└── psr2.json
├── phpunit.xml.dist
├── bin
└── format.php
├── composer.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | docs/html
3 |
--------------------------------------------------------------------------------
/docs/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grom358/pharborist/HEAD/docs/logo.png
--------------------------------------------------------------------------------
/docs/logo_128px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grom358/pharborist/HEAD/docs/logo_128px.png
--------------------------------------------------------------------------------
/generate-docs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | apigen generate --source src --destination docs/html --access-levels public --charset UTF8 --title Pharborist
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 |
8 | script:
9 | - composer install
10 | - phpunit --coverage-text
11 |
12 |
--------------------------------------------------------------------------------
/src/Objects/TraitNode.php:
--------------------------------------------------------------------------------
1 | ' comparison.
6 | */
7 | class GreaterThanNode extends BinaryOperationNode {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/Operators/NotIdenticalNode.php:
--------------------------------------------------------------------------------
1 | >' operation.
6 | */
7 | class BitwiseShiftRightNode extends BinaryOperationNode {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/Functions/ListNode.php:
--------------------------------------------------------------------------------
1 | >=' assignment.
6 | */
7 | class BitwiseShiftRightAssignNode extends BinaryOperationNode {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/Objects/ClassStatementNode.php:
--------------------------------------------------------------------------------
1 | =' comparison.
6 | */
7 | class GreaterThanOrEqualToNode extends BinaryOperationNode {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/HiddenNode.php:
--------------------------------------------------------------------------------
1 | assertEquals(4, $node->getNewlineCount());
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Constants/FunctionMagicConstantNode.php:
--------------------------------------------------------------------------------
1 | assertEquals('T_STRING', $id->getTypeName());
8 |
9 | $comma = new TokenNode(',', ',');
10 | $this->assertEquals(',', $comma->getTypeName());
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Types/FloatNode.php:
--------------------------------------------------------------------------------
1 | getText();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Types/IntegerNode.php:
--------------------------------------------------------------------------------
1 | getText();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/UnsetStatementNode.php:
--------------------------------------------------------------------------------
1 | functionCall;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/ControlStructures/GotoLabelNode.php:
--------------------------------------------------------------------------------
1 | label;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/ControlStructures/GotoStatementNode.php:
--------------------------------------------------------------------------------
1 | label;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/ExpressionStatementNode.php:
--------------------------------------------------------------------------------
1 | expression;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/ControlStructures/ExitNode.php:
--------------------------------------------------------------------------------
1 | expression;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Exceptions/ThrowStatementNode.php:
--------------------------------------------------------------------------------
1 | expression;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/ParenthesisNode.php:
--------------------------------------------------------------------------------
1 | expression;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/TryCatchNodeTest.php:
--------------------------------------------------------------------------------
1 | assertTrue($tryCatch->catches('\InvalidArgumentException'));
10 | $this->assertFalse($tryCatch->catches('\DomainException'));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | ./tests
9 |
10 |
11 |
12 |
13 |
14 | ./vendor
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Functions/CallbackCallNode.php:
--------------------------------------------------------------------------------
1 | callback;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/ControlStructures/DefaultNode.php:
--------------------------------------------------------------------------------
1 | body;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/ReturnStatementNodeTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('return TRUE;', $ret->getText());
12 | $this->assertInstanceOf('\Pharborist\Types\TrueNode', $ret->getExpression());
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/ParenTrait.php:
--------------------------------------------------------------------------------
1 | openParen;
20 | }
21 |
22 | /**
23 | * @return TokenNode
24 | */
25 | public function getCloseParen() {
26 | return $this->closeParen;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Variables/VariableVariableNode.php:
--------------------------------------------------------------------------------
1 | variable;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/FunctionCallNodeTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('test', $function_call->getName()->getText());
9 | $function_call->setName('hello');
10 | $this->assertEquals('hello', $function_call->getName()->getText());
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/VisitorInterface.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf('\Pharborist\Operators\BooleanNotNode', $not);
11 | $this->assertSame($expr, $not->getOperand());
12 | $this->assertEquals('!empty($foo)', $not->getText());
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/ControlStructures/ImportNode.php:
--------------------------------------------------------------------------------
1 | expression;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/UncommentTrait.php:
--------------------------------------------------------------------------------
1 | getCommentText());
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Objects/ClassNameScalarNode.php:
--------------------------------------------------------------------------------
1 | className;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Variables/CompoundVariableNode.php:
--------------------------------------------------------------------------------
1 | expression;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Constants/ConstantDeclarationNode.php:
--------------------------------------------------------------------------------
1 | value;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/bin/format.php:
--------------------------------------------------------------------------------
1 | format($tree);
12 | file_put_contents($filename, $tree->getText());
13 | }
14 |
15 | $directory = $argv[1];
16 |
17 | $files = FileUtil::findFiles($directory);
18 | foreach ($files as $filename) {
19 | formatFile($filename);
20 | }
21 |
--------------------------------------------------------------------------------
/src/SplatNode.php:
--------------------------------------------------------------------------------
1 | expression;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Types/ScalarNode.php:
--------------------------------------------------------------------------------
1 | variable;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/EchoStatementNode.php:
--------------------------------------------------------------------------------
1 | expressions;
18 | }
19 |
20 | /**
21 | * Return the expressions being echoed.
22 | *
23 | * @return NodeCollection|ExpressionNode[]
24 | */
25 | public function getExpressions() {
26 | return $this->expressions->getItems();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/EchoTagStatementNode.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 | class EchoTagStatementNode extends StatementNode {
10 | /**
11 | * @var CommaListNode
12 | */
13 | protected $expressions;
14 |
15 | /**
16 | * @return CommaListNode
17 | */
18 | public function getExpressionList() {
19 | return $this->expressions;
20 | }
21 |
22 | /**
23 | * @return NodeCollection|ExpressionNode[]
24 | */
25 | public function getExpressions() {
26 | return $this->expressions->getItems();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Namespaces/UseDeclarationBlockNode.php:
--------------------------------------------------------------------------------
1 | childrenByInstance('\Pharborist\Namespaces\UseDeclarationStatementNode');
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/ParserException.php:
--------------------------------------------------------------------------------
1 | assertEquals('form', $var->getName());
11 | }
12 |
13 | /**
14 | * @depends testGetName
15 | */
16 | public function testSetName() {
17 | $var = new VariableNode(T_VARIABLE, '$x');
18 | $var->setName('$y');
19 | $this->assertEquals('y', $var->getName());
20 | $var->setName('z');
21 | $this->assertEquals('z', $var->getName());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "grom358/pharborist",
3 | "description": "Pharborist builds a syntax tree for PHP that can be traversed and manipulated.",
4 | "type": "library",
5 | "keywords": [
6 | "standards",
7 | "syntax"
8 | ],
9 | "license": "GPL",
10 | "authors": [
11 | {
12 | "name": "Cameron Zemek",
13 | "role": "lead"
14 | }
15 | ],
16 | "autoload": {
17 | "psr-4" : {"Pharborist\\": "src/"}
18 | },
19 | "require": {
20 | "php": ">=5.4",
21 | "phpdocumentor/reflection-docblock": "2.0.*"
22 | },
23 | "require-dev": {
24 | "phpunit/phpunit": "4.2.*",
25 | "apigen/apigen": "2.8.*"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Generators/YieldNode.php:
--------------------------------------------------------------------------------
1 | key;
27 | }
28 |
29 | /**
30 | * @return Node
31 | */
32 | public function getValue() {
33 | return $this->value;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/ControlStructures/ContinueStatementNode.php:
--------------------------------------------------------------------------------
1 | level;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/ControlStructures/BreakStatementNode.php:
--------------------------------------------------------------------------------
1 | level;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/ControlStructures/DeclareDirectiveNode.php:
--------------------------------------------------------------------------------
1 | name;
27 | }
28 |
29 | /**
30 | * @return ExpressionNode
31 | */
32 | public function getValue() {
33 | return $this->value;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Variables/GlobalStatementNode.php:
--------------------------------------------------------------------------------
1 | variables;
22 | }
23 |
24 | /**
25 | * @return NodeCollection|VariableExpressionNode[]
26 | */
27 | public function getVariables() {
28 | return $this->variables->getItems();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Objects/InterfaceMethodNode.php:
--------------------------------------------------------------------------------
1 | getStatements()[0]->remove();
21 | return $method_node;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tests/StatementBlockNodeTest.php:
--------------------------------------------------------------------------------
1 | getThen();
10 | $this->assertInstanceOf('\Pharborist\StatementBlockNode', $statementBlock);
11 | $this->assertEquals('{ hello(); }', $statementBlock->getText());
12 | $statementBlock->appendStatement(Parser::parseSnippet('world();'));
13 | $this->assertEquals('{ hello(); world();}', $statementBlock->getText());
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/ControlStructures/CaseNode.php:
--------------------------------------------------------------------------------
1 | matchOn;
27 | }
28 |
29 | /**
30 | * @return StatementBlockNode
31 | */
32 | public function getBody() {
33 | return $this->body;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Variables/StaticVariableNode.php:
--------------------------------------------------------------------------------
1 | name;
29 | }
30 |
31 | /**
32 | * @return Node
33 | */
34 | public function getInitialValue() {
35 | return $this->initialValue;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/ControlStructures/WhileNode.php:
--------------------------------------------------------------------------------
1 | condition;
31 | }
32 |
33 | /**
34 | * @return Node
35 | */
36 | public function getBody() {
37 | return $this->body;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Objects/ClassMemberLookupNode.php:
--------------------------------------------------------------------------------
1 | className;
29 | }
30 |
31 | /**
32 | * @return Node
33 | */
34 | public function getMemberName() {
35 | return $this->memberName;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Variables/VariableNode.php:
--------------------------------------------------------------------------------
1 | getText(), '$');
19 | }
20 |
21 | /**
22 | * @param string $name
23 | * The name of the parameter, with or without the leading $.
24 | *
25 | * @return $this
26 | */
27 | public function setName($name) {
28 | return $this->setText('$' . ltrim($name, '$'));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Objects/ClassConstantLookupNode.php:
--------------------------------------------------------------------------------
1 | className;
28 | }
29 |
30 | /**
31 | * @return TokenNode
32 | */
33 | public function getConstantName() {
34 | return $this->constantName;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Objects/TraitMethodReferenceNode.php:
--------------------------------------------------------------------------------
1 | traitName;
27 | }
28 |
29 | /**
30 | * @return Node
31 | */
32 | public function getMethodReference() {
33 | return $this->methodReference;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Operators/BooleanNotNode.php:
--------------------------------------------------------------------------------
1 | addChild(Token::not(), 'operator');
24 | /** @var \Pharborist\Node $expr */
25 | $not->addChild($expr->remove(), 'operand');
26 | return $not;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/ControlStructures/SwitchNode.php:
--------------------------------------------------------------------------------
1 | switchOn;
26 | }
27 |
28 | /**
29 | * @return NodeCollection|CaseNode[]
30 | */
31 | public function getCases() {
32 | return new NodeCollection($this->childrenByInstance('\Pharborist\StatementNode'), FALSE);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Objects/NewNode.php:
--------------------------------------------------------------------------------
1 | className;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Functions/FunctionCallNode.php:
--------------------------------------------------------------------------------
1 | name;
21 | }
22 |
23 | /**
24 | * @param string|\Pharborist\Namespaces\NameNode $name
25 | * @return $this
26 | */
27 | public function setName($name) {
28 | if (is_string($name)) {
29 | $name = NameNode::create($name);
30 | }
31 | $this->name->replaceWith($name);
32 | $this->name = $name;
33 | return $this;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Variables/StaticVariableStatementNode.php:
--------------------------------------------------------------------------------
1 | variables;
27 | }
28 |
29 | /**
30 | * @return NodeCollection|StaticVariableNode[]
31 | */
32 | public function getVariables() {
33 | return $this->variables->getItems();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/ControlStructures/ReturnStatementNode.php:
--------------------------------------------------------------------------------
1 | getText() . ';');
27 | }
28 |
29 | /**
30 | * @return ExpressionNode
31 | */
32 | public function getExpression() {
33 | return $this->expression;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Types/BooleanNode.php:
--------------------------------------------------------------------------------
1 | getConfig('nl');
12 | $this->newlineCount = substr_count($this->getText(), $nl);
13 | }
14 | }
15 |
16 | public static function create($whitespace) {
17 | return new WhitespaceNode(T_WHITESPACE, $whitespace);
18 | }
19 |
20 | public function setText($text) {
21 | $nl = FormatterFactory::getDefaultFormatter()->getConfig('nl');
22 | $this->newlineCount = substr_count($text, $nl);
23 | return parent::setText($text);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Objects/TraitPrecedenceNode.php:
--------------------------------------------------------------------------------
1 | traitMethodReference;
29 | }
30 |
31 | /**
32 | * @return NodeCollection|NameNode[]
33 | */
34 | public function getTraitNames() {
35 | return $this->traitNames->getItems();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Types/NullNode.php:
--------------------------------------------------------------------------------
1 | getConfig('boolean_null_upper');
22 | $node = new NullNode();
23 | $node->addChild(NameNode::create($is_upper ? 'NULL' : 'null'), 'constantName');
24 | return $node;
25 | }
26 |
27 | /**
28 | * @return null
29 | */
30 | public function toValue() {
31 | return NULL;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Constants/ConstantDeclarationStatementNode.php:
--------------------------------------------------------------------------------
1 | declarations;
25 | }
26 |
27 | /**
28 | * @return NodeCollection|ConstantDeclarationNode[]
29 | */
30 | public function getDeclarations() {
31 | return $this->declarations->getItems();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tests/TokenIteratorTest.php:
--------------------------------------------------------------------------------
1 | peek(0);
9 | $this->assertSame($test, $peek);
10 | $this->assertNull($iterator->peek(1));
11 |
12 | $this->assertFalse($iterator->hasNext());
13 | $this->assertNull($iterator->next());
14 | $this->assertEquals(1, $iterator->getLineNumber());
15 | $this->assertEquals(5, $iterator->getColumnNumber());
16 | }
17 |
18 | public function testEmpty() {
19 | $iterator = new TokenIterator([]);
20 | $this->assertNull($iterator->peek(0));
21 | $this->assertEquals(1, $iterator->getLineNumber());
22 | $this->assertEquals(1, $iterator->getColumnNumber());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/DocCommentTraitTest.php:
--------------------------------------------------------------------------------
1 | setDocComment(DocCommentNode::create('Ni!'));
11 | $comment = $node->getDocComment();
12 | $this->assertInstanceOf('\Pharborist\DocCommentNode', $comment);
13 | $this->assertEquals('Ni!', $comment->getCommentText());
14 | }
15 |
16 | public function testChangeDocComment() {
17 | $node = ClassNode::create('Foo');
18 | $node->setDocComment(DocCommentNode::create('Ni!'));
19 | $node->setDocComment(DocCommentNode::create('Noo!'));
20 | $comment = $node->getDocComment();
21 | $this->assertInstanceOf('\Pharborist\DocCommentNode', $comment);
22 | $this->assertEquals('Noo!', $comment->getCommentText());
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/tests/StringNodeTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('Goodbye, cruel world!', $string->toValue());
11 |
12 | $string = StringNode::create('"I\'ll harrr to that!"');
13 | $this->assertEquals("I'll harrr to that!", $string->toValue());
14 |
15 | // Test escaped characters in double quoted string.
16 | $string = <<<'EOF'
17 | "h\145llo\\n\nw\x6Frld"
18 | EOF;
19 | $this->assertEquals("hello\\n\nworld", StringNode::create($string)->toValue());
20 |
21 | // Test escaped characters in single quoted string.
22 | $string = <<<'EOF'
23 | 'it\'s \a\\\'live'
24 | EOF;
25 | $this->assertEquals("it's \\a\\'live", StringNode::create($string)->toValue());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/LineCommentBlockNodeTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($line_comment_text, $comment_node->getText());
18 |
19 | $comment_node->addIndent(' ');
20 | $expected = <<<'EOF'
21 | // hello
22 | // world
23 |
24 | EOF;
25 | $this->assertEquals($expected, $comment_node->getText());
26 |
27 | $comment_node->addIndent(' ');
28 | $expected = <<<'EOF'
29 | // hello
30 | // world
31 |
32 | EOF;
33 | $this->assertEquals($expected, $comment_node->getText());
34 |
35 | $comment_node->removeIndent();
36 | $this->assertEquals($line_comment_text, $comment_node->getText());
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/ControlStructures/DeclareNode.php:
--------------------------------------------------------------------------------
1 | directives;
31 | }
32 |
33 | /**
34 | * @return NodeCollection|DeclareDirectiveNode[]
35 | */
36 | public function getDirectives() {
37 | return $this->directives->getItems();
38 | }
39 |
40 | /**
41 | * @return Node
42 | */
43 | public function getBody() {
44 | return $this->body;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/ClassMethodNodeTest.php:
--------------------------------------------------------------------------------
1 | getMethod('baz');
10 | $this->assertEquals('\Foo::baz', $method->getFullyQualifiedName());
11 | }
12 |
13 | public function testSetName() {
14 | /** @var \Pharborist\Objects\ClassNode $class */
15 | $class = Parser::parseSnippet('class Foo { public function baz() {} }');
16 | /** @var \Pharborist\Objects\ClassMethodNode $method */
17 | $method = $class->getMethod('baz');
18 | $method->setName('bar');
19 | $this->assertEquals('bar', $method->getName()->getText());
20 | $this->assertEquals('public function bar() {}', $method->getText());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Objects/TraitAliasNode.php:
--------------------------------------------------------------------------------
1 | traitMethodReference;
31 | }
32 |
33 | /**
34 | * @return TokenNode
35 | */
36 | public function getVisibility() {
37 | return $this->visibility;
38 | }
39 |
40 | /**
41 | * @return TokenNode
42 | */
43 | public function getAlias() {
44 | return $this->alias;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/ExpressionNode.php:
--------------------------------------------------------------------------------
1 | baz();`
16 | * - Logical expressions: `($a && $b)`
17 | * - Function call arguments: `foo(--$baz)`
18 | * - Comparisons: `$a > $b`
19 | *
20 | * Expressions are "smaller" than statements, in the sense that a statement
21 | * is usually composed of least one expression. Expressions can contain other
22 | * expressions -- a nested function call, for instance. Any node that implements
23 | * this interface is considered by PHP to be an expression.
24 | */
25 | interface ExpressionNode extends NodeInterface, ArrayElementNode {
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/Types/TrueNode.php:
--------------------------------------------------------------------------------
1 | getConfig('boolean_null_upper');
24 | $node = new TrueNode();
25 | $node->addChild(NameNode::create($is_upper ? 'TRUE' : 'true'), 'constantName');
26 | return $node;
27 | }
28 |
29 | /**
30 | * Gets the boolean value of the node.
31 | *
32 | * @return boolean
33 | */
34 | public function toValue() {
35 | return TRUE;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Operators/UnaryOperationNode.php:
--------------------------------------------------------------------------------
1 | operator;
28 | }
29 |
30 | /**
31 | * @return ExpressionNode
32 | */
33 | public function getOperand() {
34 | return $this->operand;
35 | }
36 |
37 | /**
38 | * @param ExpressionNode $operand
39 | * @return $this
40 | */
41 | public function setOperand(ExpressionNode $operand) {
42 | /** @var \Pharborist\Node $operand */
43 | $this->operand->replaceWith($operand);
44 | return $this;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/FileUtil.php:
--------------------------------------------------------------------------------
1 | $object) {
26 | $files[] = $name;
27 | }
28 | return $files;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/Types/FalseNode.php:
--------------------------------------------------------------------------------
1 | getConfig('boolean_null_upper');
24 | $node = new FalseNode();
25 | $node->addChild(NameNode::create($is_upper ? 'FALSE' : 'false'), 'constantName');
26 | return $node;
27 | }
28 |
29 | /**
30 | * Gets the boolean value of the node.
31 | *
32 | * @return boolean
33 | */
34 | public function toValue() {
35 | return FALSE;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/ControlStructures/AltSyntaxTrait.php:
--------------------------------------------------------------------------------
1 | openColon;
27 | }
28 |
29 | /**
30 | * The end keyword delimiter for end of control structure.
31 | *
32 | * @return TokenNode
33 | */
34 | public function getEndKeyword() {
35 | return $this->endKeyword;
36 | }
37 |
38 | /**
39 | * Return if control structure is using the altnerative syntax.
40 | *
41 | * @return bool
42 | */
43 | public function isAlterativeSyntax() {
44 | return $this->endKeyword !== NULL;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/ControlStructures/DoWhileNode.php:
--------------------------------------------------------------------------------
1 | body;
36 | }
37 |
38 | /**
39 | * @return ExpressionNode
40 | */
41 | public function getCondition() {
42 | return $this->condition;
43 | }
44 |
45 | /**
46 | * The T_WHILE keyword token.
47 | *
48 | * @return TokenNode
49 | */
50 | public function getWhileKeyword() {
51 | return $this->whileKeyword;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/ControlStructures/ElseIfNode.php:
--------------------------------------------------------------------------------
1 | condition;
36 | }
37 |
38 | /**
39 | * The colon (':') delimiter for body of statements.
40 | *
41 | * @return TokenNode
42 | */
43 | public function getOpenColon() {
44 | return $this->openColon;
45 | }
46 |
47 | /**
48 | * @return Node
49 | */
50 | public function getThen() {
51 | return $this->then;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Objects/TraitUseNode.php:
--------------------------------------------------------------------------------
1 | traits->getItems();
28 | }
29 |
30 | /**
31 | * @return StatementBlockNode
32 | */
33 | public function getAdaptationBlock() {
34 | return $this->adaptations;
35 | }
36 |
37 | /**
38 | * @return TraitAdaptationStatementNode[]
39 | */
40 | public function getAdaptations() {
41 | if ($this->adaptations === NULL) {
42 | return new NodeCollection();
43 | }
44 | return $this->adaptations->getStatements();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Objects/ModifiersNode.php:
--------------------------------------------------------------------------------
1 | abstract;
38 | }
39 |
40 | /**
41 | * @return TokenNode
42 | */
43 | public function getFinal() {
44 | return $this->final;
45 | }
46 |
47 | /**
48 | * @return TokenNode
49 | */
50 | public function getStatic() {
51 | return $this->static;
52 | }
53 |
54 | /**
55 | * @return TokenNode
56 | */
57 | public function getVisibility() {
58 | return $this->visibility;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/ControlStructures/ForeachNode.php:
--------------------------------------------------------------------------------
1 | onEach;
40 | }
41 |
42 | /**
43 | * @return Node
44 | */
45 | public function getKey() {
46 | return $this->key;
47 | }
48 |
49 | /**
50 | * @return Node
51 | */
52 | public function getValue() {
53 | return $this->value;
54 | }
55 |
56 | /**
57 | * @return Node
58 | */
59 | public function getBody() {
60 | return $this->body;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Exceptions/CatchNode.php:
--------------------------------------------------------------------------------
1 | exceptionType;
36 | }
37 |
38 | /**
39 | * Returns the variable for the caught exception.
40 | *
41 | * @return VariableNode
42 | */
43 | public function getVariable() {
44 | return $this->variable;
45 | }
46 |
47 | /**
48 | * @return StatementBlockNode
49 | */
50 | public function getBody() {
51 | return $this->body;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/ObjectPropertyNodeTest.php:
--------------------------------------------------------------------------------
1 | bar->baz');
10 | $this->assertInstanceOf('\Pharborist\Objects\ObjectPropertyNode', $property);
11 | $this->assertEquals('bar', $property->getRootProperty()->getText());
12 |
13 | $property = Parser::parseExpression('$foo->$bar');
14 | $this->assertInstanceOf('\Pharborist\Objects\ObjectPropertyNode', $property);
15 | $this->assertEquals('$bar', $property->getRootProperty()->getText());
16 | }
17 |
18 | /**
19 | * @depends testGetRootProperty
20 | */
21 | public function testGetPropertyName() {
22 | /** @var ObjectPropertyNode $property */
23 | $property = Parser::parseExpression('$node->field_foo');
24 | $this->assertEquals('field_foo', $property->getPropertyName());
25 |
26 | $property = Parser::parseExpression('$node->$field["value"]');
27 | $this->assertNull($property->getPropertyName());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Types/ArrayPairNode.php:
--------------------------------------------------------------------------------
1 | key;
27 | }
28 |
29 | /**
30 | * @return Node
31 | */
32 | public function getValue() {
33 | return $this->value;
34 | }
35 |
36 | /**
37 | * @param Node $key
38 | * Array element's key.
39 | * @param Node $value
40 | * Array element's value.
41 | *
42 | * @return ArrayPairNode
43 | */
44 | public static function create($key, $value) {
45 | $node = new ArrayPairNode();
46 | $node->addChild($key, 'key');
47 | $node->addChild(Token::space());
48 | $node->addChild(Token::doubleArrow());
49 | $node->addChild(Token::space());
50 | $node->addChild($value, 'value');
51 | return $node;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/ControlStructures/ForNode.php:
--------------------------------------------------------------------------------
1 | initial;
41 | }
42 |
43 | /**
44 | * @return CommaListNode
45 | */
46 | public function getCondition() {
47 | return $this->condition;
48 | }
49 |
50 | /**
51 | * @return CommaListNode
52 | */
53 | public function getStep() {
54 | return $this->step;
55 | }
56 |
57 | /**
58 | * @return Node
59 | */
60 | public function getBody() {
61 | return $this->body;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/BinaryOperationNodeTest.php:
--------------------------------------------------------------------------------
1 | op = Parser::parseSnippet('$doAliensExist = TRUE;')->firstChild();
12 | }
13 |
14 | public function testInstanceOf() {
15 | $this->assertInstanceOf('Pharborist\Operators\AssignNode', $this->op);
16 | }
17 |
18 | public function testGetLeftOperand() {
19 | $this->assertInstanceOf('Pharborist\Variables\VariableNode', $this->op->getLeftOperand());
20 | $this->assertEquals('doAliensExist', $this->op->getLeftOperand()->getName());
21 | }
22 |
23 | public function testGetOperator() {
24 | $this->assertInstanceOf('Pharborist\TokenNode', $this->op->getOperator());
25 | $this->assertEquals('=', $this->op->getOperator()->getText());
26 | }
27 |
28 | public function testGetRightOperand() {
29 | $this->assertInstanceOf('Pharborist\Constants\ConstantNode', $this->op->getRightOperand());
30 | $this->assertEquals('TRUE', $this->op->getRightOperand()->getText());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/DocCommentTest.php:
--------------------------------------------------------------------------------
1 | getStatements()[0];
26 | /** @var \Pharborist\Objects\InterfaceMethodNode $method */
27 | $method = $interface->getStatements()[0];
28 | $comment = DocCommentNode::create('Test');
29 | $method->setDocComment($comment);
30 | $this->assertEquals($expected, $tree->getText());
31 | }
32 |
33 | public function testClassMethodNode() {
34 | $method = ClassMethodNode::create('foo');
35 | $method->setDocComment(DocCommentNode::create('{@inheritdoc}'));
36 | $expected = <<<'END'
37 | /**
38 | * {@inheritdoc}
39 | */
40 | public function foo() {}
41 | END;
42 | $this->assertEquals($expected, $method->getText());
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/BooleanNodeTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('TRUE', $true->getText());
10 |
11 | $true = BooleanNode::create(FALSE);
12 | $this->assertEquals('FALSE', $true->getText());
13 | }
14 |
15 | public function testToUpper() {
16 | $default_formatter = FormatterFactory::getDefaultFormatter();
17 | $formatter = new Formatter(['boolean_null_upper' => FALSE]);
18 | FormatterFactory::setDefaultFormatter($formatter);
19 | $true = BooleanNode::create(TRUE);
20 | $this->assertEquals('true', $true->getText());
21 | $this->assertEquals('TRUE', $true->toUpperCase()->getText());
22 | FormatterFactory::setDefaultFormatter($default_formatter);
23 | }
24 |
25 | public function testToLower() {
26 | $default_formatter = FormatterFactory::getDefaultFormatter();
27 | $formatter = new Formatter(['boolean_null_upper' => TRUE]);
28 | FormatterFactory::setDefaultFormatter($formatter);
29 | $true = BooleanNode::create(TRUE);
30 | $this->assertEquals('TRUE', $true->getText());
31 | $this->assertEquals('true', $true->toLowerCase()->getText());
32 | FormatterFactory::setDefaultFormatter($default_formatter);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Exceptions/TryCatchNode.php:
--------------------------------------------------------------------------------
1 | try;
26 | }
27 |
28 | /**
29 | * @return CatchNode[]
30 | */
31 | public function getCatches() {
32 | return $this->childrenByInstance('\Pharborist\Exceptions\CatchNode');
33 | }
34 |
35 | /**
36 | * @return Node
37 | */
38 | public function getFinally() {
39 | return $this->finally;
40 | }
41 |
42 | /**
43 | * Returns if this try/catch has a catch for a certain exception type.
44 | *
45 | * @param string $exception ...
46 | * At least one exception type to check for. Each should be a fully qualified
47 | * name, e.g. `\Exception` instead of `Exception`.
48 | *
49 | * @return boolean
50 | */
51 | public function catches($exception) {
52 | $exceptions = func_get_args();
53 |
54 | foreach ($this->getCatches() as $catch) {
55 | if (in_array($catch->getExceptionType()->getAbsolutePath(), $exceptions, TRUE)) {
56 | return TRUE;
57 | }
58 | }
59 | return FALSE;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Functions/ParameterNodeCollection.php:
--------------------------------------------------------------------------------
1 | nodes) as $node) {
18 | if ($node instanceof ParameterNode) {
19 | if ($node->getName() === $offset) {
20 | return TRUE;
21 | }
22 | }
23 | }
24 | return FALSE;
25 | }
26 | return isset($this->nodes[$offset]);
27 | }
28 |
29 | /**
30 | * Implements \ArrayAccess::offsetGet().
31 | *
32 | * @param integer $offset
33 | *
34 | * @return ParameterNode
35 | */
36 | public function offsetGet($offset) {
37 | if (is_string($offset)) {
38 | // To deal with php allowing function test($a, $a) loop in reverse.
39 | foreach (array_reverse($this->nodes) as $node) {
40 | if ($node instanceof ParameterNode) {
41 | if ($node->getName() === $offset) {
42 | return $node;
43 | }
44 | }
45 | }
46 | return NULL;
47 | }
48 | return parent::offsetGet($offset);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Operators/BinaryOperationNode.php:
--------------------------------------------------------------------------------
1 | left;
32 | }
33 |
34 | /**
35 | * @param ExpressionNode $operand
36 | * @return $this
37 | */
38 | public function setLeftOperand(ExpressionNode $operand) {
39 | /** @var Node $operand */
40 | $this->left->replaceWith($operand);
41 | return $this;
42 | }
43 |
44 | /**
45 | * @return Node
46 | */
47 | public function getOperator() {
48 | return $this->operator;
49 | }
50 |
51 | /**
52 | * @return ExpressionNode
53 | */
54 | public function getRightOperand() {
55 | return $this->right;
56 | }
57 |
58 | /**
59 | * @param ExpressionNode $operand
60 | * @return $this
61 | */
62 | public function setRightOperand(ExpressionNode $operand) {
63 | /** @var Node $operand */
64 | $this->right->replaceWith($operand);
65 | return $this;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/DocCommentTrait.php:
--------------------------------------------------------------------------------
1 | previousToken();
18 | if (empty($whitespace_token) || $whitespace_token->getType() !== T_WHITESPACE) {
19 | return '';
20 | }
21 | $nl = FormatterFactory::getDefaultFormatter()->getConfig('nl');
22 | $lines = explode($nl, $whitespace_token->getText());
23 | $last_line = end($lines);
24 | return $last_line;
25 | }
26 |
27 | /**
28 | * @return DocCommentNode
29 | */
30 | public function getDocComment() {
31 | return $this->docComment;
32 | }
33 |
34 | /**
35 | * @param DocCommentNode $comment
36 | * @return $this
37 | */
38 | public function setDocComment(DocCommentNode $comment) {
39 | if (isset($this->docComment)) {
40 | $this->docComment->remove();
41 | }
42 |
43 | $indent = $this->getIndent();
44 | $comment->setIndent($indent);
45 | $nl = FormatterFactory::getDefaultFormatter()->getConfig('nl');
46 | /** @var ParentNode $this */
47 | $this->firstChild()->before([
48 | $comment,
49 | WhitespaceNode::create($nl . $indent),
50 | ]);
51 | $this->docComment = $comment;
52 |
53 | return $this;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Operator.php:
--------------------------------------------------------------------------------
1 | operator;
28 | }
29 |
30 | /**
31 | * Colon node. Only used by ternary operator.
32 | * @var PartialNode
33 | */
34 | public $colon;
35 |
36 | /**
37 | * Then node. Only used by ternary operator.
38 | */
39 | public $then;
40 |
41 | /**
42 | * @var int
43 | */
44 | public $mode;
45 |
46 | /**
47 | * @var int
48 | */
49 | public $precedence;
50 |
51 | /**
52 | * @var int
53 | */
54 | public $associativity;
55 |
56 | /**
57 | * @var int
58 | */
59 | public $type;
60 |
61 | /**
62 | * @var bool
63 | */
64 | public $hasBinaryMode;
65 |
66 | /**
67 | * @var bool
68 | */
69 | public $hasUnaryMode;
70 |
71 | /**
72 | * Node class to create if unary operator.
73 | * @var string
74 | */
75 | public $unaryClassName;
76 |
77 | /**
78 | * Node class to create if binary operator.
79 | * @var string
80 | */
81 | public $binaryClassName;
82 | }
83 |
--------------------------------------------------------------------------------
/tests/ObjectMethodCallNodeTest.php:
--------------------------------------------------------------------------------
1 | call = Parser::parseSnippet('$mulder->scully();')->firstChild();
12 | }
13 |
14 | public function testGetObject() {
15 | $this->assertInstanceOf('Pharborist\Variables\VariableNode', $this->call->getObject());
16 | $this->assertEquals('mulder', $this->call->getObject()->getName());
17 | }
18 |
19 | public function testGetMethodName() {
20 | $this->assertEquals('scully', $this->call->getMethodName());
21 | }
22 |
23 | public function testSetMethodName() {
24 | $this->call->setMethodName('skinner');
25 | $this->assertEquals('skinner', $this->call->getMethodName());
26 | $this->assertEquals('$mulder->skinner()', $this->call->getText());
27 | }
28 |
29 | public function testGetPreviousCall() {
30 | $call = Parser::parseSnippet('\Drupal::database()->insert("razmatazz");')->firstChild();
31 | $this->assertInstanceOf('\Pharborist\Objects\ObjectMethodCallNode', $call);
32 | $this->assertInstanceOf('\Pharborist\Objects\ClassMethodCallNode', $call->getPreviousCall());
33 |
34 | $call = Parser::parseSnippet('$raz->matazz();')->firstChild();
35 | $this->assertInstanceOf('\Pharborist\Objects\ObjectMethodCallNode', $call);
36 | $this->assertNull($call->getPreviousCall());
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/NamespaceNodeTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('\Top\Sub', $namespace_node->getName()->getAbsolutePath());
11 | $this->assertNotNull($namespace_node->getBody());
12 | }
13 |
14 | public function testUseDeclarations() {
15 | $snippet = <<<'EOF'
16 | namespace Test {
17 | use const Other\MY_CONST;
18 | use function Other\my_func;
19 | use Other\MyClass;
20 | use Other\OtherClass as Bind;
21 | class TestClass {}
22 | }
23 | EOF;
24 | /** @var NamespaceNode $namespace */
25 | $namespace = Parser::parseSnippet($snippet);
26 |
27 | $declarations = $namespace->getUseDeclarations();
28 | $this->assertCount(4, $declarations);
29 |
30 | $aliases = $namespace->getClassAliases();
31 | $this->assertCount(2, $aliases);
32 | $this->assertArrayHasKey('MyClass', $aliases);
33 | $this->assertEquals('\Other\MyClass', $aliases['MyClass']);
34 | $this->assertArrayHasKey('Bind', $aliases);
35 | $this->assertEquals('\Other\OtherClass', $aliases['Bind']);
36 |
37 | $class_node = $namespace->find(Filter::isClass('TestClass'))[0];
38 | $this->assertTrue($namespace->owns($class_node));
39 | $class_node = ClassNode::create('Dummy');
40 | $this->assertFalse($namespace->owns($class_node));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Constants/ConstantNode.php:
--------------------------------------------------------------------------------
1 | addChild($name, 'constantName');
24 | return $node;
25 | }
26 |
27 | /**
28 | * @var \Pharborist\Namespaces\NameNode
29 | */
30 | protected $constantName;
31 |
32 | /**
33 | * @return \Pharborist\Namespaces\NameNode
34 | */
35 | public function getConstantName() {
36 | return $this->constantName;
37 | }
38 |
39 | /**
40 | * Convert the constant into uppercase.
41 | *
42 | * @return $this
43 | */
44 | public function toUpperCase() {
45 | $token = $this->getConstantName()->lastToken();
46 | $token->setText(strtoupper($token->getText()));
47 | return $this;
48 | }
49 |
50 | /**
51 | * Convert the constant into lowercase.
52 | *
53 | * @return $this
54 | */
55 | public function toLowerCase() {
56 | $token = $this->getConstantName()->lastToken();
57 | $token->setText(strtolower($token->getText()));
58 | return $this;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/VisitorBase.php:
--------------------------------------------------------------------------------
1 | methodCache[$node_class_name][$prefix])) {
19 | return $this->methodCache[$node_class_name][$prefix];
20 | }
21 | else {
22 | $methods = [];
23 | $classes = array_merge([$node_class_name], class_parents($node), class_implements($node));
24 | foreach ($classes as $class_name) {
25 | $class_name = static::getShortClassName($class_name);
26 | $method_name = $prefix . $class_name;
27 | if (method_exists($this, $method_name)) {
28 | $methods[] = $method_name;
29 | }
30 | }
31 | $methods = array_reverse($methods);
32 | $this->methodCache[$node_class_name][$prefix] = $methods;
33 | return $methods;
34 | }
35 | }
36 |
37 | public function visit(Node $node) {
38 | foreach ($this->getMethods($node, 'visit') as $method_name) {
39 | $this->$method_name($node);
40 | }
41 | }
42 |
43 | public function visitEnd(ParentNode $node) {
44 | foreach ($this->getMethods($node, 'end') as $method_name) {
45 | $this->$method_name($node);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/IdentifierNameTraitTest.php:
--------------------------------------------------------------------------------
1 | getBody()->children(Filter::isFunction('hello_world'))[0];
17 | $this->assertEquals('hello_world', $function->getName()->getText());
18 | $this->assertSame($namespace_node, $function->getNamespace());
19 | $this->assertTrue($function->inNamespace($namespace_node));
20 | $this->assertTrue($function->inNamespace('\Test'));
21 | $this->assertFalse($function->inNamespace('\Dummy\Test'));
22 | }
23 |
24 | /**
25 | * @expectedException \InvalidArgumentException
26 | */
27 | public function testInvalid() {
28 | $snippet = <<<'EOF'
29 | namespace Test {
30 | function hello_world() {
31 | echo 'hello world!', PHP_EOL;
32 | }
33 | }
34 | EOF;
35 | /** @var \Pharborist\Namespaces\NamespaceNode $namespace_node */
36 | $namespace_node = Parser::parseSnippet($snippet);
37 | /** @var \Pharborist\Functions\FunctionDeclarationNode $function */
38 | $function = $namespace_node->getBody()->children(Filter::isFunction('hello_world'))[0];
39 | $this->assertTrue($function->inNamespace(new \stdClass()));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Types.php:
--------------------------------------------------------------------------------
1 | assertEquals("getText());
9 |
10 | $doc = RootNode::create('Pharborist\Test');
11 | $this->assertEquals("getText());
12 |
13 | $ns = $doc->children(Filter::isInstanceOf('\Pharborist\Namespaces\NamespaceNode'))[0];
14 | $this->assertEquals('\Pharborist\Test', $ns->getName()->getAbsolutePath());
15 | }
16 |
17 | public function testNSHelpers() {
18 | $doc = RootNode::create('Pharborist');
19 |
20 | $this->assertTrue($doc->hasNamespace('Pharborist'));
21 | $this->assertFalse($doc->hasNamespace('\Drupal'));
22 | $this->assertContains('Pharborist', $doc->getNamespaceNames());
23 | $this->assertContains('\Pharborist', $doc->getNamespaceNames(TRUE));
24 |
25 | $namespaces = $doc->getNamespaces();
26 | $this->assertInstanceOf('\Pharborist\NodeCollection', $namespaces);
27 | $this->assertCount(1, $namespaces);
28 |
29 | $this->assertNull($doc->getNamespace('Drupal'));
30 | $ns = $doc->getNamespace('Pharborist');
31 | $this->assertInstanceOf('\Pharborist\Namespaces\NamespaceNode', $ns);
32 | $this->assertSame($ns, $namespaces[0]);
33 |
34 | $code = <<<'END'
35 | assertTrue($doc->hasNamespace('RoundTable\Knights\MontyPython'));
44 | $this->assertContains('\RoundTable\Knights\MontyPython', $doc->getNamespaceNames(TRUE));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Objects/VisibilityTrait.php:
--------------------------------------------------------------------------------
1 | visibility;
18 | }
19 |
20 | /**
21 | * @param string|integer|TokenNode|NULL $visibility
22 | * @return $this
23 | */
24 | public function setVisibility($visibility) {
25 | if ($visibility === NULL) {
26 | $this->removeVisibility();
27 | }
28 | else {
29 | if ($visibility === 'private' || $visibility === T_PRIVATE) {
30 | $visibility = Token::_private();
31 | }
32 | elseif ($visibility === 'protected' || $visibility === T_PROTECTED) {
33 | $visibility = Token::_protected();
34 | }
35 | elseif ($visibility === 'public' || $visibility === T_PUBLIC) {
36 | $visibility = Token::_public();
37 | }
38 |
39 | if (isset($this->visibility)) {
40 | $this->visibility->replaceWith($visibility);
41 | }
42 | else {
43 | /** @var \Pharborist\ParentNode $this */
44 | $this->prepend([
45 | $visibility,
46 | Token::space(),
47 | ]);
48 | $this->visibility = $visibility;
49 | }
50 | }
51 | return $this;
52 | }
53 |
54 | /**
55 | * Remove the visibility modifier.
56 | */
57 | protected function removeVisibility() {
58 | // Remove whitespace after visibility keyword.
59 | $this->visibility->next()->remove();
60 | // Remove visibility keyword.
61 | $this->visibility->remove();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/FormatterFactory.php:
--------------------------------------------------------------------------------
1 | format($node);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Namespaces/IdentifierNameTrait.php:
--------------------------------------------------------------------------------
1 | name;
22 | }
23 |
24 | /**
25 | * @return NamespaceNode
26 | */
27 | public function getNamespace() {
28 | return $this->name->getNamespace();
29 | }
30 |
31 | /**
32 | * Set the identifier name of this node.
33 | *
34 | * @param string $name
35 | * New name.
36 | * @return $this
37 | */
38 | public function setName($name) {
39 | /** @var TokenNode $identifier */
40 | $identifier = $this->name->firstChild();
41 | $identifier->setText($name);
42 | return $this;
43 | }
44 |
45 | /**
46 | * Determine if this node belongs to namespace.
47 | *
48 | * @param string|NamespaceNode $ns
49 | * Either the absolute namespace path or a NamespaceNode.
50 | *
51 | * @return boolean
52 | * TRUE if the node belongs to the given namespace.
53 | */
54 | public function inNamespace($ns) {
55 | if (is_string($ns)) {
56 | $namespace_node = $this->name->getNamespace();
57 | $namespace = $namespace_node === NULL ? '' : $namespace_node->getName()->getAbsolutePath();
58 | return $ns === $namespace;
59 | }
60 | elseif ($ns instanceof NamespaceNode) {
61 | return $this->name->getNamespace() === $ns;
62 | }
63 | else {
64 | throw new \InvalidArgumentException();
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Operators/TernaryOperationNode.php:
--------------------------------------------------------------------------------
1 | condition;
33 | }
34 |
35 | /**
36 | * @param ExpressionNode $condition
37 | *
38 | * @return $this
39 | */
40 | public function setCondition(ExpressionNode $condition) {
41 | /** @var \Pharborist\Node $condition */
42 | $this->condition->replaceWith($condition);
43 | return $this;
44 | }
45 |
46 | /**
47 | * @return ExpressionNode
48 | */
49 | public function getThen() {
50 | return $this->then;
51 | }
52 |
53 | /**
54 | * @param ExpressionNode $then
55 | *
56 | * @return $this
57 | */
58 | public function setThen(ExpressionNode $then) {
59 | /** @var \Pharborist\Node $then */
60 | $this->then->replaceWith($then);
61 | return $this;
62 | }
63 |
64 | /**
65 | * @return ExpressionNode
66 | */
67 | public function getElse() {
68 | return $this->else;
69 | }
70 |
71 | /**
72 | * @param ExpressionNode $else
73 | *
74 | * @return $this
75 | */
76 | public function setElse(ExpressionNode $else) {
77 | /** @var \Pharborist\Node $else */
78 | $this->condition->replaceWith($else);
79 | return $this;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | pharborist
2 | ==========
3 |
4 | 
5 |
6 | A PHP library to query and transform PHP source code via tree operations.
7 |
8 | # Roadmap
9 | * [ ] Tests with 100% code coverage
10 | * [x] Integration with a third party library for PHPDoc comments
11 | * [ ] API to ease querying and transforming of the syntax tree
12 | * [ ] Build a PHP source code formatter using the library (Partially completed).
13 | * [ ] Build a PHP code check using the library
14 |
15 | Below is an example of how the API might look once its more developed:
16 |
17 | ```php
18 | // Add use declaration if it does not already exist. Use UtilityString alias if conflict
19 | $alias = $tree->ensureUseDeclaration('Drupal\Component\Utility\String', 'UtilityString');
20 | // Find all calls to check_plain and rename them to use String::checkPlain
21 | $function_calls = $tree->find(Filter::functionCall('check_plain'));
22 | foreach ($function_calls as $call) {
23 | $class_method_call = ClassMethodCallNode::create($alias, 'check_plain', $call->getArgumentList());
24 | $call->replaceWith($class_method_call);
25 | }
26 | ```
27 |
28 | # Usage
29 | ```php
30 | require_once 'vendor/autoload.php';
31 |
32 | use Pharborist\Parser;
33 | use Pharborist\Namespaces\NamespaceNode;
34 | use Pharborist\Filter;
35 |
36 | $filename = $argv[1];
37 | $tree = Parser::parseFile($filename);
38 |
39 | // check there only one namespace declaration
40 | $namespaces = $tree->children(Filter::isInstanceOf('\Pharborist\Namespaces\NamespaceNode'));
41 | if ($namespaces->count() > 1) {
42 | die('More then one namespace at line ' . $namespaces[1]->getLineNumber() . PHP_EOL);
43 | }
44 | ```
45 | [](https://travis-ci.org/grom358/pharborist)
46 |
--------------------------------------------------------------------------------
/src/ControlStructures/IfNode.php:
--------------------------------------------------------------------------------
1 | condition;
48 | }
49 |
50 | /**
51 | * @return Node
52 | */
53 | public function getThen() {
54 | return $this->then;
55 | }
56 |
57 | /**
58 | * @return NodeCollection|ElseIfNode[]
59 | */
60 | public function getElseIfs() {
61 | return new NodeCollection($this->childrenByInstance('\Pharborist\ControlStructures\ElseIfNode'), FALSE);
62 | }
63 |
64 | /**
65 | * The T_ELSE keyword token.
66 | *
67 | * @return TokenNode
68 | */
69 | public function getElseKeyword() {
70 | return $this->elseKeyword;
71 | }
72 |
73 | /**
74 | * The colon ':' token when using alternative syntax.
75 | *
76 | * @return TokenNode
77 | */
78 | public function getElseColon() {
79 | return $this->elseColon;
80 | }
81 |
82 | /**
83 | * @return Node
84 | */
85 | public function getElse() {
86 | return $this->else;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Objects/ObjectPropertyNode.php:
--------------------------------------------------------------------------------
1 | property`.
11 | */
12 | class ObjectPropertyNode extends ParentNode implements VariableExpressionNode {
13 | /**
14 | * @var Node
15 | */
16 | protected $object;
17 |
18 | /**
19 | * @var Node
20 | */
21 | protected $property;
22 |
23 | /**
24 | * @return Node
25 | */
26 | public function getObject() {
27 | return $this->object;
28 | }
29 |
30 | /**
31 | * @return Node
32 | */
33 | public function getProperty() {
34 | return $this->property;
35 | }
36 |
37 | /**
38 | * Returns the name of the property if it's an identifier (ie. T_STRING TokenNode).
39 | *
40 | * @return string|NULL
41 | * Name of the property or NULL if not an identifier (eg. dynamic property
42 | * name).
43 | */
44 | public function getPropertyName() {
45 | $root_property = $this->getRootProperty();
46 | if ($root_property instanceof TokenNode && $root_property->getType() === T_STRING) {
47 | return $root_property->getText();
48 | }
49 | return NULL;
50 | }
51 |
52 | /**
53 | * Returns the root property.
54 | *
55 | * For example, given an expression like $foo->bar->baz this method will
56 | * return the identifier (T_STRING TokenNode) 'bar'.
57 | *
58 | * @return Node
59 | * The node for the root property.
60 | */
61 | public function getRootProperty() {
62 | if ($this->object instanceof ObjectPropertyNode) {
63 | return $this->object->getRootProperty();
64 | }
65 | else {
66 | return $this->property;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Objects/MethodTrait.php:
--------------------------------------------------------------------------------
1 | name;
32 | }
33 |
34 | /**
35 | * @param string|TokenNode $name
36 | *
37 | * @return $this
38 | */
39 | public function setName($name) {
40 | if (is_string($name)) {
41 | $name = Token::identifier($name);
42 | }
43 | $this->name->replaceWith($name);
44 | return $this;
45 | }
46 |
47 | /**
48 | * @return bool
49 | */
50 | public function isStatic() {
51 | return $this->static !== NULL;
52 | }
53 |
54 | /**
55 | * @return TokenNode
56 | */
57 | public function getStatic() {
58 | return $this->static;
59 | }
60 |
61 | /**
62 | * @param boolean $is_static
63 | * @return $this
64 | */
65 | public function setStatic($is_static) {
66 | if ($is_static) {
67 | if (!isset($this->static)) {
68 | // Insert before T_FUNCTION.
69 | $function_token = $this->name->previous()->previous();
70 | $this->static = Token::_static();
71 | $function_token->before([
72 | $this->static,
73 | Token::space(),
74 | ]);
75 | }
76 | }
77 | else {
78 | if (isset($this->static)) {
79 | // Remove whitespace after static keyword.
80 | $this->static->next()->remove();
81 | // Remove static keyword.
82 | $this->static->remove();
83 | }
84 | }
85 | return $this;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tests/InterfaceNodeTest.php:
--------------------------------------------------------------------------------
1 | assertSame(['wambooli'], $interface->getMethodNames());
9 | }
10 |
11 | public function testHasMethod() {
12 | /** @var \Pharborist\Objects\InterfaceNode $interface */
13 | $interface = Parser::parseSnippet('interface Foo { public function wambooli(); }');
14 | $this->assertTrue($interface->hasMethod('wambooli'));
15 | $this->assertFalse($interface->hasMethod('blorf'));
16 | }
17 |
18 | public function testGetMethods() {
19 | /** @var \Pharborist\Objects\InterfaceNode $interface */
20 | $interface = Parser::parseSnippet('interface Foo { public function wambooli(); }');
21 | $methods = $interface->getMethods();
22 | $this->assertInstanceOf('\Pharborist\NodeCollection', $methods);
23 | $this->assertEquals(1, $methods->count());
24 | }
25 |
26 | public function testGetMethod() {
27 | /** @var \Pharborist\Objects\InterfaceNode $interface */
28 | $interface = Parser::parseSnippet('interface Foo { public function wambooli(); }');
29 | $method = $interface->getMethod('wambooli');
30 | $this->assertInstanceOf('\Pharborist\Objects\InterfaceMethodNode', $method);
31 | $this->assertEquals('wambooli', $method->getName()->getText());
32 | $this->assertNull($interface->getMethod('harrr'));
33 | }
34 |
35 | public function testGetConstants() {
36 | /** @var \Pharborist\Objects\InterfaceNode $interface */
37 | $interface = Parser::parseSnippet('interface Foo { const ANSWER = 42; }');
38 | $constants = $interface->getConstants();
39 | $this->assertCount(1, $constants);
40 | /** @var \Pharborist\Constants\ConstantDeclarationNode $constant */
41 | $constant = $constants[0];
42 | $this->assertInstanceOf('\Pharborist\Constants\ConstantDeclarationNode', $constant);
43 | $this->assertEquals('ANSWER', $constant->getName());
44 | $this->assertEquals('42', $constant->getValue()->getText());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Functions/CallNode.php:
--------------------------------------------------------------------------------
1 | bar();
14 | * Foo::bar();
15 | * $foo('bar');
16 | * ```
17 | */
18 | abstract class CallNode extends ParentNode {
19 | use ArgumentTrait;
20 | use ParenTrait;
21 |
22 | /**
23 | * Allows you to append a method call to this one, building a chain of method
24 | * calls.
25 | *
26 | * For example:
27 | * ```
28 | * // \Drupal::entityManager()
29 | * $classCall = ClassMethodCallNode::create('\Drupal', 'entityManager');
30 | *
31 | * $methodCall = $classCall->appendMethodCall('getDefinitions');
32 | * echo $methodCall->getText(); // \Drupal::entityManager()->getDefinitions()
33 | * echo $methodCall->getObject(); // \Drupal::entityManager()
34 | * echo $methodCall->getMethodName(); // getDefinitions
35 | *
36 | * // You can chain yet another call, and keep going as long as you want.
37 | *
38 | * $methodCall = $methodCall->appendMethodCall('clearCache')
39 | * echo $methodCall->getText(); // \Drupal::entityManager()->getDefinitions()->clearCache()
40 | *
41 | * // These methods are chainable themselves, so you can build an entire call chain
42 | * // in one fell swoop.
43 | *
44 | * $chain = ClassMethodCallNode::create('Foo', 'bar')->appendMethodCall('baz')->appendMethodCall('zorg');
45 | * echo $chain->getText(); // Foo::bar()->baz()->zorg()
46 | * ```
47 | *
48 | * @param string $method_name
49 | * The name of the method to call.
50 | *
51 | * @return \Pharborist\Objects\ObjectMethodCallNode
52 | * The newly-created method call, in which every previous part of the chain will be the
53 | * "object", and $method_name will be the "method". The call will be created without
54 | * arguments, but you can add some using appendArgument().
55 | */
56 | public function appendMethodCall($method_name) {
57 | $method_call = ObjectMethodCallNode::create(clone $this, $method_name);
58 | $this->replaceWith($method_call);
59 | return $method_call;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/ClassMemberNodeTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf('\Pharborist\Objects\ClassMemberListNode', $member);
13 | $this->assertEquals('public $lancelot;', $member->getText());
14 |
15 | $member = ClassMemberNode::create('robin', StringNode::create("'cowardly'"), 'protected');
16 | $this->assertInstanceOf('\Pharborist\Objects\ClassMemberListNode', $member);
17 | $this->assertEquals('protected $robin = \'cowardly\';', $member->getText());
18 | }
19 |
20 | public function testStatic() {
21 | /** @var ClassNode $class_node */
22 | $class_node = Parser::parseSnippet('class Foo { public $bar; }');
23 | /** @var ClassMemberNode $a */
24 | $a = $class_node->getProperties()[0];
25 | $this->assertFalse($a->isStatic());
26 | $this->assertNull($a->getStatic());
27 | $a->setStatic(TRUE);
28 | $this->assertTrue($a->isStatic());
29 | $this->assertSame('public static $bar;', $a->closest(Filter::isInstanceOf('\Pharborist\Objects\ClassMemberListNode'))->getText());
30 |
31 | $class_node = Parser::parseSnippet('class Bar { protected static $baz; }');
32 | /** @var ClassMemberNode $b */
33 | $b = $class_node->getProperties()[0];
34 | $this->assertTrue($b->isStatic());
35 | $this->assertInstanceOf('\Pharborist\TokenNode', $b->getStatic());
36 | $this->assertSame(T_STATIC, $b->getStatic()->getType());
37 | $b->setStatic(FALSE);
38 | $this->assertFalse($b->isStatic());
39 | $this->assertSame('protected $baz;', $b->closest(Filter::isInstanceOf('\Pharborist\Objects\ClassMemberListNode'))->getText());
40 | }
41 |
42 | public function testVisibility() {
43 | /** @var ClassNode $class_node */
44 | $class_node = Parser::parseSnippet('class Foo { public $bar; }');
45 | /** @var ClassMemberNode $a */
46 | $a = $class_node->getProperties()[0];
47 | $this->assertEquals('private $bar;', $a->setVisibility('private')->parent()->parent()->getText());
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/ArrayLookupNodeTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('$form_state[\'storage\']', $lookup->getText());
10 | }
11 |
12 | public function testGetKeys() {
13 | $lookup = Parser::parseExpression('$foo["bar"]["baz"]');
14 | $keys = $lookup->getKeys();
15 | $this->assertInternalType('array', $keys);
16 | $this->assertCount(2, $keys);
17 | $this->assertInstanceOf('\Pharborist\Types\StringNode', $keys[0]);
18 | $this->assertEquals('bar', $keys[0]->toValue());
19 | $this->assertInstanceOf('\Pharborist\Types\StringNode', $keys[1]);
20 | $this->assertEquals('baz', $keys[1]->toValue());
21 | }
22 |
23 | public function testHasScalarKeys() {
24 | $this->assertTrue(Parser::parseExpression('$foo["bar"]["baz"][3]')->hasScalarKeys());
25 | $this->assertFalse(Parser::parseExpression('$foo[$bar]["baz"]')->hasScalarKeys());
26 | }
27 |
28 | public function testExtractKeys() {
29 | $this->assertSame(['bar', 'baz', 3], Parser::parseExpression('$foo["bar"]["baz"][3]')->extractKeys());
30 | }
31 |
32 | /**
33 | * @expectedException \DomainException
34 | */
35 | public function testExtractNonScalarKeys() {
36 | Parser::parseExpression('$foo[$bar][baz()][30]')->extractKeys();
37 | }
38 |
39 | public function testGetRoot() {
40 | /** @var ArrayLookupNode $lookup */
41 | $lookup = Parser::parseExpression('$foo["bar"]["baz"][0]');
42 | $this->assertInstanceOf('\Pharborist\ArrayLookupNode', $lookup);
43 | $root = $lookup->getRootArray();
44 | $this->assertInstanceOf('\Pharborist\Variables\VariableNode', $root);
45 | $this->assertEquals('$foo', $root->getText());
46 |
47 | $lookup = Parser::parseExpression('foo()["bar"]');
48 | $this->assertInstanceOf('\Pharborist\ArrayLookupNode', $lookup);
49 | $root = $lookup->getRootArray();
50 | $this->assertInstanceOf('\Pharborist\Functions\FunctionCallNode', $root);
51 | $this->assertEquals('foo', $root->getName()->getText());
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/ParentNodeInterface.php:
--------------------------------------------------------------------------------
1 | addChild(Token::openTag());
21 | if (is_string($ns) && $ns) {
22 | NamespaceNode::create($ns)->appendTo($node)->after(Token::newline());
23 | }
24 | return $node;
25 | }
26 |
27 | /**
28 | * Returns if this document contains a particular namespace.
29 | *
30 | * @param string $ns
31 | * The name of the namespace to look for.
32 | *
33 | * @return boolean
34 | */
35 | public function hasNamespace($ns) {
36 | return in_array($ns, $this->getNamespaceNames());
37 | }
38 |
39 | /**
40 | * Returns every namespace in this document.
41 | *
42 | * @return \Pharborist\NodeCollection
43 | */
44 | public function getNamespaces() {
45 | return $this->children(Filter::isInstanceOf('\Pharborist\Namespaces\NamespaceNode'));
46 | }
47 |
48 | /**
49 | * Returns a particular namespace, if it exists.
50 | *
51 | * @param string $ns
52 | * The name of the namespace to look for.
53 | *
54 | * @return \Pharborist\Namespaces\NamespaceNode|NULL
55 | */
56 | public function getNamespace($ns) {
57 | $namespaces = $this
58 | ->getNamespaces()
59 | ->filter(function(NamespaceNode $node) use ($ns) {
60 | return $node->getName()->getPath() === $ns;
61 | });
62 |
63 | return $namespaces->isEmpty() ? NULL : $namespaces[0];
64 | }
65 |
66 | /**
67 | * Returns the name of every namespace in this document.
68 | *
69 | * @param boolean $absolute
70 | *
71 | * @return string[]
72 | */
73 | public function getNamespaceNames($absolute = FALSE) {
74 | $iterator = function(NamespaceNode $ns) use ($absolute) {
75 | $name = $ns->getName();
76 | return $absolute ? $name->getAbsolutePath() : $name->getPath();
77 | };
78 | return array_map($iterator, $this->getNamespaces()->toArray());
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Types/StringNode.php:
--------------------------------------------------------------------------------
1 | getText();
34 | $quote_char = $text[0];
35 | $text = substr($text, 1, -1);
36 | if ($quote_char === '"') {
37 | $rules = array(
38 | preg_quote('\\\\') => '\\',
39 | preg_quote('\n') => "\n",
40 | preg_quote('\t') => "\t",
41 | preg_quote('\"') => '"',
42 | preg_quote('\$') => '$',
43 | preg_quote('\r') => "\r",
44 | preg_quote('\v') => "\v",
45 | preg_quote('\f') => "\f",
46 | preg_quote('\e') => "\e",
47 | '\\\\[0-7]{1,3}' => '__octal__',
48 | '\\\\x[0-9A-Fa-f]{1,2}' => '__hex__',
49 | );
50 | }
51 | else {
52 | $rules = array(
53 | preg_quote('\\\\') => '\\',
54 | preg_quote("\\'") => "'"
55 | );
56 | }
57 | $replacements = array_values($rules);
58 | $regex = '@(' . implode(')|(', array_keys($rules)) . ')@';
59 | return preg_replace_callback($regex, function ($matches) use ($replacements) {
60 | // find the first non-empty element (but skipping $matches[0]) using a quick for loop
61 | for ($i = 1; '' === $matches[$i]; ++$i);
62 |
63 | $match = $matches[0];
64 | $replacement = $replacements[$i - 1];
65 | if ($replacement === '__octal__') {
66 | $replacement = chr(octdec(substr($match, 1)));
67 | }
68 | elseif ($replacement === '__hex__') {
69 | $replacement = chr(hexdec(substr($match, 2)));
70 | }
71 | return $replacement;
72 | }, $text);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/StatementNode.php:
--------------------------------------------------------------------------------
1 | A statement is a single executable unit of PHP code. You can think of a
8 | * statement as a single "sentence" of code, usually ending with
9 | * a semicolon. A single statement usually (but not always!) occupies a single
10 | * line.
11 | * Here's an example of a perfectly valid statement:
12 | * echo "Let's not go to Camelot. 'Tis a silly place.\n";
13 | * Statements can contain other statements, or a block of statements surrounded
14 | * by curly braces. A single statement is usually made up of one or more
15 | * expressions.
16 | * Declarations are also statements. For instance, if/elseif/else and switch/case
17 | * structures are statements, including all of their blocks. So is are class and function
18 | * declarations. The body of the class or function is a statement block, but it's
19 | * contained by the class or function declaration, which is a statement.
20 | */
21 | abstract class StatementNode extends ParentNode {
22 | /**
23 | * Gets the number of lines spanned by this statement.
24 | *
25 | * @return integer
26 | * Always returns at least one, because any statement will be at least
27 | * one line long.
28 | */
29 | public function getLineCount() {
30 | $count = 1;
31 |
32 | $this
33 | ->find(Filter::isInstanceOf('\Pharborist\WhitespaceNode'))
34 | ->each(function(WhitespaceNode $node) use (&$count) {
35 | $count += $node->getNewlineCount();
36 | });
37 |
38 | return $count;
39 | }
40 |
41 | /**
42 | * Creates a commented-out version of this statement.
43 | *
44 | * @return \Pharborist\CommentNode|\Pharborist\LineCommentBlockNode
45 | */
46 | public function toComment() {
47 | return CommentNode::create($this->getText());
48 | }
49 |
50 | /**
51 | * Adds a line comment block above the statement.
52 | *
53 | * @param \Pharborist\LineCommentBlockNode|string $comment
54 | * The comment to add.
55 | *
56 | * @return $this
57 | *
58 | * @throws \InvalidArgumentException
59 | */
60 | public function addCommentAbove($comment) {
61 | if ($comment instanceof LineCommentBlockNode) {
62 | $this->before($comment);
63 | }
64 | elseif (is_string($comment)) {
65 | $this->addCommentAbove(LineCommentBlockNode::create($comment));
66 | }
67 | else {
68 | throw new \InvalidArgumentException();
69 | }
70 | return $this;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Objects/ClassMethodCallNode.php:
--------------------------------------------------------------------------------
1 | className;
30 | }
31 |
32 | /**
33 | * @param string|Node $class_name
34 | * @return $this
35 | */
36 | public function setClassName($class_name) {
37 | if (is_string($class_name)) {
38 | $class_name = Token::identifier($class_name);
39 | }
40 | $this->className->replaceWith($class_name);
41 | $this->className = $class_name;
42 | return $this;
43 | }
44 |
45 | /**
46 | * @return Node
47 | */
48 | public function getMethodName() {
49 | return $this->methodName;
50 | }
51 |
52 | /**
53 | * @param string|Node $method_name
54 | * @return $this
55 | */
56 | public function setMethodName($method_name) {
57 | if (is_string($method_name)) {
58 | $method_name = Token::identifier($method_name);
59 | }
60 | $this->methodName->replaceWith($method_name);
61 | $this->methodName = $method_name;
62 | return $this;
63 | }
64 |
65 | /**
66 | * Creates a method call on a class with an empty argument list.
67 | *
68 | * @param Node|string $class_name
69 | * The class node which is typically NameNode of class.
70 | * @param string $method_name
71 | * The name of the called method.
72 | *
73 | * @return static
74 | */
75 | public static function create($class_name, $method_name) {
76 | if (is_string($class_name)) {
77 | $class_name = NameNode::create($class_name);
78 | }
79 | /** @var ClassMethodCallNode $node */
80 | $node = new static();
81 | $node->addChild($class_name, 'className');
82 | $node->addChild(Token::doubleColon());
83 | $node->addChild(Token::identifier($method_name), 'methodName');
84 | $node->addChild(Token::openParen(), 'openParen');
85 | $node->addChild(new CommaListNode(), 'arguments');
86 | $node->addChild(Token::closeParen(), 'closeParen');
87 | return $node;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/StatementBlockNode.php:
--------------------------------------------------------------------------------
1 | head;
13 | while ($child) {
14 | if ($child instanceof StatementNode) {
15 | $matches[] = $child;
16 | }
17 | elseif ($child instanceof StatementBlockNode) {
18 | $matches = array_merge($matches, $child->_getStatements());
19 | }
20 | $child = $child->next;
21 | }
22 | return $matches;
23 | }
24 |
25 | /**
26 | * @return NodeCollection|StatementNode[]
27 | */
28 | public function getStatements() {
29 | return new NodeCollection($this->_getStatements(), FALSE);
30 | }
31 |
32 | /**
33 | * Get the use declarations of this statement block.
34 | *
35 | * @return NodeCollection|UseDeclarationNode[]
36 | * Use declarations.
37 | */
38 | public function getUseDeclarations() {
39 | $declarations = [];
40 | /** @var \Pharborist\Namespaces\UseDeclarationBlockNode[] $use_blocks */
41 | $use_blocks = $this->children(Filter::isInstanceOf('\Pharborist\Namespaces\UseDeclarationBlockNode'));
42 | foreach ($use_blocks as $use_block) {
43 | foreach ($use_block->getDeclarationStatements() as $use_statement) {
44 | $declarations = array_merge($declarations, $use_statement->getDeclarations()->toArray());
45 | }
46 | }
47 | return new NodeCollection($declarations, FALSE);
48 | }
49 |
50 | /**
51 | * Return mapping of class names to fully qualified names.
52 | *
53 | * @return array
54 | * Associative array of namespace alias to fully qualified names.
55 | */
56 | public function getClassAliases() {
57 | $mappings = array();
58 | foreach ($this->getUseDeclarations() as $use_declaration) {
59 | if ($use_declaration->isClass()) {
60 | $mappings[$use_declaration->getBoundedName()] = $use_declaration->getName()->getAbsolutePath();
61 | }
62 | }
63 | return $mappings;
64 | }
65 |
66 | /**
67 | * Append statement to block.
68 | *
69 | * @param StatementNode $statementNode
70 | * Statement to append.
71 | * @return $this
72 | */
73 | public function appendStatement(StatementNode $statementNode) {
74 | if (!$this->isEmpty() && $this->firstToken()->getType() === '{') {
75 | $this->lastChild()->before($statementNode);
76 | }
77 | else {
78 | $this->appendChild($statementNode);
79 | }
80 | return $this;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Functions/FunctionDeclarationNode.php:
--------------------------------------------------------------------------------
1 | appendParameter($parameter);
39 | }
40 | }
41 | return $function;
42 | }
43 |
44 | /**
45 | * @var StatementBlockNode
46 | */
47 | protected $body;
48 |
49 | /**
50 | * Set the name of the declared function.
51 | *
52 | * @param string $name
53 | * New function name.
54 | * @return $this
55 | */
56 | public function setName($name) {
57 | /** @var TokenNode $function_name */
58 | $function_name = $this->getName()->firstChild();
59 | $function_name->setText($name);
60 | return $this;
61 | }
62 |
63 | /**
64 | * @return StatementBlockNode
65 | */
66 | public function getBody() {
67 | return $this->body;
68 | }
69 |
70 | /**
71 | * Creates a class method from this function and add it to the given
72 | * class definition.
73 | *
74 | * @param \Pharborist\Objects\ClassNode $class
75 | * The class to add the new method to.
76 | *
77 | * @return \Pharborist\Objects\ClassMethodNode
78 | * The newly created method.
79 | */
80 | public function cloneAsMethodOf(ClassNode $class) {
81 | $clone = ClassMethodNode::fromFunction($this);
82 | $class->appendMethod($clone);
83 | return $clone;
84 | }
85 |
86 | public static function fromReflector(\ReflectionFunction $reflector) {
87 | $node = static::create($reflector->getName());
88 | $node->matchReflector($reflector);
89 | return $node;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/TokenIterator.php:
--------------------------------------------------------------------------------
1 | tokens = $tokens;
31 | $this->length = count($tokens);
32 | $this->position = 0;
33 | }
34 |
35 | /**
36 | * Return the current token.
37 | * @return TokenNode
38 | */
39 | public function current() {
40 | if ($this->position >= $this->length) {
41 | return NULL;
42 | }
43 | return $this->tokens[$this->position];
44 | }
45 |
46 | /**
47 | * Peek ahead.
48 | * @param int $offset Offset from current position.
49 | * @return TokenNode
50 | */
51 | public function peek($offset) {
52 | if ($this->position + $offset >= $this->length) {
53 | return NULL;
54 | }
55 | return $this->tokens[$this->position + $offset];
56 | }
57 |
58 | /**
59 | * Move to the next token and return it.
60 | * @return TokenNode
61 | */
62 | public function next() {
63 | $this->position++;
64 | if ($this->position >= $this->length) {
65 | $this->position = $this->length;
66 | return NULL;
67 | }
68 | return $this->tokens[$this->position];
69 | }
70 |
71 | /**
72 | * Return TRUE if there are more tokens.
73 | * @return bool
74 | */
75 | public function hasNext() {
76 | return $this->position + 1 < $this->length;
77 | }
78 |
79 | /**
80 | * @return int
81 | */
82 | public function getLineNumber() {
83 | if ($this->length === 0) {
84 | return 1;
85 | }
86 | $token = $this->current();
87 | if ($token === NULL) {
88 | $token = $this->tokens[$this->length - 1];
89 | return $token->getLineNumber();
90 | }
91 | return $token->getLineNumber();
92 | }
93 |
94 | /**
95 | * @return int
96 | */
97 | public function getColumnNumber() {
98 | if ($this->length === 0) {
99 | return 1;
100 | }
101 | $token = $this->current();
102 | if ($token === NULL) {
103 | $token = $this->tokens[$this->length - 1];
104 | return $token->getColumnNumber() + $token->getByteLength();
105 | }
106 | return $token->getColumnNumber();
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/tests/StatementNodeTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(7, $node->getLineCount());
18 | // What, you haven't seen Spaceballs?
19 | $node = Parser::parseSnippet('$combination = 12345;');
20 | $this->assertEquals(1, $node->getLineCount());
21 |
22 |
23 | $text = <<<'END'
24 | db_delete('variable')
25 | ->condition('name', 'cron_last')
26 | ->execute();
27 | END;
28 | $node = Parser::parseSnippet($text);
29 | $this->assertEquals(3, $node->getLineCount());
30 | }
31 |
32 | public function testToComment() {
33 | $original = <<<'END'
34 | class Foo {
35 | private $larry;
36 | private $moe;
37 | private $curly;
38 | }
39 | END;
40 |
41 | // The expected text needs the extra line, because PHP ignores the new line
42 | // before END for whatever reason. Weird, but the tests pass this way.
43 | $expected = <<<'END'
44 | // class Foo {
45 | // private $larry;
46 | // private $moe;
47 | // private $curly;
48 | // }
49 |
50 | END;
51 | /** @var StatementNode $statement_node */
52 | $statement_node = Parser::parseSnippet($original);
53 | $comment = $statement_node->toComment();
54 | $this->assertEquals($expected, $comment->getText());
55 | $this->assertEquals($original, $comment->uncomment()->getText());
56 | }
57 |
58 | public function testAddCommentAbove() {
59 | $original = '$value = variable_get("my_variable", NULL);';
60 | $comment = <<addCommentAbove($comment);
76 | $this->assertEquals($expected, $node->parent()->getText());
77 | }
78 |
79 | /**
80 | * @expectedException \InvalidArgumentException
81 | */
82 | public function testInvalidCommentAbove() {
83 | $original = '$value = variable_get("my_variable", NULL);';
84 | /** @var StatementNode $statement_node */
85 | $statement_node = Parser::parseSnippet($original);
86 | $statement_node->addCommentAbove(NULL);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/tests/UseDeclarationStatementNodeTest.php:
--------------------------------------------------------------------------------
1 | getDeclarationStatements()[0];
11 | $this->assertCount(1, $use_declaration_statement->getDeclarations());
12 | $this->assertEquals($use_declaration_statement->getDeclarations(), $use_declaration_statement->getDeclarationList()->getItems());
13 | $this->assertTrue($use_declaration_statement->importsClass());
14 | $this->assertFalse($use_declaration_statement->importsConst());
15 | $this->assertFalse($use_declaration_statement->importsFunction());
16 | $this->assertTrue($use_declaration_statement->importsClass('MyNamespace\MyClass'));
17 | $this->assertFalse($use_declaration_statement->importsClass('MyNamespace\NotFound'));
18 | }
19 |
20 | public function testImportConst() {
21 | /** @var UseDeclarationBlockNode $use_block */
22 | $use_block = Parser::parseSnippet('use const MyNamespace\MY_CONST;');
23 | $use_declaration_statement = $use_block->getDeclarationStatements()[0];
24 | $this->assertCount(1, $use_declaration_statement->getDeclarations());
25 | $this->assertEquals($use_declaration_statement->getDeclarations(), $use_declaration_statement->getDeclarationList()->getItems());
26 | $this->assertTrue($use_declaration_statement->importsConst());
27 | $this->assertFalse($use_declaration_statement->importsClass());
28 | $this->assertFalse($use_declaration_statement->importsFunction());
29 | $this->assertTrue($use_declaration_statement->importsConst('MyNamespace\MY_CONST'));
30 | $this->assertFalse($use_declaration_statement->importsConst('MyNamespace\NOT_FOUND'));
31 | }
32 |
33 | public function testImportFunction() {
34 | /** @var UseDeclarationBlockNode $use_block */
35 | $use_block = Parser::parseSnippet('use function MyNamespace\test_func;');
36 | $use_declaration_statement = $use_block->getDeclarationStatements()[0];
37 | $this->assertCount(1, $use_declaration_statement->getDeclarations());
38 | $this->assertEquals($use_declaration_statement->getDeclarations(), $use_declaration_statement->getDeclarationList()->getItems());
39 | $this->assertTrue($use_declaration_statement->importsFunction());
40 | $this->assertFalse($use_declaration_statement->importsConst());
41 | $this->assertFalse($use_declaration_statement->importsClass());
42 | $this->assertTrue($use_declaration_statement->importsFunction('MyNamespace\test_func'));
43 | $this->assertFalse($use_declaration_statement->importsFunction('MyNamespace\not_found'));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Objects/ObjectMethodCallNode.php:
--------------------------------------------------------------------------------
1 | method()`
13 | */
14 | class ObjectMethodCallNode extends CallNode implements VariableExpressionNode {
15 | /**
16 | * @var Node
17 | */
18 | protected $object;
19 |
20 | /**
21 | * @var TokenNode
22 | */
23 | protected $operator;
24 |
25 | /**
26 | * @var Node
27 | */
28 | protected $methodName;
29 |
30 | /**
31 | * @return Node
32 | */
33 | public function getObject() {
34 | return $this->object;
35 | }
36 |
37 | /**
38 | * The object operator '->' token (T_OBJECT_OPERATOR).
39 | *
40 | * @return TokenNode
41 | */
42 | public function getOperator() {
43 | return $this->operator;
44 | }
45 |
46 | /**
47 | * @return Node
48 | */
49 | public function getMethodName() {
50 | return $this->methodName;
51 | }
52 |
53 | /**
54 | * @param string|Node $method_name
55 | * @return $this
56 | */
57 | public function setMethodName($method_name) {
58 | if (is_string($method_name)) {
59 | $method_name = Token::identifier($method_name);
60 | }
61 | $this->methodName->replaceWith($method_name);
62 | $this->methodName = $method_name;
63 | return $this;
64 | }
65 |
66 | /**
67 | * Creates a method call on an object with an empty argument list.
68 | *
69 | * @param Node $object
70 | * The expression that is an object.
71 | * @param string $method_name
72 | * The name of the called method.
73 | *
74 | * @return static
75 | */
76 | public static function create(Node $object, $method_name) {
77 | /** @var ObjectMethodCallNode $node */
78 | $node = new static();
79 | $node->addChild($object, 'object');
80 | $node->addChild(Token::objectOperator(), 'operator');
81 | $node->addChild(Token::identifier($method_name), 'methodName');
82 | $node->addChild(Token::openParen(), 'openParen');
83 | $node->addChild(new CommaListNode(), 'arguments');
84 | $node->addChild(Token::closeParen(), 'closeParen');
85 | return $node;
86 | }
87 |
88 | /**
89 | * If this is a chained method call (e.g., foo()->bar()->baz()), returns
90 | * the previous call in the chain.
91 | *
92 | * @return \Pharborist\Functions\CallNode|NULL
93 | * The previous call in the chain or NULL if there is none.
94 | */
95 | public function getPreviousCall() {
96 | if ($this->object instanceof CallNode) {
97 | return $this->object;
98 | }
99 | else {
100 | return NULL;
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/Functions/FunctionTrait.php:
--------------------------------------------------------------------------------
1 | reference;
25 | }
26 |
27 | /**
28 | * @param boolean $is_reference
29 | * @return $this
30 | */
31 | public function setReference($is_reference) {
32 | if ($is_reference) {
33 | if (!isset($this->reference)) {
34 | /** @var \Pharborist\Functions\FunctionDeclarationNode|\Pharborist\Objects\ClassMethodNode|\Pharborist\Objects\InterfaceMethodNode $this */
35 | $this->reference = Token::reference();
36 | $this->name->before($this->reference);
37 | }
38 | }
39 | else {
40 | if (isset($this->reference)) {
41 | $this->reference->remove();
42 | }
43 | }
44 | return $this;
45 | }
46 |
47 | /**
48 | * Return TRUE if function has phpDoc return type.
49 | *
50 | * @return bool
51 | */
52 | public function hasReturnTypes() {
53 | $doc_comment = $this->getDocComment();
54 | if (!$doc_comment) {
55 | return FALSE;
56 | }
57 | $return_tag = $doc_comment->getReturn();
58 | if (!$return_tag) {
59 | return FALSE;
60 | }
61 | $types = $return_tag->getTypes();
62 | return !empty($types);
63 | }
64 |
65 | /**
66 | * Get the return type of the function as defined by the doc comment.
67 | *
68 | * @return string[]
69 | * The types as defined by phpdoc standard. Default is ['void'].
70 | */
71 | public function getReturnTypes() {
72 | $types = ['void'];
73 | $doc_comment = $this->getDocComment();
74 | if (!$doc_comment) {
75 | return $types;
76 | }
77 | $return_tag = $doc_comment->getReturn();
78 | if (!$return_tag) {
79 | return $types;
80 | }
81 | $types = Types::normalize($return_tag->getTypes());
82 | if (empty($types)) {
83 | $types[] = 'void';
84 | }
85 | return $types;
86 | }
87 |
88 | public function matchReflector(\ReflectionFunctionAbstract $reflector) {
89 | $this->setReference($reflector->returnsReference());
90 |
91 | foreach ($reflector->getParameters() as $i => $parameter) {
92 | try {
93 | $this->getParameterAtIndex($i)->matchReflector($parameter);
94 | }
95 | catch (\OutOfBoundsException $e) {
96 | $this->appendParameter(ParameterNode::fromReflector($parameter));
97 | }
98 | }
99 |
100 | return $this;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/LineCommentBlockNode.php:
--------------------------------------------------------------------------------
1 | addChild($comment_node);
30 | }
31 | return $block_comment;
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | public function getCommentText() {
38 | $comment = '';
39 | $child = $this->firstChild();
40 | $first = TRUE;
41 | while ($child) {
42 | if ($child instanceof CommentNode) {
43 | if ($first) {
44 | $first = FALSE;
45 | }
46 | else {
47 | $comment .= "\n";
48 | }
49 | $comment .= $child->getCommentText();
50 | }
51 | $child = $child->next;
52 | }
53 | return $comment;
54 | }
55 |
56 | /**
57 | * Set indent for document comment.
58 | *
59 | * @param string $indent
60 | * Whitespace to use as indent.
61 | * @return $this
62 | */
63 | public function setIndent($indent) {
64 | // Normalize comment block.
65 | $this->removeIndent();
66 | // Add indent to comment block.
67 | $this->addIndent($indent);
68 | return $this;
69 | }
70 |
71 | /**
72 | * Remove indent from document comment.
73 | *
74 | * @return $this
75 | */
76 | public function removeIndent() {
77 | $this->children(function (Node $node) {
78 | return !($node instanceof CommentNode);
79 | })->remove();
80 | return $this;
81 | }
82 |
83 | /**
84 | * Add indent to comment.
85 | *
86 | * @param string $whitespace
87 | * Additional whitespace to add.
88 | * @return $this
89 | */
90 | public function addIndent($whitespace) {
91 | $has_indent = $this->children(function (Node $node) {
92 | return !($node instanceof CommentNode);
93 | })->count() > 0;
94 | if ($has_indent) {
95 | $this->children(Filter::isInstanceOf('\Pharborist\WhitespaceNode'))->each(function (WhitespaceNode $ws_node) use ($whitespace) {
96 | $ws_node->setText($ws_node->getText() . $whitespace);
97 | });
98 | }
99 | else {
100 | $this->children()->before(Token::whitespace($whitespace));
101 | }
102 | return $this;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/tests/FunctionDeclarationNodeTest.php:
--------------------------------------------------------------------------------
1 | cloneAsMethodOf($class);
15 | $this->assertTrue($class->hasMethod('sing_goofy_song'));
16 | }
17 |
18 | public function testName() {
19 | /** @var \Pharborist\Functions\FunctionDeclarationNode $func */
20 | $func = Parser::parseSnippet('function hello() {}');
21 | $this->assertEquals('hello', $func->getName()->getText());
22 | $func->setName('test');
23 | $this->assertEquals('test', $func->getName()->getText());
24 | }
25 |
26 | public function testReference() {
27 | /** @var \Pharborist\Functions\FunctionDeclarationNode $func */
28 | $func = Parser::parseSnippet('function hello() {}');
29 | $this->assertNull($func->getReference());
30 | $func->setReference(TRUE);
31 | $this->assertNotNull($func->getReference());
32 | $this->assertEquals('&', $func->getReference()->getText());
33 | $this->assertEquals('function &hello() {}', $func->getText());
34 | $func->setReference(FALSE);
35 | $this->assertNull($func->getReference());
36 | $this->assertEquals('function hello() {}', $func->getText());
37 | }
38 |
39 | public function testReturnType() {
40 | /** @var \Pharborist\Functions\FunctionDeclarationNode $func */
41 | $func = Parser::parseSnippet('function hello() {}');
42 | $this->assertFalse($func->hasReturnTypes());
43 | $this->assertEquals(['void'], $func->getReturnTypes());
44 |
45 | $func = Parser::parseSnippet('/** @return */ function hello() {}');
46 | $this->assertFalse($func->hasReturnTypes());
47 | $this->assertEquals(['void'], $func->getReturnTypes());
48 |
49 | $func = Parser::parseSnippet('/** @return void */ function hello() {}');
50 | $this->assertTrue($func->hasReturnTypes());
51 | $this->assertEquals(['void'], $func->getReturnTypes());
52 |
53 | $func = Parser::parseSnippet('/** @return string */ function hello() {}');
54 | $this->assertTrue($func->hasReturnTypes());
55 | $this->assertEquals(['string'], $func->getReturnTypes());
56 |
57 | $func = Parser::parseSnippet('/** @return string|int */ function hello() {}');
58 | $this->assertTrue($func->hasReturnTypes());
59 | $this->assertEquals(['string', 'int'], $func->getReturnTypes());
60 | }
61 |
62 | public function testMatchReflector() {
63 | $function = FunctionDeclarationNode::create('array_walk');
64 | $reflector = new \ReflectionFunction('array_walk');
65 | // $function->getReference() should return a TokenNode or NULL, which will
66 | // loosely evaluate to TRUE or FALSE
67 | $this->assertEquals($function->getReference(), $reflector->returnsReference());
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Namespaces/NamespaceNode.php:
--------------------------------------------------------------------------------
1 | name;
71 | }
72 |
73 | /**
74 | * @return StatementBlockNode
75 | */
76 | public function getBody() {
77 | return $this->body;
78 | }
79 |
80 | /**
81 | * Determine if the node belongs to this namespace.
82 | *
83 | * @param ClassNode|InterfaceNode|TraitNode|FunctionDeclarationNode|ConstantDeclarationNode $node
84 | * Node to test if owned by namespace.
85 | *
86 | * @return bool
87 | */
88 | public function owns($node) {
89 | return $this === $node->getName()->getNamespace();
90 | }
91 |
92 | /**
93 | * Get use declarations.
94 | *
95 | * @return UseDeclarationNode[]|\Pharborist\NodeCollection
96 | * Use declarations.
97 | */
98 | public function getUseDeclarations() {
99 | return $this->body->getUseDeclarations();
100 | }
101 |
102 | /**
103 | * Return mapping of class names to fully qualified names.
104 | *
105 | * @return array
106 | * Associative array of namespace alias to fully qualified names.
107 | */
108 | public function getClassAliases() {
109 | return $this->body->getClassAliases();
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/TokenNode.php:
--------------------------------------------------------------------------------
1 | type = $type;
55 | $this->text = $text;
56 | $this->filename = $filename;
57 | $this->lineNo = $lineNo;
58 | $this->newlineCount = $newlineCount;
59 | $this->colNo = $colNo;
60 | $this->byteOffset = $byteOffset;
61 | }
62 |
63 | /**
64 | * @return string
65 | */
66 | public function getFilename() {
67 | return $this->filename;
68 | }
69 |
70 | /**
71 | * @return int
72 | */
73 | public function getLineNumber() {
74 | return $this->lineNo;
75 | }
76 |
77 | /**
78 | * @return int
79 | */
80 | public function getNewlineCount() {
81 | return $this->newlineCount;
82 | }
83 |
84 | /**
85 | * @return int
86 | */
87 | public function getColumnNumber() {
88 | return $this->colNo;
89 | }
90 |
91 | /**
92 | * @return int
93 | */
94 | public function getByteOffset() {
95 | return $this->byteOffset;
96 | }
97 |
98 | /**
99 | * @return int
100 | */
101 | public function getByteLength() {
102 | return strlen($this->text);
103 | }
104 |
105 | /**
106 | * @return int
107 | */
108 | public function getType() {
109 | return $this->type;
110 | }
111 |
112 | /**
113 | * @param int $type
114 | * @return string
115 | */
116 | public static function typeName($type) {
117 | if (is_string($type)) {
118 | return $type;
119 | }
120 | else {
121 | return token_name($type);
122 | }
123 | }
124 |
125 | /**
126 | * @return string
127 | */
128 | public function getTypeName() {
129 | return self::typeName($this->type);
130 | }
131 |
132 | /**
133 | * @return string
134 | */
135 | public function getText() {
136 | return $this->text;
137 | }
138 |
139 | /**
140 | * @param string $text
141 | * @return $this
142 | */
143 | public function setText($text) {
144 | $this->text = $text;
145 | return $this;
146 | }
147 |
148 | /**
149 | * @return string
150 | */
151 | public function __toString() {
152 | return $this->text;
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/Functions/ArgumentTrait.php:
--------------------------------------------------------------------------------
1 | arguments;
23 | }
24 |
25 | /**
26 | * @return NodeCollection|ExpressionNode[]
27 | */
28 | public function getArguments() {
29 | return $this->arguments->getItems();
30 | }
31 |
32 | /**
33 | * @param mixed $argument
34 | * The argument to prepend. Can be an ExpressionNode or a scalar value,
35 | * which will be converted to an expression.
36 | *
37 | * @return $this
38 | *
39 | * @throws \InvalidArgumentException
40 | */
41 | public function appendArgument($argument) {
42 | if (is_scalar($argument)) {
43 | $argument = Node::fromValue($argument);
44 | }
45 |
46 | if ($argument instanceof ExpressionNode) {
47 | /** @var Node $argument */
48 | $this->arguments->appendItem($argument);
49 | }
50 | else {
51 | throw new \InvalidArgumentException();
52 | }
53 |
54 | return $this;
55 | }
56 |
57 | /**
58 | * @param mixed $argument
59 | * The argument to prepend. Can be an ExpressionNode or a scalar value,
60 | * which will be converted to an expression.
61 | *
62 | * @return $this
63 | *
64 | * @throws \InvalidArgumentException
65 | */
66 | public function prependArgument($argument) {
67 | if (is_scalar($argument)) {
68 | $argument = Node::fromValue($argument);
69 | }
70 |
71 | if ($argument instanceof ExpressionNode) {
72 | /** @var Node $argument */
73 | $this->arguments->prependItem($argument);
74 | }
75 | else {
76 | throw new \InvalidArgumentException();
77 | }
78 |
79 | return $this;
80 | }
81 |
82 | /**
83 | * Insert argument before argument at index.
84 | *
85 | * @param mixed $argument
86 | * The argument to insert. Can be an ExpressionNode or a scalar value,
87 | * which will be converted to an expression.
88 | * @param int $index
89 | * Position to insert argument at.
90 | * @throws \OutOfBoundsException
91 | * Index out of bounds.
92 | * @throws \InvalidArgumentException
93 | *
94 | * @return $this
95 | */
96 | public function insertArgument($argument, $index) {
97 | if (is_scalar($argument)) {
98 | $argument = Node::fromValue($argument);
99 | }
100 |
101 | if ($argument instanceof ExpressionNode) {
102 | /** @var Node $argument */
103 | $this->arguments->insertItem($argument, $index);
104 | }
105 | else {
106 | throw new \InvalidArgumentException();
107 | }
108 |
109 | return $this;
110 | }
111 |
112 | /**
113 | * Remove all arguments.
114 | *
115 | * @return $this
116 | */
117 | public function clearArguments() {
118 | $this->arguments->clear();
119 | return $this;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/tests/ParameterTraitTest.php:
--------------------------------------------------------------------------------
1 | assertTrue($function->hasParameter('a'));
16 | $this->assertTrue($function->hasParameter('$a'));
17 | $this->assertTrue($function->hasParameter($function->getParameterAtIndex(0)));
18 | $this->assertFalse($function->hasParameter('b'));
19 | $blorf = ParameterNode::create('$blorf');
20 | $this->assertFalse($function->hasParameter($blorf));
21 |
22 | $this->assertTrue($function->hasParameter('a', 'stdClass'));
23 | $this->assertFalse($function->hasParameter('a', 'Node'));
24 |
25 | $this->assertFalse($function->hasRequiredParameter('a', 'stdClass'));
26 | $this->assertTrue($function->hasOptionalParameter('a', 'stdClass'));
27 |
28 | $function->getParameterAtIndex(0)->setValue(NULL);
29 | $this->assertTrue($function->hasRequiredParameter('a', 'stdClass'));
30 | $this->assertFalse($function->hasOptionalParameter('a', 'stdClass'));
31 | }
32 |
33 | public function testGetParameters() {
34 | /** @var \Pharborist\Functions\FunctionDeclarationNode $function */
35 | $function = Parser::parseSnippet('function foo($a, $a = 1, $b, $c) {}');
36 | $parameters = $function->getParameters();
37 | $a = $parameters['a'];
38 | $this->assertEquals('$a = 1', $a->getText());
39 | $this->assertEquals('$a', $parameters[0]);
40 | $this->assertTrue(isset($parameters[2]));
41 | $this->assertTrue(isset($parameters['b']));
42 | $this->assertEquals('$b', $parameters['b']);
43 | $this->assertEquals('$c', $parameters[3]);
44 | }
45 |
46 | /**
47 | * @depends testHasParameter
48 | */
49 | public function testAppendParameter() {
50 | /** @var \Pharborist\Functions\FunctionDeclarationNode $function */
51 | $function = Parser::parseSnippet('function baz() {}');
52 |
53 | $function->appendParameter(ParameterNode::create('$neo'));
54 | $this->assertTrue($function->hasParameter('neo'));
55 |
56 | $function->appendParameter(function(FunctionDeclarationNode $function) {
57 | return ParameterNode::create('$trinity');
58 | });
59 | $this->assertTrue($function->hasParameter('trinity'));
60 | }
61 |
62 | /**
63 | * @expectedException \InvalidArgumentException
64 | */
65 | public function testHasParamterInvalidArgumentException() {
66 | Parser::parseSnippet('function foo($bar) {}')->hasParameter(1);
67 | }
68 |
69 | /**
70 | * @requires PHP 5.6
71 | */
72 | public function testIsVariadic() {
73 | $doc = <<<'END'
74 | children(Filter::isFunction('foo'))->get(0);
80 | $this->assertTrue($func->isVariadic());
81 |
82 | $doc = <<<'END'
83 | children(Filter::isFunction('baz'))->get(0);
88 | $this->assertFalse($func->isVariadic());
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/tests/ArgumentTraitTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf('\Pharborist\Functions\FunctionCallNode', $call);
13 | $this->assertCount(0, $call->getArguments());
14 |
15 | $call->appendArgument(1)->appendArgument('hohoho');
16 | $arguments = $call->getArguments();
17 | $this->assertCount(2, $arguments);
18 | $this->assertInstanceOf('\Pharborist\Types\IntegerNode', $arguments[0]);
19 | $this->assertInstanceof('\Pharborist\Types\StringNode', $arguments[1]);
20 |
21 | $pi = FloatNode::fromValue(3.141);
22 | $call->appendArgument($pi);
23 | $this->assertSame($pi, $call->getArguments()->get(2));
24 | }
25 |
26 | public function testPrependArgument() {
27 | /** @var \Pharborist\Functions\FunctionCallNode $call */
28 | $call = Parser::parseExpression('foo()');
29 |
30 | $call->prependArgument('wozwoz');
31 | $arguments = $call->getArguments();
32 | $this->assertCount(1, $arguments);
33 | $this->assertInstanceOf('\Pharborist\Types\StringNode', $arguments[0]);
34 |
35 | $bazbaz = StringNode::fromValue('bazbaz');
36 | $call->prependArgument($bazbaz);
37 | $arguments = $call->getArguments();
38 | $this->assertCount(2, $arguments);
39 | $this->assertSame($bazbaz, $arguments[0]);
40 | }
41 |
42 | /**
43 | * @expectedException \InvalidArgumentException
44 | */
45 | public function testPrependInvalidArgument() {
46 | /** @var \Pharborist\Functions\FunctionCallNode $call */
47 | $call = Parser::parseExpression('foo()');
48 | $call->prependArgument(NULL);
49 | }
50 |
51 | /**
52 | * @expectedException \InvalidArgumentException
53 | */
54 | public function testAppendInvalidArgument() {
55 | /** @var \Pharborist\Functions\FunctionCallNode $call */
56 | $call = Parser::parseExpression('foo()');
57 | $call->appendArgument(NULL);
58 | }
59 |
60 | public function testInsertArgument() {
61 | /** @var \Pharborist\Functions\FunctionCallNode $call */
62 | $call = Parser::parseExpression('foo()');
63 | $call->insertArgument('a', 0);
64 | $arguments = $call->getArguments();
65 | $this->assertCount(1, $arguments);
66 | $this->assertInstanceOf('\Pharborist\Types\StringNode', $arguments[0]);
67 | }
68 |
69 | /**
70 | * @expectedException \InvalidArgumentException
71 | */
72 | public function testInsertInvalidArgument() {
73 | /** @var \Pharborist\Functions\FunctionCallNode $call */
74 | $call = Parser::parseExpression('foo()');
75 | $call->insertArgument(NULL, 0);
76 | }
77 |
78 | /**
79 | * @expectedException \OutOfBoundsException
80 | */
81 | public function testInsertInvalidIndex() {
82 | /** @var \Pharborist\Functions\FunctionCallNode $call */
83 | $call = Parser::parseExpression('foo()');
84 | $call->insertArgument(42, 1);
85 | }
86 |
87 | public function testClearArguments() {
88 | /** @var \Pharborist\Functions\FunctionCallNode $call */
89 | $call = Parser::parseExpression('foo(42)');
90 | $this->assertCount(1, $call->getArguments());
91 | $call->clearArguments();
92 | $this->assertTrue($call->getArguments()->isEmpty());
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/tests/AnonymousFunctionNodeTest.php:
--------------------------------------------------------------------------------
1 | assertNull($func->getReference());
11 | $func->setReference(TRUE);
12 | $this->assertNotNull($func->getReference());
13 | $this->assertEquals('&', $func->getReference()->getText());
14 | $this->assertEquals('function &() {}', $func->getText());
15 |
16 | $func->setReference(FALSE);
17 | $this->assertNull($func->getReference());
18 | $this->assertEquals('function () {}', $func->getText());
19 | }
20 |
21 | public function testLexicalVariables() {
22 | /** @var AnonymousFunctionNode $func */
23 | $func = Parser::parseExpression('function () {}');
24 | $func->appendLexicalVariable(Token::variable('$a'));
25 | $this->assertTrue($func->hasLexicalVariables());
26 | $lexical_variables = $func->getLexicalVariables();
27 | $this->assertCount(1, $func->getLexicalVariables());
28 | $this->assertEquals('$a', $lexical_variables[0]->getText());
29 | $this->assertEquals($lexical_variables, $func->getLexicalVariableList()->getItems());
30 | $this->assertEquals('function () use ($a) {}', $func->getText());
31 | $this->assertEquals('use', $func->getLexicalUse()->getText());
32 | $this->assertEquals('(', $func->getLexicalOpenParen());
33 | $this->assertNotSame($func->getOpenParen(), $func->getLexicalOpenParen());
34 | $this->assertEquals(')', $func->getLexicalCloseParen());
35 | $this->assertNotSame($func->getCloseParen(), $func->getLexicalCloseParen());
36 |
37 | $func->prependLexicalVariable(Token::variable('$before'));
38 | $lexical_variables = $func->getLexicalVariables();
39 | $this->assertCount(2, $func->getLexicalVariables());
40 | $this->assertEquals('$before', $lexical_variables[0]->getText());
41 | $this->assertEquals('$a', $lexical_variables[1]->getText());
42 | $this->assertEquals($lexical_variables, $func->getLexicalVariableList()->getItems());
43 | $this->assertEquals('function () use ($before, $a) {}', $func->getText());
44 |
45 | $func->insertLexicalVariable(Token::variable('$middle'), 1);
46 | $lexical_variables = $func->getLexicalVariables();
47 | $this->assertCount(3, $func->getLexicalVariables());
48 | $this->assertEquals('$before', $lexical_variables[0]->getText());
49 | $this->assertEquals('$middle', $lexical_variables[1]->getText());
50 | $this->assertEquals('$a', $lexical_variables[2]->getText());
51 | $this->assertEquals($lexical_variables, $func->getLexicalVariableList()->getItems());
52 | $this->assertEquals('function () use ($before, $middle, $a) {}', $func->getText());
53 |
54 | $func->clearLexicalVariables();
55 | $this->assertFalse($func->hasLexicalVariables());
56 | $this->assertEquals('function () {}', $func->getText());
57 | }
58 |
59 | /**
60 | * @expectedException \OutOfBoundsException
61 | */
62 | public function testInsertNegativeIndex() {
63 | /** @var AnonymousFunctionNode $func */
64 | $func = Parser::parseExpression('function () use ($b) {}');
65 | $func->insertLexicalVariable(Token::variable('$a'), -1);
66 | }
67 |
68 | /**
69 | * @expectedException \OutOfBoundsException
70 | */
71 | public function testInsertOutOfBoundsEmpty() {
72 | /** @var AnonymousFunctionNode $func */
73 | $func = Parser::parseExpression('function () {}');
74 | $func->insertLexicalVariable(Token::variable('$a'), 1);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/CommentNode.php:
--------------------------------------------------------------------------------
1 | commentType = self::DOC;
35 | }
36 | else {
37 | $prefix = substr($prefix, 0, 2);
38 | if ($prefix === self::BLOCK) {
39 | $this->commentType = self::BLOCK;
40 | }
41 | elseif ($prefix === self::SINGLE) {
42 | $this->commentType = self::SINGLE;
43 | }
44 | elseif ($prefix[0] === self::HASH) {
45 | $this->commentType = self::HASH;
46 | }
47 | }
48 | }
49 |
50 | /**
51 | * Create line comment.
52 | *
53 | * @param string $comment
54 | * Comment without leading prefix.
55 | * @return CommentNode|LineCommentBlockNode
56 | */
57 | public static function create($comment) {
58 | $comment = trim($comment);
59 | $nl_count = substr_count($comment, "\n");
60 | if ($nl_count > 1) {
61 | return LineCommentBlockNode::create($comment);
62 | }
63 | else {
64 | return new CommentNode(T_COMMENT, '// ' . $comment . "\n");
65 | }
66 | }
67 |
68 | /**
69 | * @return string
70 | */
71 | public function getCommentType() {
72 | return $this->commentType;
73 | }
74 |
75 | /**
76 | * @return bool
77 | */
78 | public function isLineComment() {
79 | return $this->commentType === self::SINGLE || $this->commentType === self::HASH;
80 | }
81 |
82 | /**
83 | * @return string
84 | */
85 | public function getCommentText() {
86 | switch ($this->commentType) {
87 | case self::SINGLE:
88 | case self::HASH:
89 | $comment_text = rtrim(substr($this->text, strlen($this->commentType)));
90 | if ($comment_text[0] === ' ') {
91 | $comment_text = substr($comment_text, 1);
92 | }
93 | return $comment_text;
94 | case self::DOC:
95 | $lines = explode("\n", $this->text);
96 | if (count($lines) === 1) {
97 | return trim(substr($this->text, 3, -2));
98 | }
99 | else {
100 | $last_index = count($lines) - 1;
101 | unset($lines[0]); // ignore first line
102 | unset($lines[$last_index]); // ignore last line
103 | $comment = '';
104 | $first = TRUE;
105 | foreach ($lines as $line) {
106 | $line = trim($line);
107 | if ($line !== '' && $line[0] === '*') {
108 | if (!$first) {
109 | $comment .= "\n";
110 | }
111 | else {
112 | $first = FALSE;
113 | }
114 | $comment .= substr($line, 2);
115 | }
116 | }
117 | return $comment;
118 | }
119 | case self::BLOCK:
120 | return trim(substr($this->text, 2, -2));
121 | default:
122 | throw new \LogicException("Unhandled comment type in CommentNode::getCommentText()");
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/Namespaces/UseDeclarationStatementNode.php:
--------------------------------------------------------------------------------
1 | useFunction || $this->useConst) {
50 | return FALSE;
51 | }
52 | if ($class_name) {
53 | foreach ($this->getDeclarations() as $declaration) {
54 | if ($declaration->getName()->getPath() === $class_name) {
55 | return TRUE;
56 | }
57 | }
58 | return FALSE;
59 | }
60 | else {
61 | return TRUE;
62 | }
63 | }
64 |
65 | /**
66 | * Test whether use declaration imports a function.
67 | *
68 | * @param string|NULL $function_name
69 | * (Optional) Function name to check if being imported by use statement.
70 | *
71 | * @return bool
72 | * TRUE if this use declaration imports function.
73 | */
74 | public function importsFunction($function_name = NULL) {
75 | if (!$this->useFunction) {
76 | return FALSE;
77 | }
78 | if ($function_name) {
79 | foreach ($this->getDeclarations() as $declaration) {
80 | if ($declaration->getName()->getPath() === $function_name) {
81 | return TRUE;
82 | }
83 | }
84 | return FALSE;
85 | }
86 | else {
87 | return TRUE;
88 | }
89 | }
90 |
91 | /**
92 | * Test whether use declaration imports a constant.
93 | *
94 | * @param string|NULL $const_name
95 | * (Optional) Constant name to check if being imported by use statement.
96 | *
97 | * @return bool
98 | * TRUE if this use declaration imports constant.
99 | */
100 | public function importsConst($const_name = NULL) {
101 | if (!$this->useConst) {
102 | return FALSE;
103 | }
104 | if ($const_name) {
105 | foreach ($this->getDeclarations() as $declaration) {
106 | if ($declaration->getName()->getPath() === $const_name) {
107 | return TRUE;
108 | }
109 | }
110 | return FALSE;
111 | }
112 | else {
113 | return TRUE;
114 | }
115 | }
116 |
117 | /**
118 | * @var CommaListNode
119 | */
120 | protected $declarations;
121 |
122 | /**
123 | * @return CommaListNode
124 | */
125 | public function getDeclarationList() {
126 | return $this->declarations;
127 | }
128 |
129 | /**
130 | * @return NodeCollection|UseDeclarationNode[]
131 | */
132 | public function getDeclarations() {
133 | return $this->declarations->getItems();
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/ArrayLookupNode.php:
--------------------------------------------------------------------------------
1 | addChild($array, 'array');
37 | $node->addChild(Token::openBracket());
38 | /** @var Node $key */
39 | $node->addChild($key, 'key');
40 | $node->addChild(Token::closeBracket());
41 | return $node;
42 | }
43 |
44 | /**
45 | * @return Node
46 | */
47 | public function getArray() {
48 | return $this->array;
49 | }
50 |
51 | /**
52 | * @return \Pharborist\Node[]
53 | */
54 | public function getKeys() {
55 | $keys = [];
56 |
57 | if ($this->key) {
58 | $keys[] = clone $this->key;
59 | }
60 | if ($this->array instanceof ArrayLookupNode) {
61 | $keys = array_merge($this->array->getKeys(), $keys);
62 | }
63 |
64 | return $keys;
65 | }
66 |
67 | /**
68 | * Returns a specific key in the lookup.
69 | *
70 | * @param integer $index
71 | * The index of the key to return.
72 | *
73 | * @return \Pharborist\Node
74 | *
75 | * @throws
76 | * \InvalidArgumentException if $index is not an integer.
77 | * \OutOfBoundsException if $index is less than zero or greater than the
78 | * number of keys in the lookup.
79 | */
80 | public function getKey($index = 0) {
81 | $keys = $this->getKeys();
82 |
83 | if (!is_integer($index)) {
84 | throw new \InvalidArgumentException();
85 | }
86 | if ($index < 0 || $index >= count($keys)) {
87 | throw new \OutOfBoundsException();
88 | }
89 | return $keys[$index];
90 | }
91 |
92 | /**
93 | * Returns TRUE if all keys in the lookup are scalar. So a lookup like
94 | * $foo['bar']['baz'][0] will be TRUE, but $foo[$bar]['baz'][0] won't.
95 | *
96 | * @return boolean
97 | */
98 | public function hasScalarKeys() {
99 | if ($this->key instanceof ScalarNode) {
100 | return $this->array instanceof ArrayLookupNode ? $this->array->hasScalarKeys() : TRUE;
101 | }
102 | else {
103 | return FALSE;
104 | }
105 | }
106 |
107 | /**
108 | * Returns every key in the lookup. For example, $foo['bar']['baz'][5] will
109 | * return ['bar', 'baz', 5].
110 | *
111 | * @return mixed[]
112 | *
113 | * @throws \DomainException if the lookup contains any non-scalar keys.
114 | */
115 | public function extractKeys() {
116 | if (!$this->hasScalarKeys()) {
117 | throw new \DomainException('Cannot extract non-scalar keys from array lookup ' . $this);
118 | }
119 | return array_map(function(ScalarNode $key) { return $key->toValue(); }, $this->getKeys());
120 | }
121 |
122 | /**
123 | * Returns the root of the lookup.
124 | *
125 | * For example, given an expression like $foo['bar']['baz'],
126 | * this method will return a VariableNode for $foo.
127 | *
128 | * @return Node
129 | */
130 | public function getRootArray() {
131 | return $this->array instanceof ArrayLookupNode ? $this->array->getRootArray() : $this->array;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------