app/resto/core/utils/Lexer.php

Summary

Maintainability
D
1 day
Test Coverage
<?php
/*
 * Copyright 2022 Jérôme Gasperi
 *
 * Licensed under the Apache License, version 2.0 (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

require(realpath(dirname(__FILE__)) . '/../../../vendor/lexer-1.2.3/lib/Doctrine/Common/Lexer/AbstractLexer.php');

class Lexer extends Doctrine\Common\Lexer\AbstractLexer
{
    // All tokens that are not valid identifiers must be < 100
    public const T_NONE              = 1;
    public const T_NUMBER            = 2;
    public const T_STRING            = 3;
    public const T_OPEN_PARENTHESIS  = 4;
    public const T_CLOSE_PARENTHESIS = 5;
    public const T_OPEN_BLOCK        = 6;
    public const T_CLOSE_BLOCK       = 7;
    public const T_COMMA             = 8;
    public const T_DATE              = 9;

    // Logical tokens should be >= 200 and < 300
    public const T_AND      = 200;
    public const T_OR       = 201;

    // Operator tokens should be >= 300 and < 400
    public const T_EQ       = 300;
    public const T_NE       = 301;
    public const T_LT       = 302;
    public const T_LTE      = 303;
    public const T_GT       = 304;
    public const T_GTE      = 305;
    public const T_IN       = 306;
    public const T_NI       = 307;
    public const T_NOT      = 308;
    public const T_IS_NULL  = 309;

    // Functions that can be applied on value only should be >= 400
    public const T_TIMESTAMP = 400;

    // Functions that can be applied either on value or property should be >= 500
    public const T_S_INTERSECTS = 500;

    public function namedOperation($value)
    {
        switch ($value) {
            case self::T_IS_NULL:
                return 'isNull';
            case self::T_AND:
                return 'and';
            case self::T_OR:
                return 'or';
            case self::T_NOT:
                return 'not';
            case self::T_EQ:
                return '=';
            case self::T_NE:
                return '<>';
            case self::T_LT:
                return '<';
            case self::T_LTE:
                return '<=';
            case self::T_GT:
                return '>';
            case self::T_GTE:
                return '>=';
            case self::T_IN:
                return 'in';
            case self::T_NI:
                return 'ni';
            case self::T_S_INTERSECTS:
                return 'intersects';
            default:
                return null;
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function getCatchablePatterns()
    {
        return [
            '[a-z0-9_.:-]*',                // Identifiers
            '(?:[0-9]+)?',                  // numbers
            '(?:"[^"]+")',                  // Double quoted strings
            "(?:'[^']+')",                  // Single quoted strings
            '>=', '<=', '<>'                // Operators
        ];
    }

    /**
     * Lexical non-catchable patterns.
     *
     * @return array
     */
    protected function getNonCatchablePatterns()
    {
        return array(
            '\s+'
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function getType(&$value)
    {
        $type = self::T_NONE;

        switch (true) {
            // Recognize numeric values
            case (is_numeric($value)):
                return self::T_NUMBER;

                // Recognize quoted strings
            case ($value[0] === '"'):
                $value = str_replace('""', '"', substr($value, 1, strlen($value) - 2));
                return $this->processQuotedString($value);

                // Recognize quoted strings
            case ($value[0] === '\''):
                $value = str_replace('\'', '', substr($value, 1, strlen($value) - 2));
                return $this->processQuotedString($value);

                // Recognize TIMESTAMP
            case (strpos($value, 'TIMESTAMP(') !== false):
                return self::T_TIMESTAMP;

                // Recognize identifiers, aliased or qualified names
            case (ctype_alpha($value[0])):
                $name = 'Lexer::T_' . strtoupper($value);

                if (defined($name)) {
                    $type = constant($name);

                    if ($type > 100) {
                        return $type;
                    }
                }

                return self::T_STRING;

                // Recognize symbols
            case ($value === '='):
                return self::T_EQ;

            case ($value === '<>'):
                return self::T_NE;

            case ($value === '>'):
                return self::T_GT;

            case ($value === '>='):
                return self::T_GTE;

            case ($value === '<'):
                return self::T_LT;

            case ($value === '<='):
                return self::T_LTE;

            case ($value === '('):
                return self::T_OPEN_PARENTHESIS;

            case ($value === ')'):
                return self::T_CLOSE_PARENTHESIS;

            case ($value === '['):
                return self::T_OPEN_BLOCK;

            case ($value === ']'):
                return self::T_CLOSE_BLOCK;

            case ($value === ','):
                return self::T_COMMA;

                // Default
            default:
                // Do nothing
        }

        return $type;
    }

    /**
     * Process a quoted string
     *
     * @param string $value
     * @return int
     */
    private function processQuotedString($value)
    {
        if (is_numeric($value)) {
            // Quoted numbers are still numbers
            return self::T_NUMBER;
        }

        // See if we are a quoted date
        try {
            $ret = new \DateTime($value, new \DateTimeZone("UTC"));
            if ($ret instanceof \DateTime) {
                return self::T_DATE;
            }
        } catch (\Exception $e) {
            // It's ok when it's not a date.
        }

        return self::T_STRING;
    }
}