neneaX/multi-routing

View on GitHub
src/Adapters/JsonRpc/Request/Parsers/Parser.php

Summary

Maintainability
A
25 mins
Test Coverage
<?php
namespace MultiRouting\Adapters\JsonRpc\Request\Parsers;

use MultiRouting\Adapters\JsonRpc\Request\Content;
use MultiRouting\Adapters\JsonRpc\Response\ErrorFactory;

class Parser
{

    const JSON_RPC_VERSION = '2.0';

    /**
     * The errors occurred during raw content validation against the JSON-RPC standards
     *
     * $priority => $error
     *
     * @var array
     */
    protected $errors;

    /**
     * The raw content, as received from the request
     *
     * @var \stdClass
     */
    protected $rawContent;

    /**
     * The content object
     *
     * @var Content
     */
    protected $content;

    /**
     * JsonRpcParser constructor.
     *
     * @param $requestContent
     */
    public function __construct($requestContent)
    {
        $this->setRawContent($requestContent);
        $this->validate();
        $this->buildContent();
    }

    /**
     * @return Content
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * @return array
     */
    public function getErrors()
    {
        return $this->errors;
    }

    /**
     * Set the content object. Accepts json-encoded strings or objects.
     *
     * @param mixed $rawContent
     * @throws \InvalidArgumentException when content is not valid.
     */
    protected function setRawContent($rawContent)
    {
        switch (true) {
            case is_string($rawContent):
                $this->rawContent = json_decode($rawContent);
                break;

            case ($rawContent instanceof \stdClass):
                $this->rawContent = $rawContent;
                break;

            default:
                break;
        }
    }

    protected function buildContent()
    {
        $this->content = new Content(
            $this->getRawContentId(),
            $this->getRawContentMethod(),
            $this->getRawContentParams()
        );
    }

    /**
    * Get the request id from the content
    *
    * @return int|null
    */
    protected function getRawContentId()
    {
        if (isset($this->rawContent->id)) {
            return $this->rawContent->id;
        }
        return null;
    }

    /**
     * Get the called method from the content
     *
     * @return string|null
     */
    protected function getRawContentMethod()
    {
        if (isset($this->rawContent->method)) {
            return $this->rawContent->method;
        }
        return null;
    }

    /**
     * Get all the parameters from the called method (from the content).
     *
     * @return array
     */
    protected function getRawContentParams()
    {
        if (isset($this->rawContent->params)) {
            return is_array($this->rawContent->params) ? $this->rawContent->params : get_object_vars($this->rawContent->params);
        }
        return [];
    }

    /**
     * Validate the raw content and set errors accordingly
     */
    protected function validate()
    {
        try {
            $this->validateContent();
        } catch (\Exception $e) {
            $this->errors[0] = ErrorFactory::parseError();
            return;
        }

        try {
            $this->validateVersion();
        } catch (\Exception $e) {
            $this->errors[1] = ErrorFactory::invalidRequest();
        }

        try {
            $this->validateId();
        } catch (\Exception $e) {
            // The id is not set or is invalid
            try {
                $this->validateIdExists();

                // If validating passes, the id is set but invalid
                $this->errors[1] = ErrorFactory::invalidRequest();
            } catch (\Exception $e) {
                // The id is not set, the request is a Notification
            }
        }

        try {
            $this->validateMethod();
        } catch (\Exception $e) {
            $this->errors[1] = ErrorFactory::invalidRequest();
        }
    }

    /**
     * Validate if the content is set.
     *
     * @throws \Exception if content not set or method is not defined for content.
     */
    protected function validateContent()
    {
        if (!($this->rawContent instanceof \stdClass)) {
            throw new \Exception('Invalid content');
        }
    }

    /**
     * Validate the JSON-RPC version sent in the Request
     *
     * @throws \Exception
     */
    protected function validateVersion()
    {
        if ( !isset($this->rawContent->jsonrpc)
            || $this->rawContent->jsonrpc !== static::JSON_RPC_VERSION
        ) {
            throw new \Exception('Invalid JSON-RPC version');
        }
    }

    /**
     * Validate if the JSON-RPC id is sent in the Request
     * In case it is not sent, we assume the Request is a Notification
     *
     * @see http://www.jsonrpc.org/specification#notification
     *
     * @throws \Exception
     */
    protected function validateIdExists()
    {
        if (!property_exists($this->rawContent, 'id')) {
            throw new \Exception('The request is a notification');
        }
    }

    /**
     * Validate the JSON-RPC id sent in the Request
     *
     * @throws \Exception
     */
    protected function validateId()
    {
        if (property_exists($this->rawContent, 'id') && !is_numeric($this->rawContent->id)) {
            throw new \Exception('Invalid JSON-RPC id');
        }
    }

    /**
     * Validate the JSON-RPC method sent in the Request
     *
     * @throws \Exception
     */
    protected function validateMethod()
    {
        if (!isset($this->rawContent->method) || !is_string($this->rawContent->method)) {
            throw new \Exception('Invalid JSON-RPC method');
        }
    }
}