Skip to content

Commit 0b7ce31

Browse files
committed
Refactoring
1 parent 82379e8 commit 0b7ce31

26 files changed

+718
-910
lines changed

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# CHANGELOG
2+
3+
## Unreleased
4+
5+
* Moving to a flattened namespace structure.
6+
* Runtimes are now only PHP callables.
7+
* Fixed an error in the way empty JSON literals are parsed so that they now
8+
return an empty string to match the Python and JavaScript implementations.
9+
* Removed functions from runtimes. Instead there is now a function dispatcher
10+
class, FnDispatcher, that provides function implementations behind a single
11+
dispatch function.
12+
* Removed ExprNode in lieu of just using a PHP callable with bound variables.
13+
* Removed debug methods from runtimes and instead into a new Debugger class.
14+
* Heavily cleaned up function argument validation.
15+
* Slice syntax is now properly validated (i.e., colons are followed by the
16+
appropriate value).
17+
* Lots of code cleanup and performance improvements.
18+
19+
## 1.1.1 - 2014-10-08
20+
21+
* Added support for using ArrayAccess and Countable as arrays and objects.
22+
23+
## 1.1.0 - 2014-08-06
24+
25+
* Added the ability to search data returned from json_decode() where JSON
26+
objects are returned as stdClass objects.

CHANGELOG.rst

Lines changed: 0 additions & 14 deletions
This file was deleted.

README.rst

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ using the ``mtdowling/jmespath.php`` package.
2222
]
2323
];
2424
25-
\JmesPath\Env::search($expression, $data);
25+
JmesPath\Env::search($expression, $data);
2626
// Returns: [1, 2, 3]
2727
2828
- `JMESPath documentation <http://jmespath.readthedocs.org/en/latest/>`_
@@ -47,11 +47,7 @@ Runtimes
4747
jmespath.php utilizes *runtimes*. There are currently two runtimes:
4848
AstRuntime and CompilerRuntime.
4949

50-
AstRuntime is utilized by ``JmesPath\Env::search()`` by default. Depending on
51-
your application, it may be useful to customize the runtime used by
52-
``JmesPath\Env::search()``. You can change the runtime utilized by
53-
``JmesPath\Env::search()`` by calling ``JmesPath\registerRuntime()``, passing in an
54-
instance of ``JmesPath\Runtime\RuntimeInterface``.
50+
AstRuntime is utilized by ``JmesPath\Env::search()`` by default.
5551

5652
AstRuntime
5753
~~~~~~~~~~
@@ -61,10 +57,16 @@ and interpret the AST using an external tree visitor. AstRuntime provides a
6157
good general approach for interpreting JMESPath expressions that have a low to
6258
moderate level of reuse.
6359

60+
.. code-block:: php
61+
62+
$runtime = new JmesPath\AstRuntime();
63+
$runtime('foo.bar', ['foo' => ['bar' => 'baz']]);
64+
// > 'baz'
65+
6466
CompilerRuntime
6567
~~~~~~~~~~~~~~~
6668

67-
``JmesPath\Runtime\CompilerRuntime`` provides the most performance for
69+
``JmesPath\CompilerRuntime`` provides the most performance for
6870
applications that have a moderate to high level of reuse of JMESPath
6971
expressions. The CompilerRuntime will walk a JMESPath AST and emit PHP source
7072
code, resulting in anywhere from 7x to 60x speed improvements.
@@ -80,6 +82,13 @@ Use the CompilerRuntime if you know that you will be executing JMESPath
8082
expressions more than once or if you can pre-compile JMESPath expressions
8183
before executing them (for example, server-side applications).
8284

85+
.. code-block:: php
86+
87+
// Note: The cache directory argument is optional.
88+
$runtime = new JmesPath\CompilerRuntime('/path/to/compile/folder');
89+
$runtime('foo.bar', ['foo' => ['bar' => 'baz']]);
90+
// > 'baz'
91+
8392
Environment Variables
8493
^^^^^^^^^^^^^^^^^^^^^
8594

bin/jp.php

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env php
22
<?php
3-
require 'vendor/autoload.php';
3+
require __DIR__ . '/../vendor/autoload.php';
44

55
$description = <<<EOT
66
Runs a JMESPath expression on the provided input or a test case.
@@ -13,10 +13,8 @@
1313
1414
EOT;
1515

