Overview

Namespaces

  • Pharborist
    • Constants
    • ControlStructures
    • Exceptions
    • Functions
    • Generators
    • Namespaces
    • Objects
    • Operators
    • Types
    • Variables

Classes

  • Pharborist\Variables\CompoundVariableNode
  • Pharborist\Variables\GlobalStatementNode
  • Pharborist\Variables\ReferenceVariableNode
  • Pharborist\Variables\StaticVariableNode
  • Pharborist\Variables\StaticVariableStatementNode
  • Pharborist\Variables\VariableNode
  • Pharborist\Variables\VariableVariableNode

Interfaces

  • Pharborist\Variables\VariableExpressionNode
  • Overview
  • Namespace
  • Class
  1: <?php
  2: namespace Pharborist;
  3: 
  4: /**
  5:  * A node that has children.
  6:  */
  7: abstract class ParentNode extends Node implements ParentNodeInterface {
  8:   /**
  9:    * @var Node
 10:    */
 11:   protected $head;
 12: 
 13:   /**
 14:    * @var Node
 15:    */
 16:   protected $tail;
 17: 
 18:   /**
 19:    * @var int
 20:    */
 21:   protected $childCount = 0;
 22: 
 23:   protected function getProperties() {
 24:     $properties = get_object_vars($this);
 25:     unset($properties['head']);
 26:     unset($properties['tail']);
 27:     unset($properties['childCount']);
 28:     unset($properties['parent']);
 29:     unset($properties['previous']);
 30:     unset($properties['next']);
 31:     return $properties;
 32:   }
 33: 
 34:   public function isEmpty() {
 35:     return $this->childCount === 0;
 36:   }
 37: 
 38:   public function childCount() {
 39:     return $this->childCount;
 40:   }
 41: 
 42:   public function firstChild() {
 43:     return $this->head;
 44:   }
 45: 
 46:   public function lastChild() {
 47:     return $this->tail;
 48:   }
 49: 
 50:   /**
 51:    * Get children that are instance of class.
 52:    * @param string $class_name
 53:    * @return Node[]
 54:    */
 55:   protected function childrenByInstance($class_name) {
 56:     $matches = [];
 57:     $child = $this->head;
 58:     while ($child) {
 59:       if ($child instanceof $class_name) {
 60:         $matches[] = $child;
 61:       }
 62:       $child = $child->next;
 63:     }
 64:     return $matches;
 65:   }
 66: 
 67:   public function children(callable $callback = NULL) {
 68:     $matches = [];
 69:     $child = $this->head;
 70:     while ($child) {
 71:       if ($callback === NULL || $callback($child)) {
 72:         $matches[] = $child;
 73:       }
 74:       $child = $child->next;
 75:     }
 76:     return new NodeCollection($matches, FALSE);
 77:   }
 78: 
 79:   public function clear() {
 80:     $this->head = $this->tail = NULL;
 81:   }
 82: 
 83:   /**
 84:    * Called when a child has been inserted into the node.
 85:    * @param Node $node
 86:    */
 87:   protected function childInserted(Node $node) {
 88:     // Do nothing by default.
 89:   }
 90: 
 91:   /**
 92:    * Prepend a child to node.
 93:    * @param Node $node
 94:    * @return $this
 95:    */
 96:   protected function prependChild(Node $node) {
 97:     if ($this->head === NULL) {
 98:       $this->childCount++;
 99:       $node->parent = $this;
100:       $node->previous = NULL;
101:       $node->next = NULL;
102:       $this->head = $this->tail = $node;
103:       $this->childInserted($node);
104:     }
105:     else {
106:       $this->insertBeforeChild($this->head, $node);
107:       $this->head = $node;
108:     }
109:     return $this;
110:   }
111: 
112:   public function prepend($nodes) {
113:     if ($nodes instanceof Node) {
114:       $this->prependChild($nodes);
115:     }
116:     elseif ($nodes instanceof NodeCollection) {
117:       foreach ($nodes->reverse() as $node) {
118:         $this->prependChild($node);
119:       }
120:     }
121:     elseif (is_array($nodes)) {
122:       foreach (array_reverse($nodes) as $node) {
123:         $this->prependChild($node);
124:       }
125:     }
126:     else {
127:       throw new \InvalidArgumentException();
128:     }
129:     return $this;
130:   }
131: 
132:   /**
133:    * Append a child to node.
134:    * @param Node $node
135:    * @return $this
136:    */
137:   protected function appendChild(Node $node) {
138:     if ($this->tail === NULL) {
139:       $this->prependChild($node);
140:     }
141:     else {
142:       $this->insertAfterChild($this->tail, $node);
143:       $this->tail = $node;
144:     }
145:     return $this;
146:   }
147: 
148:   /**
149:    * Add a child to node.
150:    *
151:    * Internal use only, used by parser when building a node.
152:    *
153:    * @param Node $node
154:    * @param string $property_name
155:    * @return $this
156:    */
157:   public function addChild(Node $node, $property_name = NULL) {
158:     $this->appendChild($node);
159:     if ($property_name !== NULL) {
160:       $this->{$property_name} = $node;
161:     }
162:     return $this;
163:   }
164: 
165:   /**
166:    * Add children to node.
167:    *
168:    * Internal use only, used by parser when building a node.
169:    *
170:    * @param Node[] $children
171:    */
172:   public function addChildren(array $children) {
173:     foreach ($children as $child) {
174:       $this->appendChild($child);
175:     }
176:   }
177: 
178:   /**
179:    * Merge another parent node into this node.
180:    * @param ParentNode $node
181:    */
182:   public function mergeNode(ParentNode $node) {
183:     $child = $node->head;
184:     while ($child) {
185:       $next = $child->next;
186:       $this->appendChild($child);
187:       $child = $next;
188:     }
189:     foreach ($node->getProperties() as $name => $value) {
190:       $this->{$name} = $value;
191:     }
192:   }
193: 
194:   public function append($nodes) {
195:     if ($nodes instanceof Node) {
196:       $this->appendChild($nodes);
197:     }
198:     elseif ($nodes instanceof NodeCollection || is_array($nodes)) {
199:       foreach ($nodes as $node) {
200:         $this->appendChild($node);
201:       }
202:     }
203:     else {
204:       throw new \InvalidArgumentException();
205:     }
206:     return $this;
207:   }
208: 
209:   /**
210:    * Insert a node before a child.
211:    * @param Node $child
212:    * @param Node $node
213:    * @return $this
214:    */
215:   protected function insertBeforeChild(Node $child, Node $node) {
216:     $this->childCount++;
217:     $node->parent = $this;
218:     if ($child->previous === NULL) {
219:       $this->head = $node;
220:     }
221:     else {
222:       $child->previous->next = $node;
223:     }
224:     $node->previous = $child->previous;
225:     $node->next = $child;
226:     $child->previous = $node;
227:     $this->childInserted($node);
228:     return $this;
229:   }
230: 
231:   /**
232:    * Insert a node after a child.
233:    * @param Node $child
234:    * @param Node $node
235:    * @return $this
236:    */
237:   protected function insertAfterChild(Node $child, Node $node) {
238:     $this->childCount++;
239:     $node->parent = $this;
240:     if ($child->next === NULL) {
241:       $this->tail = $node;
242:     }
243:     else {
244:       $child->next->previous = $node;
245:     }
246:     $node->previous = $child;
247:     $node->next = $child->next;
248:     $child->next = $node;
249:     $this->childInserted($node);
250:     return $this;
251:   }
252: 
253:   /**
254:    * Remove a child node.
255:    * @param Node $child
256:    * @return $this
257:    */
258:   protected function removeChild(Node $child) {
259:     $this->childCount--;
260:     foreach ($this->getProperties() as $name => $value) {
261:       if ($child === $value) {
262:         $this->{$name} = NULL;
263:         break;
264:       }
265:     }
266:     if ($child->previous === NULL) {
267:       $this->head = $child->next;
268:     }
269:     else {
270:       $child->previous->next = $child->next;
271:     }
272:     if ($child->next === NULL) {
273:       $this->tail = $child->previous;
274:     }
275:     else {
276:       $child->next->previous = $child->previous;
277:     }
278:     $child->parent = NULL;
279:     $child->previous = NULL;
280:     $child->next = NULL;
281:     return $this;
282:   }
283: 
284:   /**
285:    * Replace a child node.
286:    * @param Node $child
287:    * @param Node $replacement
288:    * @return $this
289:    */
290:   protected function replaceChild(Node $child, Node $replacement) {
291:     foreach ($this->getProperties() as $name => $value) {
292:       if ($child === $value) {
293:         $this->{$name} = $replacement;
294:         break;
295:       }
296:     }
297:     $replacement->parent = $this;
298:     $replacement->previous = $child->previous;
299:     $replacement->next = $child->next;
300:     if ($child->previous === NULL) {
301:       $this->head = $replacement;
302:     }
303:     else {
304:       $child->previous->next = $replacement;
305:     }
306:     if ($child->next === NULL) {
307:       $this->tail = $replacement;
308:     }
309:     else {
310:       $child->next->previous = $replacement;
311:     }
312:     $child->parent = NULL;
313:     $child->previous = NULL;
314:     $child->next = NULL;
315:     return $this;
316:   }
317: 
318:   /**
319:    * {@inheritDoc}
320:    * @return TokenNode
321:    */
322:   public function firstToken() {
323:     $head = $this->head;
324:     while ($head instanceof ParentNode) {
325:       $head = $head->head;
326:     }
327:     return $head;
328:   }
329: 
330:   /**
331:    * {@inheritDoc}
332:    * @return TokenNode
333:    */
334:   public function lastToken() {
335:     $tail = $this->tail;
336:     while ($tail instanceof ParentNode) {
337:       $tail = $tail->tail;
338:     }
339:     return $tail;
340:   }
341: 
342:   public function has(callable $callback) {
343:     $child = $this->head;
344:     while ($child) {
345:       if ($child instanceof ParentNode && $child->has($callback)) {
346:         return TRUE;
347:       }
348:       elseif ($callback($child)) {
349:         return TRUE;
350:       }
351:       $child = $child->next;
352:     }
353:     return FALSE;
354:   }
355: 
356:   public function isDescendant(Node $node) {
357:     $parent = $node->parent;
358:     while ($parent) {
359:       if ($parent === $this) {
360:         return TRUE;
361:       }
362:       $parent = $parent->parent;
363:     }
364:     return FALSE;
365:   }
366: 
367:   private function _find(&$matches, callable $callback) {
368:     $child = $this->head;
369:     while ($child) {
370:       if ($callback($child)) {
371:         $matches[] = $child;
372:       }
373:       if ($child instanceof ParentNode) {
374:         $child->_find($matches, $callback);
375:       }
376:       $child = $child->next;
377:     }
378:   }
379: 
380:   public function find(callable $callback) {
381:     $matches = [];
382:     $this->_find($matches, $callback);
383:     return new NodeCollection($matches, FALSE);
384:   }
385: 
386:   public function walk(callable $callback) {
387:     $callback($this);
388:     $child = $this->head;
389:     while($child) {
390:       if($child instanceof ParentNode) {
391:         $child->walk($callback);
392:       }
393:       else {
394:         $callback($child);
395:       }
396:       $child = $child->next;
397:     }
398:   }
399: 
400:   public function acceptVisitor(VisitorInterface $visitor) {
401:     $visitor->visit($this);
402:     $child = $this->head;
403:     while($child) {
404:       if($child instanceof ParentNode) {
405:         $child->acceptVisitor($visitor);
406:       }
407:       else {
408:         $visitor->visit($child);
409:       }
410:       $child = $child->next;
411:     }
412:     $visitor->visitEnd($this);
413:   }
414: 
415:   public function getSourcePosition() {
416:     if ($this->head === NULL) {
417:       return $this->parent->getSourcePosition();
418:     }
419:     $child = $this->head;
420:     return $child->getSourcePosition();
421:   }
422: 
423:   public function __clone() {
424:     // Clone does not belong to a parent.
425:     $this->parent = NULL;
426:     $this->previous = NULL;
427:     $this->next = NULL;
428:     $properties = $this->getProperties();
429:     $children = [];
430:     $child = $this->head;
431:     while ($child) {
432:       $key = array_search($child, $properties, TRUE);
433:       if ($key !== FALSE) {
434:         $children[$key] = clone $child;
435:       }
436:       else {
437:         $children[] = clone $child;
438:       }
439:       $child = $child->next;
440:     }
441:     $keys = array_keys($children);
442:     $this->head = empty($children) ? NULL : $children[$keys[0]];
443:     $this->tail = $this->head;
444:     /** @var Node $prev */
445:     $prev = NULL;
446:     foreach ($children as $key => $child) {
447:       if (!is_int($key)) {
448:         $this->{$key} = $child;
449:       }
450:       $this->tail = $child;
451:       $child->parent = $this;
452:       $child->previous = $prev;
453:       if ($prev) {
454:         $prev->next = $child;
455:       }
456:       $prev = $child;
457:     }
458:   }
459: 
460:   public function getText() {
461:     $str = '';
462:     $child = $this->head;
463:     while ($child) {
464:       $str .= $child->getText();
465:       $child = $child->next;
466:     }
467:     return $str;
468:   }
469: 
470:   public function __toString() {
471:     return $this->getText();
472:   }
473: 
474:   /**
475:    * Convert tree into array.
476:    *
477:    * Useful for viewing the tree structure.
478:    */
479:   public function getTree() {
480:     $children = array();
481:     $properties = $this->getProperties();
482:     $child = $this->head;
483:     $i = 0;
484:     while ($child) {
485:       $key = array_search($child, $properties, TRUE);
486:       if (!$key) {
487:         $key = $i;
488:       }
489:       if ($child instanceof ParentNode) {
490:         $children[$key] = $child->getTree();
491:       }
492:       else {
493:         /** @var TokenNode $child */
494:         $children[$key] = array($child->getTypeName() => $child->getText());
495:       }
496:       $child = $child->next;
497:       $i++;
498:     }
499:     $class_name = get_class($this);
500:     return array($class_name => $children);
501:   }
502: }
503: 
Pharborist API documentation generated by ApiGen