320 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			320 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| 
								 | 
							
								<?php /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * This file is part of the Peast package
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * (c) Marco Marchiò <marco.mm89@gmail.com>
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * For the full copyright and license information refer to the LICENSE file
							 | 
						||
| 
								 | 
							
								 * distributed with this source code
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								namespace Peast\Syntax\Node;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use Peast\Syntax\SourceLocation;
							 | 
						||
| 
								 | 
							
								use Peast\Syntax\Position;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Base class for all the nodes generated by Peast.
							 | 
						||
| 
								 | 
							
								 * 
							 | 
						||
| 
								 | 
							
								 * @author Marco Marchiò <marco.mm89@gmail.com>
							 | 
						||
| 
								 | 
							
								 * 
							 | 
						||
| 
								 | 
							
								 * @abstract
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								abstract class Node implements \JSONSerializable
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Map of node properties
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @var array 
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected $propertiesMap = array(
							 | 
						||
| 
								 | 
							
								        "type" => false,
							 | 
						||
| 
								 | 
							
								        "location" => false,
							 | 
						||
| 
								 | 
							
								        "leadingComments" => false,
							 | 
						||
| 
								 | 
							
								        "trailingComments" => false
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Node location in the source code
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @var SourceLocation
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public $location;
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Leading comments array
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @var Comment[]
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected $leadingComments = array();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Trailing comments array
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @var Comment[]
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected $trailingComments = array();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Class constructor
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function __construct()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $this->location = new SourceLocation;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns node type
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @return string
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getType()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $class = explode("\\", get_class($this));
							 | 
						||
| 
								 | 
							
								        return array_pop($class);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Sets leading comments array
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param Comment[] $comments Comments array
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return $this
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function setLeadingComments($comments)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $this->assertArrayOf($comments, "Comment");
							 | 
						||
| 
								 | 
							
								        $this->leadingComments = $comments;
							 | 
						||
| 
								 | 
							
								        return $this;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns leading comments array
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return Comment[]
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getLeadingComments()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->leadingComments;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Sets trailing comments array
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param Comment[] $comments Comments array
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return $this
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function setTrailingComments($comments)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $this->assertArrayOf($comments, "Comment");
							 | 
						||
| 
								 | 
							
								        $this->trailingComments = $comments;
							 | 
						||
| 
								 | 
							
								        return $this;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns trailing comments array
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return Comment[]
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getTrailingComments()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->trailingComments;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns node location in the source code
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @return SourceLocation
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getLocation()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->location;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Sets the start position of the node in the source code
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @param Position $position Start position
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @return $this
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function setStartPosition(Position $position)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $this->location->start = $position;
							 | 
						||
| 
								 | 
							
								        return $this;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Sets the end position of the node in the source code
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @param Position $position Start position
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @return $this
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function setEndPosition(Position $position)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $this->location->end = $position;
							 | 
						||
| 
								 | 
							
								        return $this;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Traverses the current node and all its child nodes using the given
							 | 
						||
| 
								 | 
							
								     * function
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @param callable $fn      Function that will be called on each node
							 | 
						||
| 
								 | 
							
								     * @param array    $options Options array. See Traverser class
							 | 
						||
| 
								 | 
							
								     *                          documentation for available options
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @return $this
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function traverse(callable $fn, $options = array())
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $traverser = new \Peast\Traverser($options);
							 | 
						||
| 
								 | 
							
								        $traverser->addFunction($fn)->traverse($this);
							 | 
						||
| 
								 | 
							
								        return $this;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns a serializable version of the node
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @return array
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    #[\ReturnTypeWillChange]
							 | 
						||
| 
								 | 
							
								    public function jsonSerialize()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $ret = array();
							 | 
						||
| 
								 | 
							
								        $props = \Peast\Syntax\Utils::getNodeProperties($this);
							 | 
						||
| 
								 | 
							
								        foreach ($props as $prop) {
							 | 
						||
| 
								 | 
							
								            $ret[$prop["name"]] = $this->{$prop["getter"]}();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return $ret;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Renders the current node
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @param \Peast\Formatter\Base $formatter Formatter to use for the
							 | 
						||
| 
								 | 
							
								     *                                         rendering
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @return string
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function render(\Peast\Formatter\Base $formatter)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $renderer = new \Peast\Renderer();
							 | 
						||
| 
								 | 
							
								        return $renderer->setFormatter($formatter)->render($this);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Asserts that the given value is an array of defined type
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @param mixed        $params    Value to check
							 | 
						||
| 
								 | 
							
								     * @param string|array $classes   Class or array of classes to check against
							 | 
						||
| 
								 | 
							
								     * @param bool         $allowNull If true, null values are allowed
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @return void
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @codeCoverageIgnore
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected function assertArrayOf($params, $classes, $allowNull = false)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (!is_array($classes)) {
							 | 
						||
| 
								 | 
							
								            $classes = array($classes);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (!is_array($params)) {
							 | 
						||
| 
								 | 
							
								            $this->typeError($params, $classes, $allowNull, true);
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            foreach ($params as $param) {
							 | 
						||
| 
								 | 
							
								                foreach ($classes as $class) {
							 | 
						||
| 
								 | 
							
								                    if ($param === null && $allowNull) {
							 | 
						||
| 
								 | 
							
								                        continue 2;
							 | 
						||
| 
								 | 
							
								                    } else {
							 | 
						||
| 
								 | 
							
								                        $c = "Peast\\Syntax\\Node\\$class";
							 | 
						||
| 
								 | 
							
								                        if ($param instanceof $c) {
							 | 
						||
| 
								 | 
							
								                            continue 2;
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                $this->typeError($param, $classes, $allowNull, true, true);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Asserts that the given value respects the defined type
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @param mixed        $param     Value to check
							 | 
						||
| 
								 | 
							
								     * @param string|array $classes   Class or array of classes to check against
							 | 
						||
| 
								 | 
							
								     * @param bool         $allowNull If true, null values are allowed
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @return void
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @codeCoverageIgnore
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected function assertType($param, $classes, $allowNull = false)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (!is_array($classes)) {
							 | 
						||
| 
								 | 
							
								            $classes = array($classes);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if ($param === null) {
							 | 
						||
| 
								 | 
							
								            if (!$allowNull) {
							 | 
						||
| 
								 | 
							
								                $this->typeError($param, $classes, $allowNull);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            foreach ($classes as $class) {
							 | 
						||
| 
								 | 
							
								                $c = "Peast\\Syntax\\Node\\$class";
							 | 
						||
| 
								 | 
							
								                if ($param instanceof $c) {
							 | 
						||
| 
								 | 
							
								                    return;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            $this->typeError($param, $classes, $allowNull);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Throws an error if the defined type is not supported b
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @param mixed $param        The value to check
							 | 
						||
| 
								 | 
							
								     * @param mixed $allowedTypes Class or array of classes to check against
							 | 
						||
| 
								 | 
							
								     * @param bool  $allowNull    If true, null values are allowed
							 | 
						||
| 
								 | 
							
								     * @param bool  $array        If true, the value must be an array
							 | 
						||
| 
								 | 
							
								     * @param bool  $inArray      If true, the value is an array but the content
							 | 
						||
| 
								 | 
							
								     *                            does not respects the type
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @return void
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @throws \TypeError
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @codeCoverageIgnore
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected function typeError(
							 | 
						||
| 
								 | 
							
								        $param, $allowedTypes, $allowNull = false, $array = false,
							 | 
						||
| 
								 | 
							
								        $inArray = false
							 | 
						||
| 
								 | 
							
								    ) {
							 | 
						||
| 
								 | 
							
								        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
							 | 
						||
| 
								 | 
							
								        $method = $backtrace[2]["class"] . "::" . $backtrace[2]["function"];
							 | 
						||
| 
								 | 
							
								        $msg = "Argument 0 passed to $method must be ";
							 | 
						||
| 
								 | 
							
								        if ($array) {
							 | 
						||
| 
								 | 
							
								            $msg .= "an array of ";
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $msg .= implode(" or ", $allowedTypes);
							 | 
						||
| 
								 | 
							
								        if ($allowNull) {
							 | 
						||
| 
								 | 
							
								            $msg .= " or null";
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (is_object($param)) {
							 | 
						||
| 
								 | 
							
								            $parts = explode("\\", get_class($param));
							 | 
						||
| 
								 | 
							
								            $type = array_pop($parts);
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            $type = gettype($param);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if ($inArray) {
							 | 
						||
| 
								 | 
							
								            $type = "array of $type";
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $msg .= ", $type given";
							 | 
						||
| 
								 | 
							
								        if (version_compare(phpversion(), '7', '>=')) {
							 | 
						||
| 
								 | 
							
								            throw new \TypeError($msg);
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            trigger_error($msg, E_USER_ERROR);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |