v2: Updates

* Simplifies & beautifies everything
* Introduces a new Class system.
* Errors are defaulted to AWS's handler.
* New function names & more efficient handling.
* Should fix a majority of the errors.

Please read the README for more!
This commit is contained in:
Devang Srivastava 2020-09-28 15:32:51 +05:30
commit e6d7753dc8
1095 changed files with 45088 additions and 2911 deletions

View file

@ -29,7 +29,7 @@ class AstRuntime
* is similar to data returned from json_decode
* using associative arrays rather than objects.
*
* @return mixed|null Returns the matching data or null
* @return mixed Returns the matching data or null
*/
public function __invoke($expression, $data)
{

View file

@ -19,8 +19,8 @@ class CompilerRuntime
private $interpreter;
/**
* @param string $dir Directory used to store compiled PHP files.
* @param Parser $parser JMESPath parser to utilize
* @param string|null $dir Directory used to store compiled PHP files.
* @param Parser|null $parser JMESPath parser to utilize
* @throws \RuntimeException if the cache directory cannot be created
*/
public function __construct($dir = null, Parser $parser = null)
@ -46,7 +46,7 @@ class CompilerRuntime
* is similar to data returned from json_decode
* using associative arrays rather than objects.
*
* @return mixed|null Returns the matching data or null
* @return mixed Returns the matching data or null
* @throws \RuntimeException
*/
public function __invoke($expression, $data)

View file

@ -20,14 +20,16 @@ final class Env
* @param string $expression JMESPath expression to evaluate
* @param mixed $data JSON-like data to search
*
* @return mixed|null Returns the matching data or null
* @return mixed Returns the matching data or null
*/
public static function search($expression, $data)
{
static $runtime;
if (!$runtime) {
$runtime = Env::createRuntime();
}
return $runtime($expression, $data);
}
@ -39,7 +41,7 @@ final class Env
*/
public static function createRuntime()
{
switch ($compileDir = getenv(self::COMPILE_DIR)) {
switch ($compileDir = self::getEnvVariable(self::COMPILE_DIR)) {
case false: return new AstRuntime();
case 'on': return new CompilerRuntime();
default: return new CompilerRuntime($compileDir);
@ -55,7 +57,8 @@ final class Env
public static function cleanCompileDir()
{
$total = 0;
$compileDir = getenv(self::COMPILE_DIR) ?: sys_get_temp_dir();
$compileDir = self::getEnvVariable(self::COMPILE_DIR) ?: sys_get_temp_dir();
foreach (glob("{$compileDir}/jmespath_*.php") as $file) {
$total++;
unlink($file);
@ -63,4 +66,26 @@ final class Env
return $total;
}
/**
* Reads an environment variable from $_SERVER, $_ENV or via getenv().
*
* @param string $name
*
* @return string|null
*/
private static function getEnvVariable($name)
{
if (array_key_exists($name, $_SERVER)) {
return $_SERVER[$name];
}
if (array_key_exists($name, $_ENV)) {
return $_ENV[$name];
}
$value = getenv($name);
return $value === false ? null : $value;
}
}

View file

@ -45,7 +45,7 @@ class FnDispatcher
{
$this->validate('avg', $args, [['array']]);
$sum = $this->reduce('avg:0', $args[0], ['number'], function ($a, $b) {
return $a + $b;
return Utils::add($a, $b);
});
return $args[0] ? ($sum / count($args[0])) : null;
}
@ -62,7 +62,7 @@ class FnDispatcher
if (is_array($args[0])) {
return in_array($args[1], $args[0]);
} elseif (is_string($args[1])) {
return strpos($args[0], $args[1]) !== false;
return mb_strpos($args[0], $args[1], 0, 'UTF-8') !== false;
} else {
return null;
}
@ -72,7 +72,7 @@ class FnDispatcher
{
$this->validate('ends_with', $args, [['string'], ['string']]);
list($search, $suffix) = $args;
return $suffix === '' || substr($search, -strlen($suffix)) === $suffix;
return $suffix === '' || mb_substr($search, -mb_strlen($suffix, 'UTF-8'), null, 'UTF-8') === $suffix;
}
private function fn_floor(array $args)
@ -112,13 +112,15 @@ class FnDispatcher
private function fn_length(array $args)
{
$this->validate('length', $args, [['string', 'array', 'object']]);
return is_string($args[0]) ? strlen($args[0]) : count((array) $args[0]);
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; };
$fn = function ($a, $b) {
return $a >= $b ? $a : $b;
};
return $this->reduce('max:0', $args[0], ['number', 'string'], $fn);
}
@ -137,7 +139,9 @@ class FnDispatcher
private function fn_min(array $args)
{
$this->validate('min', $args, [['array']]);
$fn = function ($a, $b, $i) { return $i && $a <= $b ? $a : $b; };
$fn = function ($a, $b, $i) {
return $i && $a <= $b ? $a : $b;
};
return $this->reduce('min:0', $args[0], ['number', 'string'], $fn);
}
@ -167,7 +171,9 @@ class FnDispatcher
private function fn_sum(array $args)
{
$this->validate('sum', $args, [['array']]);
$fn = function ($a, $b) { return $a + $b; };
$fn = function ($a, $b) {
return Utils::add($a, $b);
};
return $this->reduce('sum:0', $args[0], ['number'], $fn);
}
@ -201,7 +207,7 @@ class FnDispatcher
{
$this->validate('starts_with', $args, [['string'], ['string']]);
list($search, $prefix) = $args;
return $prefix === '' || strpos($search, $prefix) === 0;
return $prefix === '' || mb_strpos($search, $prefix, 0, 'UTF-8') === 0;
}
private function fn_type(array $args)
@ -234,7 +240,7 @@ class FnDispatcher
if ($type == 'number') {
return $value;
} elseif ($type == 'string' && is_numeric($value)) {
return strpos($value, '.') ? (float) $value : (int) $value;
return mb_strpos($value, '.', 0, 'UTF-8') ? (float) $value : (int) $value;
} else {
return null;
}
@ -276,7 +282,7 @@ class FnDispatcher
private function typeError($from, $msg)
{
if (strpos($from, ':')) {
if (mb_strpos($from, ':', 0, 'UTF-8')) {
list($fn, $pos) = explode(':', $from);
throw new \RuntimeException(
sprintf('Argument %d of %s %s', $pos, $fn, $msg)

View file

@ -7,7 +7,7 @@ namespace JmesPath;
* @param string $expression Expression to search.
* @param mixed $data Data to search.
*
* @return mixed|null
* @return mixed
*/
if (!function_exists(__NAMESPACE__ . '\search')) {
function search($expression, $data)

View file

@ -340,7 +340,7 @@ class Lexer
eof:
$tokens[] = [
'type' => self::T_EOF,
'pos' => strlen($input),
'pos' => mb_strlen($input, 'UTF-8'),
'value' => null
];

View file

@ -55,7 +55,7 @@ class Parser
];
/**
* @param Lexer $lexer Lexer used to tokenize expressions
* @param Lexer|null $lexer Lexer used to tokenize expressions
*/
public function __construct(Lexer $lexer = null)
{
@ -142,7 +142,8 @@ class Parser
return ['type' => T::T_NOT, 'children' => [$this->expr(self::$bp[T::T_NOT])]];
}
private function nud_lparen() {
private function nud_lparen()
{
$this->next();
$result = $this->expr(0);
if ($this->token['type'] !== T::T_RPAREN) {

View file

@ -23,7 +23,7 @@ class TreeCompiler
$this->source = $this->indentation = '';
$this->write("<?php\n")
->write('use JmesPath\\TreeInterpreter as Ti;')
->write('use JmesPath\\FnDispatcher as Fn;')
->write('use JmesPath\\FnDispatcher as Fd;')
->write('use JmesPath\\Utils;')
->write('')
->write('function %s(Ti $interpreter, $value) {', $fnName)
@ -257,7 +257,7 @@ class TreeCompiler
}
return $this->write(
'$value = Fn::getInstance()->__invoke("%s", %s);',
'$value = Fd::getInstance()->__invoke("%s", %s);',
$node['value'], $args
);
}

View file

@ -10,9 +10,9 @@ class TreeInterpreter
private $fnDispatcher;
/**
* @param callable $fnDispatcher Function dispatching function that accepts
* a function name argument and an array of
* function arguments and returns the result.
* @param callable|null $fnDispatcher Function dispatching function that accepts
* a function name argument and an array of
* function arguments and returns the result.
*/
public function __construct(callable $fnDispatcher = null)
{

View file

@ -3,7 +3,7 @@ namespace JmesPath;
class Utils
{
static $typeMap = [
public static $typeMap = [
'boolean' => 'boolean',
'string' => 'string',
'NULL' => 'null',
@ -126,6 +126,31 @@ class Utils
}
}
/**
* Safely add together two values.
*
* @param mixed $a First value to add
* @param mixed $b Second value to add
*
* @return int|float
*/
public static function add($a, $b)
{
if (is_numeric($a)) {
if (is_numeric($b)) {
return $a + $b;
} else {
return $a;
}
} else {
if (is_numeric($b)) {
return $b;
} else {
return 0;
}
}
}
/**
* JMESPath requires a stable sorting algorithm, so here we'll implement
* a simple Schwartzian transform that uses array index positions as tie
@ -140,14 +165,18 @@ class Utils
public static function stableSort(array $data, callable $sortFn)
{
// Decorate each item by creating an array of [value, index]
array_walk($data, function (&$v, $k) { $v = [$v, $k]; });
array_walk($data, function (&$v, $k) {
$v = [$v, $k];
});
// Sort by the sort function and use the index as a tie-breaker
uasort($data, function ($a, $b) use ($sortFn) {
return $sortFn($a[0], $b[0]) ?: ($a[1] < $b[1] ? -1 : 1);
});
// Undecorate each item and return the resulting sorted array
return array_map(function ($v) { return $v[0]; }, array_values($data));
return array_map(function ($v) {
return $v[0];
}, array_values($data));
}
/**
@ -210,7 +239,7 @@ class Utils
private static function sliceIndices($subject, $start, $stop, $step)
{
$type = gettype($subject);
$len = $type == 'string' ? strlen($subject) : count($subject);
$len = $type == 'string' ? mb_strlen($subject, 'UTF-8') : count($subject);
list($start, $stop, $step) = self::adjustSlice($len, $start, $stop, $step);
$result = [];
@ -224,6 +253,6 @@ class Utils
}
}
return $type == 'string' ? implode($result, '') : $result;
return $type == 'string' ? implode('', $result) : $result;
}
}