Lenient traversal of nested arrays and objects

0.3 2014-04-17 11:07 UTC


Lenient traversal of objects and arrays


By far the easiest way is to use composer and install AQuery from packagist. The package can be found here.

Refer to the composer documentation for installation instructions; the AQuery class is in the AQuery namespace, that is, \AQuery\AQuery.

If you're feeling sassy, or you just want to see the source code, check out the bitbucket repo.

What It Does

AQuery provides a unified query interface for objects, arrays, and ArrayAccess objects. It encapsulates the following common pattern:

if (isset($array['foo']['bar']['baz'])) {
    return $array['foo']['bar']['baz'];
else {
    return $defValue;

Note how this code sample violates the DRY principle: the path into the nested array appears twice. This means that if we want to change it, we have to change it twice, and if we mistype one of the two, we'll get strange errors. However, because isset is a language construct, not a function, we cannot combine the two statements. The following won't work:

if (isset($value = $array['foo']['bar']['baz'])) {
    return $value;
else {
    return $defValue;

Neither will this:

$value = $array['foo']['bar']['baz'];
if (isset($value)) {
    return $value;
else {
    return $defValue;

AQuery provides a nice way out, and a super-simple little query language along with it. Here's what the above example looks like with AQuery:

return AQuery::query('foo/bar/baz', $array, $defValue);

Not only does this descend into our array, it also fails gracefully by returning the default value if the query doesn't resolve, no matter at which level (i.e., if $array['foo'] doesn't contain a key at 'bar', AQuery short-circuits and returns the default value, rather than raise a warning or crash.

AQuery works not only on associative arrays, but also on numeric arrays, plain objects (note, however, that if you implement __get(), you have to also provide a suitable __isset() for the same keys, otherwise AQuery will not see the magic properties thus defined), and objects that implement ArrayAccess.

Basic Usage Example

use \AQuery\AQuery;

$myArray = array(
              'foo' => array(
                  'bar' => array(
                      'baz' => 'quux')));
echo AQuery::query('foo/bar/baz', $myArray);
// Or, equivalent:
echo AQuery::query(array('foo', 'bar', 'baz'), $myArray);

// For more control, or if you want to run the same query over several
// input data structures, instantiate an AQuery object explicitly:
$aquery = new AQuery('foo/bar/baz');

// Or, equivalent (overriding the separator character):
$aquery = new AQuery('foo:bar:baz', ':');
$aquery->run($myArray, "Sorry, didn't find a thing.");