shrink0r/php-schema

View on GitHub
src/BuilderStack.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace Shrink0r\PhpSchema;

/**
 * Helper class to allow Builders to be implemented without circular
 * references. Represents the current nesting produced using $builder->child
 * accesses.
 */
class BuilderStack implements BuilderInterface, \ArrayAccess
{
    /**
     * @var BuilderInterface[]
     */
    protected $builders;

    /**
     * @param array $builders Must be contiguous start with index 0.
     */
    public function __construct(array $builders)
    {
        if (count($builders) === 0) {
            throw new Exception('BuilderStack can not be empty');
        }
        $this->builders = $builders;
    }

    /**
     * @return BuilderInterface
     */
    protected function first()
    {
        return $this->builders[0];
    }

    /**
     * @return BuilderInterface
     */
    protected function last()
    {
        return $this->builders[count($this->builders) - 1];
    }

    /**
     * {@inheritdoc}
     */
    public function build(array $defaults = [])
    {
        return $this->first()->build($defaults);
    }

    /**
     * {@inheritdoc}
     */
    public function rewind()
    {
        return $this->builders[0];
    }

    /**
     * {@inheritdoc}
     */
    public function end()
    {
        if (count($this->builders) === 1) {
            return $this->builders[0];
        }
        return new static(array_slice($this->builders, 0, -1));
    }

    /**
     * {@inheritdoc}
     */
    public function valueOf($key)
    {
        return $this->last()->valueOf($key);
    }

    /**
     * Tells if the given key exists in the builder at the top of the stack.
     *
     * @param string $key
     *
     * @return bool
     */
    public function offsetExists($key)
    {
        return isset($this->last()[$key]);
    }

    /**
     * Navigate to the given key, creating it along the way if it does not yet exist.
     *
     * @param string $key
     *
     * @return BuilderInterface Returns self
     */
    public function offsetGet($key)
    {
        return $this->last()[$key];
    }

    /**
     * Assign a given value to the given key to the builder at the top the stack.
     *
     * @param string $key
     * @param mixed $value
     *
     * @return $this
     */
    public function offsetSet($key, $value)
    {
        $this->last()[$key] = $value;

        return $this;
    }

    /**
     * Unset the given key in the builder at the top of the stack.
     *
     * @param string $key
     */
    public function offsetUnset($key)
    {
        unset($this->last()[$key]);
    }

    /**
     * Navigate to the given key, creating it along the way if it does not yet exist.
     *
     * @param string $key
     *
     * @return BuilderInterface Returns a new BuilderStack instance representing
     *                          the current access path.
     */
    public function __get($key)
    {
        $value = $this->last()->{$key};
        if ($value instanceof static) {
            if ($this->last() !== $value->first()) {
                throw new Exception('Trying to merge incompatible BuilderStacks');
            }
            return new static(array_merge($this->builders, array_slice($value->builders, 1)));
        } else {
            return new static(array_merge($this->builders, [$value]));
        }
    }

    /**
     * Assign a given value to the builder at the top of builder the stack.
     *
     * @param string $key
     * @param mixed $value
     */
    public function __set($key, $value)
    {
        $this->last()->{$key} = $value;
    }

    /**
     * Tells if the given key exists in the builder at the top of the stack.
     *
     * @param string $key
     * @return bool
     */
    public function __isset($key)
    {
        return isset($this->last()->{$key});
    }

    /**
     * Unset the given key in the builder at the top of the stack.
     *
     * @param string $key
     */
    public function __unset($key)
    {
        unset($this->last()->{$key});
    }

    /**
     * Assign a given value to the given key to the builder at the top of the stack.
     *
     * @param string $key
     * @param mixed[] $args
     *
     * @return $this
     */
    public function __call($key, array $args = [])
    {
        $this->last()->{$key}(...$args);
        return $this;
    }
}