Seditio Source
Root |
./othercms/dle15_0/upload/engine/classes/composer/vendor/mtdowling/jmespath.php/src/FnDispatcher.php
<?php
namespace JmesPath;

/**
 * Dispatches to named JMESPath functions using a single function that has the
 * following signature:
 *
 *     mixed $result = fn(string $function_name, array $args)
 */
class FnDispatcher
{
   
/**
     * Gets a cached instance of the default function implementations.
     *
     * @return FnDispatcher
     */
   
public static function getInstance()
    {
        static
$instance = null;
        if (!
$instance) {
           
$instance = new self();
        }

        return
$instance;
    }

   
/**
     * @param string $fn   Function name.
     * @param array  $args Function arguments.
     *
     * @return mixed
     */
   
public function __invoke($fn, array $args)
    {
        return
$this->{'fn_' . $fn}($args);
    }

    private function
fn_abs(array $args)
    {
       
$this->validate('abs', $args, [['number']]);
        return
abs($args[0]);
    }

    private function
fn_avg(array $args)
    {
       
$this->validate('avg', $args, [['array']]);
       
$sum = $this->reduce('avg:0', $args[0], ['number'], function ($a, $b) {
            return
Utils::add($a, $b);
        });
        return
$args[0] ? ($sum / count($args[0])) : null;
    }

    private function
fn_ceil(array $args)
    {
       
$this->validate('ceil', $args, [['number']]);
        return
ceil($args[0]);
    }

    private function
fn_contains(array $args)
    {
       
$this->validate('contains', $args, [['string', 'array'], ['any']]);
        if (
is_array($args[0])) {
            return
in_array($args[1], $args[0]);
        } elseif (
is_string($args[1])) {
            return
mb_strpos($args[0], $args[1], 0, 'UTF-8') !== false;
        } else {
            return
null;
        }
    }

    private function
fn_ends_with(array $args)
    {
       
$this->validate('ends_with', $args, [['string'], ['string']]);
        list(
$search, $suffix) = $args;
        return
$suffix === '' || mb_substr($search, -mb_strlen($suffix, 'UTF-8'), null, 'UTF-8') === $suffix;
    }

    private function
fn_floor(array $args)
    {
       
$this->validate('floor', $args, [['number']]);
        return
floor($args[0]);
    }

    private function
fn_not_null(array $args)
    {
        if (!
$args) {
            throw new \
RuntimeException(
               
"not_null() expects 1 or more arguments, 0 were provided"
           
);
        }

        return
array_reduce($args, function ($carry, $item) {
            return
$carry !== null ? $carry : $item;
        });
    }

    private function
fn_join(array $args)
    {
       
$this->validate('join', $args, [['string'], ['array']]);
       
$fn = function ($a, $b, $i) use ($args) {
            return
$i ? ($a . $args[0] . $b) : $b;
        };
        return
$this->reduce('join:0', $args[1], ['string'], $fn);
    }

    private function
fn_keys(array $args)
    {
       
$this->validate('keys', $args, [['object']]);
        return
array_keys((array) $args[0]);
    }

    private function
fn_length(array $args)
    {
       
$this->validate('length', $args, [['string', 'array', 'object']]);
        return
is_string($args[0]) ? mb_strlen($args[0], 'UTF-8') : count((array) $args[0]);
    }

    private function
fn_max(array $args)
    {
       
$this->validate('max', $args, [['array']]);
       
$fn = function ($a, $b) {
            return
$a >= $b ? $a : $b;
        };
        return
$this->reduce('max:0', $args[0], ['number', 'string'], $fn);
    }

    private function
fn_max_by(array $args)
    {
       
$this->validate('max_by', $args, [['array'], ['expression']]);
       
$expr = $this->wrapExpression('max_by:1', $args[1], ['number', 'string']);
       
$fn = function ($carry, $item, $index) use ($expr) {
            return
$index
               
? ($expr($carry) >= $expr($item) ? $carry : $item)
                :
$item;
        };
        return
$this->reduce('max_by:1', $args[0], ['any'], $fn);
    }

