Seditio Source
Root |
./othercms/croogo-4.0.7/vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php
<?php

namespace PhpParser;

use
PhpParser\Node\Expr;
use
PhpParser\Node\Scalar;

/**
 * Evaluates constant expressions.
 *
 * This evaluator is able to evaluate all constant expressions (as defined by PHP), which can be
 * evaluated without further context. If a subexpression is not of this type, a user-provided
 * fallback evaluator is invoked. To support all constant expressions that are also supported by
 * PHP (and not already handled by this class), the fallback evaluator must be able to handle the
 * following node types:
 *
 *  * All Scalar\MagicConst\* nodes.
 *  * Expr\ConstFetch nodes. Only null/false/true are already handled by this class.
 *  * Expr\ClassConstFetch nodes.
 *
 * The fallback evaluator should throw ConstExprEvaluationException for nodes it cannot evaluate.
 *
 * The evaluation is dependent on runtime configuration in two respects: Firstly, floating
 * point to string conversions are affected by the precision ini setting. Secondly, they are also
 * affected by the LC_NUMERIC locale.
 */
class ConstExprEvaluator
{
    private
$fallbackEvaluator;

   
/**
     * Create a constant expression evaluator.
     *
     * The provided fallback evaluator is invoked whenever a subexpression cannot be evaluated. See
     * class doc comment for more information.
     *
     * @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
     */
   
public function __construct(callable $fallbackEvaluator = null) {
       
$this->fallbackEvaluator = $fallbackEvaluator ?? function(Expr $expr) {
            throw new
ConstExprEvaluationException(
               
"Expression of type {$expr->getType()} cannot be evaluated"
           
);
        };
    }

   
/**
     * Silently evaluates a constant expression into a PHP value.
     *
     * Thrown Errors, warnings or notices will be converted into a ConstExprEvaluationException.
     * The original source of the exception is available through getPrevious().
     *
     * If some part of the expression cannot be evaluated, the fallback evaluator passed to the
     * constructor will be invoked. By default, if no fallback is provided, an exception of type
     * ConstExprEvaluationException is thrown.
     *
     * See class doc comment for caveats and limitations.
     *
     * @param Expr $expr Constant expression to evaluate
     * @return mixed Result of evaluation
     *
     * @throws ConstExprEvaluationException if the expression cannot be evaluated or an error occurred
     */
   
public function evaluateSilently(Expr $expr) {
       
set_error_handler(function($num, $str, $file, $line) {
            throw new \
ErrorException($str, 0, $num, $file, $line);
        });

        try {
            return
$this->evaluate($expr);
        } catch (\
Throwable $e) {
            if (!
$e instanceof ConstExprEvaluationException) {
               
$e = new ConstExprEvaluationException(
                   
"An error occurred during constant expression evaluation", 0, $e);
            }
            throw
$e;
        } finally {
           
restore_error_handler();
        }
    }

   
/**
     * Directly evaluates a constant expression into a PHP value.
     *
     * May generate Error exceptions, warnings or notices. Use evaluateSilently() to convert these
     * into a ConstExprEvaluationException.
     *
     * If some part of the expression cannot be evaluated, the fallback evaluator passed to the
     * constructor will be invoked. By default, if no fallback is provided, an exception of type
     * ConstExprEvaluationException is thrown.
     *
     * See class doc comment for caveats and limitations.
     *
     * @param Expr $expr Constant expression to evaluate
     * @return mixed Result of evaluation
     *
     * @throws ConstExprEvaluationException if the expression cannot be evaluated
     */
   
public function evaluateDirectly(Expr $expr) {
        return
$this->evaluate($expr);
    }

    private function
evaluate(Expr $expr) {
        if (
$expr instanceof Scalar\LNumber
           
|| $expr instanceof Scalar\DNumber
           
|| $expr instanceof Scalar\String_
       
) {
            return
$expr->value;
        }

        if (
$expr instanceof Expr\Array_) {
            return
$this->evaluateArray($expr);
        }

       
// Unary operators
       
if ($expr instanceof Expr\UnaryPlus) {
            return +
$this->evaluate($expr->expr);
        }
        if (
$expr instanceof Expr\UnaryMinus) {
            return -
$this->evaluate($expr->expr);
        }
        if (
$expr instanceof Expr\BooleanNot) {
            return !
$this->evaluate($expr->expr);
        }
        if (
$expr instanceof Expr\BitwiseNot) {
            return ~
$this->evaluate($expr->expr);
        }

        if (
$expr instanceof Expr\BinaryOp) {
            return
$this->evaluateBinaryOp($expr);
        }

        if (
$expr instanceof Expr\Ternary) {
            return
$this->evaluateTernary($expr);
        }

        if (
$expr instanceof Expr\ArrayDimFetch && null !== $expr->dim) {
            return
$this->evaluate($expr->var)[$this->evaluate($expr->dim)];
        }

        if (
$expr instanceof Expr\ConstFetch) {
            return
$this->evaluateConstFetch($expr);
        }

        return (
$this->fallbackEvaluator)($expr);
    }

