AbstractLanguage.php 4.08 KB
<?php

namespace ApiQL\Language;

/**
 * Class AbstractLanguage
 *
 * @package ApiQL\Language
 */
abstract class AbstractLanguage implements LanguageInterface
{
    /**
     * Relational operators
     *
     * @var static
     */
    private static $RELATION_OPERATORS = ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'is_null', 'is_not_null',
    'like', 'not_like', 'in', 'not_in', 'equals_any', 'not_equals_any', 'equals_all', 'not_equals_all', 'search', 'se'];

    /**
     * Relation key in expression
     *
     * @var const
     */
    public const RELATION = 'rel';

    /**
     * Logic operators
     *
     * @var static
     */
    private static $LOGIC_OPERATORS = ['and', 'or'];

    /**
     * Logic key in expression
     *
     * @var const
     */
    public const LOGIC = 'logic';

    /**
     * Spec key in expression
     *
     * @var const
     */
    public const SPEC = 'spec';

    /**
     * Expression to be interpreted
     *
     * @var array
     */
    protected $expression = [];

    /**
     * Query builder decorator
     *
     * @var ApiQueryBuilder
     */
    protected static $builder = null;

    /**
     * Specification mappings
     *
     * @var array
     */
    protected static $specs = [];

    /**
     * Construct abstract interpreter
     *
     * @param array $expression - expression to be interpreted
     * @param mixed $builder - implementation of query builder
     */
    public function __construct(array $expression)
    {
        $this->expression = $expression;
    }

    /**
     * Check if it is unary operator
     *
     * @param string $operator - operator to be checked
     *
     * @return bool
     */
    public static function isRelationOperator(string $operator): bool
    {
        return in_array($operator, self::$RELATION_OPERATORS);
    }


    /**
     * Check if it is variadic operator
     *
     * @param string $operator - operator to be checked
     *
     * @return bool
     */
    public static function isLogicOperator(string $operator): bool
    {
        return in_array($operator, self::$LOGIC_OPERATORS);
    }

    /**
     * Get relation operator from expression
     *
     * @param array $expression - expression with operator
     *
     * @return null|string
     */
    public static function getRelationOperator(array $expression): ?string
    {
        if (array_key_exists(self::RELATION, $expression) && self::isRelationOperator($expression[self::RELATION])) {
            return $expression[self::RELATION];
        }
        return null;
    }

    /**
     * Get logic operator from expression
     *
     * @param array $expression - expression with operator
     *
     * @return null|string
     */
    public static function getLogicOperator(array $expression): ?string
    {
        if (array_key_exists(self::LOGIC, $expression) && self::isLogicOperator($expression[self::LOGIC])) {
            return $expression[self::LOGIC];
        }
        return null;
    }

    /**
     * Check if it is specification
     *
     * @param string $operator - operator to be checked
     *
     * @return bool
     */
    public static function isSpecification(string $operator): bool
    {
        return $operator == 'spec';
    }

    /**
     * Apply handler method based on its type
     *
     * @param string $name method name
     * @param mixed $args interpreters
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public function method(string $name = null, ...$args)
    {
        $interpreters = array_map(function ($interpreter) {
            return $interpreter->execute();
        }, $args);

        switch ($name) {
            case 'or':
                return self::$builder->orX(...$interpreters);
            case 'and':
                return self::$builder->andX(...$interpreters);
            case 'spec':
                return self::$builder->spec(...$interpreters);
            default:
                throw new \InvalidArgumentException(sprintf('Method "%s" not found', $name));
        }
    }

    /**
     * Execute expression
     *
     * @return mixed
     */
    abstract public function execute();
}