    private function
fn_min(array $args)
    {
       
$this->validate('min', $args, [['array']]);
       
$fn = function ($a, $b, $i) {
            return
$i && $a <= $b ? $a : $b;
        };
        return
$this->reduce('min:0', $args[0], ['number', 'string'], $fn);
    }

    private function
fn_min_by(array $args)
    {
       
$this->validate('min_by', $args, [['array'], ['expression']]);
       
$expr = $this->wrapExpression('min_by:1', $args[1], ['number', 'string']);
       
$i = -1;
       
$fn = function ($a, $b) use ($expr, &$i) {
            return ++
$i ? ($expr($a) <= $expr($b) ? $a : $b) : $b;
        };
        return
$this->reduce('min_by:1', $args[0], ['any'], $fn);
    }

    private function
fn_reverse(array $args)
    {
       
$this->validate('reverse', $args, [['array', 'string']]);
        if (
is_array($args[0])) {
            return
array_reverse($args[0]);
        } elseif (
is_string($args[0])) {
            return
strrev($args[0]);
        } else {
            throw new \
RuntimeException('Cannot reverse provided argument');
        }
    }

    private function
fn_sum(array $args)
    {
       
$this->validate('sum', $args, [['array']]);
       
$fn = function ($a, $b) {
            return
Utils::add($a, $b);
        };
        return
$this->reduce('sum:0', $args[0], ['number'], $fn);
    }

    private function
fn_sort(array $args)
    {
       
$this->validate('sort', $args, [['array']]);
       
$valid = ['string', 'number'];
        return
Utils::stableSort($args[0], function ($a, $b) use ($valid) {
           
$this->validateSeq('sort:0', $valid, $a, $b);
            return
strnatcmp($a, $b);
        });
    }

    private function
fn_sort_by(array $args)
    {
       
$this->validate('sort_by', $args, [['array'], ['expression']]);
       
$expr = $args[1];
       
$valid = ['string', 'number'];
        return
Utils::stableSort(
           
$args[0],
            function (
$a, $b) use ($expr, $valid) {
               
$va = $expr($a);
               
$vb = $expr($b);
               
$this->validateSeq('sort_by:0', $valid, $va, $vb);
                return
strnatcmp($va, $vb);
            }
        );
    }

    private function
fn_starts_with(array $args)
    {
       
$this->validate('starts_with', $args, [['string'], ['string']]);
        list(
$search, $prefix) = $args;
        return
$prefix === '' || mb_strpos($search, $prefix, 0, 'UTF-8') === 0;
    }

    private function
fn_type(array $args)
    {
       
$this->validateArity('type', count($args), 1);
        return
Utils::type($args[0]);
    }

    private function
fn_to_string(array $args)
    {
       
$this->validateArity('to_string', count($args), 1);
       
$v = $args[0];
        if (
is_string($v)) {
            return
$v;
        } elseif (
is_object($v)
            && !(
$v instanceof \JsonSerializable)
            &&
method_exists($v, '__toString')
        ) {
            return (string)
$v;
        }

        return
json_encode($v);
    }

    private function
fn_to_number(array $args)
    {
       
$this->validateArity('to_number', count($args), 1);
       
$value = $args[0];
       
$type = Utils::type($value);
        if (
$type == 'number') {
            return
$value;
        } elseif (
$type == 'string' && is_numeric($value)) {
            return
mb_strpos($value, '.', 0, 'UTF-8') ? (float) $value : (int) $value;
        } else {
            return
null;
        }
    }

    private function
fn_values(array $args)
    {
       
$this->validate('values', $args, [['array', 'object']]);
        return
array_values((array) $args[0]);
    }

    private function
fn_merge(array $args)
    {
        if (!
$args) {
            throw new \
RuntimeException(
               
"merge() expects 1 or more arguments, 0 were provided"
           
);
        }

        return
call_user_func_array('array_replace', $args);
    }

    private function
fn_to_array(array $args)
    {
       
$this->validate('to_array', $args, [['any']]);

        return
Utils::isArray($args[0]) ? $args[0] : [$args[0]];
    }

