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