16-
// Parse the provided arguments
17-
$args = array();
16+
$args = [];
1817
$currentKey = null;
19-
2018
for ($i = 1, $total = count($argv); $i < $total; $i++) {
2119
if ($i % 2) {
2220
if (substr($argv[$i], 0, 2) == '--') {
@@ -31,21 +29,17 @@
3129
}
3230

3331
$expression = $currentKey;
34-
$runtime = \JmesPath\Env::createRuntime();
3532

3633
if (isset($args['file']) || isset($args['suite']) || isset($args['case'])) {
37-
3834
if (!isset($args['file']) || !isset($args['suite']) || !isset($args['case'])) {
3935
die($description);
4036
}
41-
4237
// Manually run a compliance test
4338
$path = realpath($args['file']);
4439
file_exists($path) or die('File not found at ' . $path);
4540
$json = json_decode(file_get_contents($path), true);
4641
$set = $json[$args['suite']];
4742
$data = $set['given'];
48-
4943
if (!isset($expression)) {
5044
$expression = $set['cases'][$args['case']]['expression'];
5145
echo "Expects\n=======\n";
@@ -57,12 +51,11 @@
5751
echo "NULL\n\n";
5852
}
5953
}
60-
6154
} elseif (isset($expression)) {
6255
// Pass in an expression and STDIN as a standalone argument
6356
$data = json_decode(stream_get_contents(STDIN), true);
6457
} else {
6558
die($description);
6659
}
6760

68-
$runtime->debug($expression, $data);
61+
(new \JmesPath\Debugger())->debugFromEnv(STDOUT, $expression, $data);

bin/perf.php

Lines changed: 23 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,51 @@
11
#!/usr/bin/env php
22
<?php
3-
require 'vendor/autoload.php';
4-
5-
use JmesPath\Runtime\RuntimeInterface;
6-
7-
$runtime = \JmesPath\Env::createRuntime();
8-
9-
if (!isset($_SERVER['CACHE'])) {
10-
$_SERVER['CACHE'] = false;
11-
}
12-
13-
if (isset($argv[1])) {
14-
$dir = $argv[1];
15-
} else {
16-
$dir = __DIR__ . '/../tests/compliance/perf';
17-
}
3+
require __DIR__ . '/../vendor/autoload.php';
184

5+
$dir = isset($argv[1]) ? $argv[1] : __DIR__ . '/../tests/compliance/perf';
196
is_dir($dir) or die('Dir not found: ' . $dir);
20-
217
// Warm up the runner
22-
$runtime->search('abcdefg', array());
8+
\JmesPath\Env::createRuntime()->search('foo', []);
9+
// Run the test suites.
10+
array_map('runSuite', glob($dir . '/*.json'));
2311

24-
$total = 0;
25-
foreach (glob($dir . '/*.json') as $file) {
26-
if (!strpos($file, 'syntax')) {
27-
$total += runSuite($file, $runtime);
28-
}
29-
}
30-
31-
echo "\nTotal time: {$total}ms\n";
32-
33-
function runSuite($file, RuntimeInterface $runtime)
12+
function runSuite($file)
3413
{
3514
$contents = file_get_contents($file);
3615
$json = json_decode($contents, true);
37-
$total = 0;
3816
foreach ($json as $suite) {
3917
foreach ($suite['cases'] as $case) {
40-
$total += runCase(
18+
runCase(
4119
str_replace(getcwd(), '.', $file),
4220
$suite['given'],
43-
$case['expression'],
44-
$runtime
21+
$case['expression']
4522
);
4623
}
4724
}
48-
49-
return $total;
5025
}
5126

52-
function runCase(
53-
$file,
54-
$given,
55-
$expression,
56-
RuntimeInterface $runtime
57-
) {
27+
function runCase($file, $given, $expression)
28+
{
5829
$best = 99999;
30+
$runtime = \JmesPath\Env::createRuntime();
5931

6032
for ($i = 0; $i < 1000; $i++) {
61-
if (!$_SERVER['CACHE']) {
62-
$runtime->clearCache();
63-
}
64-
try {
65-
$t = microtime(true);
66-
$runtime->search($expression, $given);
67-
$tryTime = (microtime(true) - $t) * 1000;
68-
} catch (\Exception $e) {
69-
// Failure test cases shouldn't be tested
70-
return 0;
71-
}
33+
$t = microtime(true);
34+
$runtime->search($expression, $given);
35+
$tryTime = (microtime(true) - $t) * 1000;
7236
if ($tryTime < $best) {
7337
$best = $tryTime;
7438
}
39+
if (!getenv('CACHE')) {
40+
$runtime = \JmesPath\Env::createRuntime();
41+
// Delete compiled scripts if not caching.
42+
if ($runtime instanceof \JmesPath\CompilerRuntime) {
43+
array_map('unlink', glob(sys_get_temp_dir() . '/jmespath_*.php'));
44+
}
45+
}
7546
}
7647

77-
$template = "time: %fms, description: %s, name: %s\n";
48+
$template = "time: %fms, %s: %s\n";
7849
$expression = str_replace("\n", '\n', $expression);
79-
printf($template, $best, $file, $expression);
80-
81-
return $best;
50+
printf($template, $best, basename($file), substr($expression, 0, 50));
8251
}

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
"extra": {
3232
"branch-alias": {
33-
"dev-master": "1.1-dev"
33+
"dev-master": "2.0-dev"
3434
}
3535
}
3636
}

src/AstRuntime.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
namespace JmesPath;
3+
4+
/**
5+
* Default JMESPath runtime environment that uses an external tree visitor to
6+
* interpret an AST.
7+
*/
8+
class AstRuntime
9+
{
10+
private $parser;
11+
private $interpreter;
12+
private $cache = [];
13+
private $cachedCount = 0;
14+
15+
public function __construct(
16+
Parser $parser = null,
17+
callable $fnDispatcher = null
18+
) {
19+
$fnDispatcher = $fnDispatcher ?: FnDispatcher::getInstance();
20+
$this->interpreter = new TreeInterpreter($fnDispatcher);
21+
$this->parser = $parser ?: new Parser();
22+
}
23+
24+
/**
25+
* Returns data from the provided input that matches a given JMESPath
26+
* expression.
27+
*
28+
* @param string $expression JMESPath expression to evaluate
29+
* @param mixed $data Data to search. This data should be data that
30+
* is similar to data returned from json_decode
31+
* using associative arrays rather than objects.
32+
*
33+
* @return mixed|null Returns the matching data or null
34+
*/
35+
public function __invoke($expression, $data)
36+
{
37+
if (!isset($this->cache[$expression])) {
38+
// Clear the AST cache when it hits 1024 entries
39+
if (++$this->cachedCount > 1024) {
40+
$this->cache = [];
41+
$this->cachedCount = 0;
42+
}
43+
$this->cache[$expression] = $this->parser->parse($expression);
44+
}
45+
46+
return $this->interpreter->visit($this->cache[$expression], $data);
47+
}
48+
}

0 commit comments

Comments
 (0)