    private function
fn_map(array $args)
    {
       
$this->validate('map', $args, [['expression'], ['any']]);
       
$result = [];
        foreach (
$args[1] as $a) {
           
$result[] = $args[0]($a);
        }
        return
$result;
    }

    private function
typeError($from, $msg)
    {
        if (
mb_strpos($from, ':', 0, 'UTF-8')) {
            list(
$fn, $pos) = explode(':', $from);
            throw new \
RuntimeException(
               
sprintf('Argument %d of %s %s', $pos, $fn, $msg)
            );
        } else {
            throw new \
RuntimeException(
               
sprintf('Type error: %s %s', $from, $msg)
            );
        }
    }

    private function
validateArity($from, $given, $expected)
    {
        if (
$given != $expected) {
           
$err = "%s() expects {$expected} arguments, {$given} were provided";
            throw new \
RuntimeException(sprintf($err, $from));
        }
    }

    private function
validate($from, $args, $types = [])
    {
       
$this->validateArity($from, count($args), count($types));
        foreach (
$args as $index => $value) {
            if (!isset(
$types[$index]) || !$types[$index]) {
                continue;
            }
           
$this->validateType("{$from}:{$index}", $value, $types[$index]);
        }
    }

    private function
validateType($from, $value, array $types)
    {
        if (
$types[0] == 'any'
           
|| in_array(Utils::type($value), $types)
            || (
$value === [] && in_array('object', $types))
        ) {
            return;
        }
       
$msg = 'must be one of the following types: ' . implode(', ', $types)
            .
'. ' . Utils::type($value) . ' found';
       
$this->typeError($from, $msg);
    }

   
/**
     * Validates value A and B, ensures they both are correctly typed, and of
     * the same type.
     *
     * @param string   $from   String of function:argument_position
     * @param array    $types  Array of valid value types.
     * @param mixed    $a      Value A
     * @param mixed    $b      Value B
     */
   
private function validateSeq($from, array $types, $a, $b)
    {
       
$ta = Utils::type($a);
       
$tb = Utils::type($b);

        if (
$ta !== $tb) {
           
$msg = "encountered a type mismatch in sequence: {$ta}, {$tb}";
           
$this->typeError($from, $msg);
        }

       
$typeMatch = ($types && $types[0] == 'any') || in_array($ta, $types);
        if (!
$typeMatch) {
           
$msg = 'encountered a type error in sequence. The argument must be '
               
. 'an array of ' . implode('|', $types) . ' types. '
               
. "Found {$ta}, {$tb}.";
           
$this->typeError($from, $msg);
        }
    }

   
/**
     * Reduces and validates an array of values to a single value using a fn.
     *
     * @param string   $from   String of function:argument_position
     * @param array    $values Values to reduce.
     * @param array    $types  Array of valid value types.
     * @param callable $reduce Reduce function that accepts ($carry, $item).
     *
     * @return mixed
     */
   
private function reduce($from, array $values, array $types, callable $reduce)
    {
       
$i = -1;
        return
array_reduce(
           
$values,
            function (
$carry, $item) use ($from, $types, $reduce, &$i) {
                if (++
$i > 0) {
                   
$this->validateSeq($from, $types, $carry, $item);
                }
                return
$reduce($carry, $item, $i);
            }
        );
    }

   
/**
     * Validates the return values of expressions as they are applied.
     *
     * @param string   $from  Function name : position
     * @param callable $expr  Expression function to validate.
     * @param array    $types Array of acceptable return type values.
     *
     * @return callable Returns a wrapped function
     */
   
private function wrapExpression($from, callable $expr, array $types)
    {
        list(
$fn, $pos) = explode(':', $from);
       
$from = "The expression return value of argument {$pos} of {$fn}";
        return function (
$value) use ($from, $expr, $types) {
           
$value = $expr($value);
           
$this->validateType($from, $value, $types);
            return
$value;
        };
    }

   
/** @internal Pass function name validation off to runtime */
   
public function __call($name, $args)
    {
       
$name = str_replace('fn_', '', $name);
        throw new \
RuntimeException("Call to undefined function {$name}");
    }
}