php-yaoi/php-yaoi

View on GitHub
src/Command/Web/RequestMapper.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?php

namespace Yaoi\Command\Web;

use Yaoi\Command;
use Yaoi\Command\Option;
use Yaoi\Command\RequestMapperContract;
use Yaoi\Io\Request;
use Yaoi\String\Expression;
use Yaoi\String\Utils;

class RequestMapper implements RequestMapperContract
{
    private $request;

    public function __construct(Request $request)
    {
        $this->request = $request;

        $this->unnamedMapper = function ($name) {
            return Utils::fromCamelCase($name, '-');
        };

        $this->namedMapper = function ($name) {
            return Utils::fromCamelCase($name, '_');
        };
    }


    private function readUnnamedOption(Option $option, Command\State $commandState, Command\State $requestState)
    {
        if ($option->type === Option::TYPE_ENUM) {
            $option->setEnumMapper($this->unnamedMapper);
        }

        $value = array();
        $requestValue = array();

        while (false !== $item = $this->getUnnamed()) {
            $requestValue[] = $item;
            $item = $option->validateFilterValue($item);

            if (!$option->isVariadic) {
                $value = $item;
                $requestValue = $requestValue[0];
                break;
            } else {
                $value[] = $item;
            }
        }

        if ($value) {
            $commandState->{$option->name} = $value;
        }


        if ($requestValue) {
            $requestState->{$option->name} = $requestValue;
        }
    }


    private function readNamedOption(Option $option, Command\State $commandState, Command\State $requestState)
    {
        $publicName = $this->namedMapper->__invoke($option->name);
        $requestValue = $this->request->request($publicName);

        if ($option->type === Option::TYPE_ENUM) {
            $option->setEnumMapper($this->namedMapper);
        }

        $value = $option->validateFilterValue($requestValue);

        if ($option->isVariadic) {
            if (!is_array($value)) {
                $value = array($value);
            }
        }

        if ($requestValue !== null) {
            $requestState->{$option->name} = $requestValue;
            $commandState->{$option->name} = $value;

        }
    }


    /** @var \Closure */
    private $unnamedMapper;
    /** @var \Closure */
    private $namedMapper;

    /**
     * @param Option[] $commandOptions
     * @return \stdClass
     * @throws Command\Exception
     */
    /**
     * @param array $commandOptions
     * @param Command\State $commandState
     * @param Command\State $requestState
     */
    public function readOptions(array $commandOptions, Command\State $commandState, Command\State $requestState)
    {
        foreach ($commandOptions as $option) {
            if ($option->isUnnamed) {
                $this->readUnnamedOption($option, $commandState, $requestState);
            } else {
                $this->readNamedOption($option, $commandState, $requestState);
            }

            /*
            if (false === $value && $option->isRequired) {
                throw new Command\Exception('Option ' . $option->name . ' required',
                    Command\Exception::OPTION_REQUIRED);
            }
            */
        }
    }


    private $unnamedValues;

    private function getUnnamed()
    {
        if (null === $this->unnamedValues) {
            $this->unnamedValues = explode('/', trim($this->request->path(), '/'));
        }
        if (!$this->unnamedValues) {
            return false;
        } else {
            return array_shift($this->unnamedValues);
        }
    }

    /**
     * @param array $properties
     * @return Expression
     */
    public function makeAnchor(array $properties)
    {
        if (!$properties) {
            throw new Command\Exception('Unable to make anchor, no properties');
        }

        $unnamed = array();
        $unnamedTemplate = '';

        $queryTemplate = '';
        $query = array();

        foreach ($properties as $property) {
            /** @var Option $option */
            list($option, $value) = $property;

            if ($option->isUnnamed) {
                if (!$option->isVariadic) {
                    $value = array($value);
                }
                foreach ($value as $item) {
                    $unnamedTemplate .= '/??';
                    $unnamed[] = $this->unnamedMapper->__invoke($item);
                }
            } else {
                $queryTemplate .= '&' . $this->namedMapper->__invoke($option->name) . '=??';
                $query[] = $value;
            }
        }

        $template = $unnamedTemplate;
        $binds = $unnamed;
        if ($queryTemplate) {
            $template .= '?' . substr($queryTemplate, 1);
            $binds = array_merge($binds, $query);
        }

        $expression = new Expression($template, $binds);
        $expression->setPlaceholder('??');
        return $expression;
    }

    public function getExportName(Option $option)
    {
        if ($option->isUnnamed) {
            return $this->unnamedMapper->__invoke($option->name);
        } else {
            return $this->namedMapper->__invoke($option->name);
        }
    }

}