susina/psr2-code-generator

View on GitHub
src/Model/Parts/ParametersPart.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php declare(strict_types=1);

namespace Susina\Codegen\Model\Parts;

use gossi\docblock\Docblock;
use gossi\docblock\tags\ParamTag;
use Susina\Codegen\Model\PhpParameter;

/**
 * Parameters Part.
 *
 * For all models that can have parameters
 *
 * @author Thomas Gossmann
 */
trait ParametersPart
{
    /** @var PhpParameter[] */
    private $parameters = [];

    /**
     * Sets a collection of parameters.
     *
     * Note: clears all parameters before setting the new ones
     *
     * @param PhpParameter[] $parameters
     *
     * @return $this
     */
    public function setParameters(array $parameters): self
    {
        $this->parameters = [];
        foreach ($parameters as $parameter) {
            $this->addParameter($parameter);
        }

        return $this;
    }

    /**
     * Adds a parameter.
     *
     * @return $this
     */
    public function addParameter(PhpParameter $parameter): self
    {
        $this->parameters[] = $parameter;

        return $this;
    }

    /**
     * Checks whether a parameter exists.
     *
     * @param string $name parameter name
     *
     * @return bool `true` if a parameter exists and `false` if not
     */
    public function hasParameter(string $name): bool
    {
        foreach ($this->parameters as $param) {
            if ($param->getName() == $name) {
                return true;
            }
        }

        return false;
    }

    /**
     * A quick way to add a parameter which is created from the given parameters.
     *
     * @param mixed $defaultValue omit the argument to define no default value
     *
     * @return $this
     */
    public function addSimpleParameter(string $name, ?string $type = null, $defaultValue = null): self
    {
        $parameter = new PhpParameter($name);
        if (null !== $type) {
            $parameter->setType($type);
        }

        if (2 < func_num_args()) {
            $parameter->setValue($defaultValue);
        }

        $this->addParameter($parameter);

        return $this;
    }

    /**
     * A quick way to add a parameter with description which is created from the given parameters.
     *
     * @param mixed $defaultValue omit the argument to define no default value
     *
     * @return $this
     */
    public function addSimpleDescParameter(string $name, ?string $type = null, ?string $typeDescription = null, $defaultValue = null): self
    {
        $parameter = new PhpParameter($name);
        $parameter->setType($type ?? '');
        $parameter->setTypeDescription($typeDescription ?? '');

        if (3 == 3 < func_num_args()) {
            $parameter->setValue($defaultValue);
        }

        $this->addParameter($parameter);

        return $this;
    }

    /**
     * Returns a parameter by index or name.
     *
     * @throws \InvalidArgumentException
     */
    public function getParameterByName(string $name): PhpParameter
    {
        foreach ($this->parameters as $param) {
            if ($param->getName() === $name) {
                return $param;
            }
        }

        throw new \InvalidArgumentException(sprintf('There is no parameter named "%s".', $name));
    }

    /**
     * Returns a parameter by index or name.
     *
     * @throws \InvalidArgumentException
     */
    public function getParameterByPosition(int $position): PhpParameter
    {
        $this->checkPosition($position);

        return $this->parameters[$position];
    }

    /**
     * Replaces a parameter at a given position.
     *
     * @throws \InvalidArgumentException
     *
     * @return $this
     */
    public function replaceParameter(int $position, PhpParameter $parameter): self
    {
        $this->checkPosition($position);
        $this->parameters[$position] = $parameter;

        return $this;
    }

    /**
     * Remove the given parameter.
     *
     * @throws \InvalidArgumentException If the parameter doesn't exist
     *
     * @return $this
     */
    public function removeParameter(PhpParameter $param): self
    {
        return $this->removeParameterByName($param->getName());
    }

    /**
     * Remove the parameter at the given position.
     *
     * @throws \InvalidArgumentException If the parameter doesn't exist
     *
     * @return $this
     */
    public function removeParameterByPosition(int $position): self
    {
        $this->checkPosition($position);
        unset($this->parameters[$position]);
        $this->parameters = array_values($this->parameters);

        return $this;
    }

    /**
     * Remove a parameter having the given name.
     *
     * @throws \InvalidArgumentException If the parameter doesn't exist
     *
     * @return $this
     */
    public function removeParameterByName(string $name): self
    {
        $position = null;
        foreach ($this->parameters as $index => $param) {
            if ($param->getName() == $name) {
                $position = $index;
            }
        }

        if (null !== $position) {
            $this->removeParameterByPosition($position);
        }

        return $this;
    }

    /**
     * Returns an array of parameters.
     *
     * @return PhpParameter[]
     */
    public function getParameters(): array
    {
        return $this->parameters;
    }

    /**
     * Returns the docblock.
     */
    abstract protected function getDocblock(): Docblock;

    /**
     * Generates docblock for params.
     *
     * @psalm-suppress TooManyArguments
     */
    protected function generateParamDocblock(): void
    {
        $docblock = $this->getDocblock();
        $tags = $docblock->getTags('param');
        foreach ($this->parameters as $param) {
            $ptag = $param->getDocblockTag();

            $tag = $tags->find($ptag, function (ParamTag $tag, ParamTag $ptag): bool {
                return $tag->getVariable() == $ptag->getVariable();
            });

            // try to update existing docblock first
            if (null !== $tag) {
                $tag->setDescription($ptag->getDescription());
                $tag->setType($ptag->getType());
            }

            // ... append if it doesn't exist
            else {
                $docblock->appendTag($ptag);
            }
        }
    }

    /**
     * @throws \InvalidArgumentException if the position is not correct
     */
    private function checkPosition(int $position): void
    {
        if ($position < 0 || $position > count($this->parameters)) {
            throw new \InvalidArgumentException(sprintf('The position must be in the range [0, %d].', count($this->parameters)));
        }
    }
}