155 lines
3.5 KiB
PHP
155 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace GraphQL\Language\AST;
|
|
|
|
use ArrayAccess;
|
|
use Countable;
|
|
use GraphQL\Utils\AST;
|
|
use InvalidArgumentException;
|
|
use IteratorAggregate;
|
|
use ReturnTypeWillChange;
|
|
use Traversable;
|
|
use function array_merge;
|
|
use function array_splice;
|
|
use function count;
|
|
use function is_array;
|
|
|
|
/**
|
|
* @template T of Node
|
|
* @phpstan-implements ArrayAccess<int|string, T>
|
|
* @phpstan-implements IteratorAggregate<T>
|
|
*/
|
|
class NodeList implements ArrayAccess, IteratorAggregate, Countable
|
|
{
|
|
/**
|
|
* @var Node[]
|
|
* @phpstan-var array<T>
|
|
*/
|
|
private $nodes;
|
|
|
|
/**
|
|
* @param Node[] $nodes
|
|
*
|
|
* @phpstan-param array<T> $nodes
|
|
* @phpstan-return self<T>
|
|
*/
|
|
public static function create(array $nodes) : self
|
|
{
|
|
return new static($nodes);
|
|
}
|
|
|
|
/**
|
|
* @param Node[] $nodes
|
|
*
|
|
* @phpstan-param array<T> $nodes
|
|
*/
|
|
public function __construct(array $nodes)
|
|
{
|
|
$this->nodes = $nodes;
|
|
}
|
|
|
|
/**
|
|
* @param int|string $offset
|
|
*/
|
|
public function offsetExists($offset) : bool
|
|
{
|
|
return isset($this->nodes[$offset]);
|
|
}
|
|
|
|
/**
|
|
* TODO enable strict typing by changing how the Visitor deals with NodeList.
|
|
* Ideally, this function should always return a Node instance.
|
|
* However, the Visitor currently allows mutation of the NodeList
|
|
* and puts arbitrary values in the NodeList, such as strings.
|
|
* We will have to switch to using an array or a less strict
|
|
* type instead so we can enable strict typing in this class.
|
|
*
|
|
* @param int|string $offset
|
|
*
|
|
* @phpstan-return T
|
|
*/
|
|
#[ReturnTypeWillChange]
|
|
public function offsetGet($offset)// : Node
|
|
{
|
|
$item = $this->nodes[$offset];
|
|
|
|
if (is_array($item) && isset($item['kind'])) {
|
|
/** @phpstan-var T $node */
|
|
$node = AST::fromArray($item);
|
|
$this->nodes[$offset] = $node;
|
|
}
|
|
|
|
return $this->nodes[$offset];
|
|
}
|
|
|
|
/**
|
|
* @param int|string|null $offset
|
|
* @param Node|mixed[] $value
|
|
*
|
|
* @phpstan-param T|mixed[] $value
|
|
*/
|
|
public function offsetSet($offset, $value) : void
|
|
{
|
|
if (is_array($value)) {
|
|
/** @phpstan-var T $value */
|
|
$value = AST::fromArray($value);
|
|
}
|
|
|
|
// Happens when a Node is pushed via []=
|
|
if ($offset === null) {
|
|
$this->nodes[] = $value;
|
|
|
|
return;
|
|
}
|
|
|
|
$this->nodes[$offset] = $value;
|
|
}
|
|
|
|
/**
|
|
* @param int|string $offset
|
|
*/
|
|
public function offsetUnset($offset) : void
|
|
{
|
|
unset($this->nodes[$offset]);
|
|
}
|
|
|
|
/**
|
|
* @param mixed $replacement
|
|
*
|
|
* @phpstan-return NodeList<T>
|
|
*/
|
|
public function splice(int $offset, int $length, $replacement = null) : NodeList
|
|
{
|
|
return new NodeList(array_splice($this->nodes, $offset, $length, $replacement));
|
|
}
|
|
|
|
/**
|
|
* @param NodeList|Node[] $list
|
|
*
|
|
* @phpstan-param NodeList<T>|array<T> $list
|
|
* @phpstan-return NodeList<T>
|
|
*/
|
|
public function merge($list) : NodeList
|
|
{
|
|
if ($list instanceof self) {
|
|
$list = $list->nodes;
|
|
}
|
|
|
|
return new NodeList(array_merge($this->nodes, $list));
|
|
}
|
|
|
|
public function getIterator() : Traversable
|
|
{
|
|
foreach ($this->nodes as $key => $_) {
|
|
yield $this->offsetGet($key);
|
|
}
|
|
}
|
|
|
|
public function count() : int
|
|
{
|
|
return count($this->nodes);
|
|
}
|
|
}
|