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);
 | 
						|
        }
 | 
						|
    }
 | 
						|
} |