    private function
evaluateArray(Expr\Array_ $expr) {
       
$array = [];
        foreach (
$expr->items as $item) {
            if (
null !== $item->key) {
               
$array[$this->evaluate($item->key)] = $this->evaluate($item->value);
            } else {
               
$array[] = $this->evaluate($item->value);
            }
        }
        return
$array;
    }

    private function
evaluateTernary(Expr\Ternary $expr) {
        if (
null === $expr->if) {
            return
$this->evaluate($expr->cond) ?: $this->evaluate($expr->else);
        }

        return
$this->evaluate($expr->cond)
            ?
$this->evaluate($expr->if)
            :
$this->evaluate($expr->else);
    }

    private function
evaluateBinaryOp(Expr\BinaryOp $expr) {
        if (
$expr instanceof Expr\BinaryOp\Coalesce
           
&& $expr->left instanceof Expr\ArrayDimFetch
       
) {
           
// This needs to be special cased to respect BP_VAR_IS fetch semantics
           
return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)]
                ??
$this->evaluate($expr->right);
        }

       
// The evaluate() calls are repeated in each branch, because some of the operators are
        // short-circuiting and evaluating the RHS in advance may be illegal in that case
       
$l = $expr->left;
       
$r = $expr->right;
        switch (
$expr->getOperatorSigil()) {
            case
'&':   return $this->evaluate($l) &   $this->evaluate($r);
            case
'|':   return $this->evaluate($l) |   $this->evaluate($r);
            case
'^':   return $this->evaluate($l) ^   $this->evaluate($r);
            case
'&&':  return $this->evaluate($l) &&  $this->evaluate($r);
            case
'||':  return $this->evaluate($l) ||  $this->evaluate($r);
            case
'??':  return $this->evaluate($l) ??  $this->evaluate($r);
            case
'.':   return $this->evaluate($l) .   $this->evaluate($r);
            case
'/':   return $this->evaluate($l) /   $this->evaluate($r);
            case
'==':  return $this->evaluate($l) ==  $this->evaluate($r);
            case
'>':   return $this->evaluate($l) >   $this->evaluate($r);
            case
'>=':  return $this->evaluate($l) >=  $this->evaluate($r);
            case
'===': return $this->evaluate($l) === $this->evaluate($r);
            case
'and': return $this->evaluate($l) and $this->evaluate($r);
            case
'or':  return $this->evaluate($l) or  $this->evaluate($r);
            case
'xor': return $this->evaluate($l) xor $this->evaluate($r);
            case
'-':   return $this->evaluate($l) -   $this->evaluate($r);
            case
'%':   return $this->evaluate($l) %   $this->evaluate($r);
            case
'*':   return $this->evaluate($l) *   $this->evaluate($r);
            case
'!=':  return $this->evaluate($l) !=  $this->evaluate($r);
            case
'!==': return $this->evaluate($l) !== $this->evaluate($r);
            case
'+':   return $this->evaluate($l) +   $this->evaluate($r);
            case
'**':  return $this->evaluate($l) **  $this->evaluate($r);
            case
'<<':  return $this->evaluate($l) <<  $this->evaluate($r);
            case
'>>':  return $this->evaluate($l) >>  $this->evaluate($r);
            case
'<':   return $this->evaluate($l) <   $this->evaluate($r);
            case
'<=':  return $this->evaluate($l) <=  $this->evaluate($r);
            case
'<=>': return $this->evaluate($l) <=> $this->evaluate($r);
        }

        throw new \
Exception('Should not happen');
    }

    private function
evaluateConstFetch(Expr\ConstFetch $expr) {
       
$name = $expr->name->toLowerString();
        switch (
$name) {
            case
'null': return null;
            case
'false': return false;
            case
'true': return true;
        }

        return (
$this->fallbackEvaluator)($expr);
    }
}