. */ private $innerAssignments = []; /** * @param string $type * @param (AFPTreeNode|null)[]|string[]|AFPToken $children * @param int $position */ public function __construct( $type, $children, $position ) { $this->type = $type; $this->children = $children; $this->position = $position; $this->populateInnerAssignments(); } /** * Save in this node all the variable names used in the children, and in this node if it's an * assignment-related node. Note that this doesn't check whether the variable is custom or builtin: * this is already checked when calling setUserVariable. * In case we'll ever need to store other data in a node, or maybe even a Scope object, this could * be moved to a different class which could also re-visit the whole AST. */ private function populateInnerAssignments() { if ( $this->type === self::ATOM ) { return; } if ( $this->type === self::ASSIGNMENT || $this->type === self::INDEX_ASSIGNMENT || $this->type === self::ARRAY_APPEND ) { $this->innerAssignments = [ $this->children[0] ]; } elseif ( $this->type === self::FUNCTION_CALL && in_array( $this->children[0], [ 'set', 'set_var' ] ) && // If unset, parsing will fail when checking arguments isset( $this->children[1] ) ) { $varnameNode = $this->children[1]; if ( $varnameNode->type !== self::ATOM ) { // Shouldn't happen since variable variables are not allowed // @codeCoverageIgnoreStart throw new AFPInternalException( "Got non-atom type {$varnameNode->type} for set_var" ); // @codeCoverageIgnoreEnd } $this->innerAssignments = [ $varnameNode->children->value ]; } // @phan-suppress-next-line PhanTypeSuspiciousNonTraversableForeach ATOM excluded above foreach ( $this->children as $child ) { if ( $child instanceof self ) { $this->innerAssignments = array_merge( $this->innerAssignments, $child->getInnerAssignments() ); } } } /** * @return string[] */ public function getInnerAssignments() : array { return $this->innerAssignments; } /** * @return string * @codeCoverageIgnore */ public function toDebugString() { return implode( "\n", $this->toDebugStringInner() ); } /** * @return array * @codeCoverageIgnore */ private function toDebugStringInner() { if ( $this->type === self::ATOM ) { return [ "ATOM({$this->children->type} {$this->children->value})" ]; } $align = function ( $line ) { return ' ' . $line; }; $lines = [ "{$this->type}" ]; // @phan-suppress-next-line PhanTypeSuspiciousNonTraversableForeach children is array here foreach ( $this->children as $subnode ) { if ( $subnode instanceof AFPTreeNode ) { $sublines = array_map( $align, $subnode->toDebugStringInner() ); } elseif ( is_string( $subnode ) ) { $sublines = [ " {$subnode}" ]; } else { throw new AFPInternalException( "Each node parameter has to be either a node or a string" ); } $lines = array_merge( $lines, $sublines ); } return $lines; } }