Google in their Performance Tips for
APIs, suggest to limit required bandwidth by filtering out unused fields in response. Their APIs
support additional URL parameter fields which asks API to include only specific fields in response.
fields parameter follows a simple syntax, which allows to query for nested keys, multiple keys
or use wildcard to include all fields (see Syntax and Grammar sections below).
This library implements parsing of fields parameter and filtering of arrays / objects.
See also: PSR-15 compatible middleware based on this library: Partial Response PSR-15 Middleware
<?php
require_once 'vendor/autoload.php';
use messere\phpValueMask\Parser\Parser;
use messere\phpValueMask\Parser\ParserException;
$parser = new Parser();
$input = [
'id' => 1,
'resource' => 'book',
'title' => 'Good Omens',
'identifiers' => (object)[
'isbn' => 'ISBN 83-85100-63-6',
'amazon' => '0060853980',
],
'authors' => [
[
'firstName' => 'Terry',
'lastName' => 'Pratchett'
],
[
'firstName' => 'Neil',
'lastName' => 'Gaiman'
],
],
'year' => [
'us' => 1990,
'uk' => 1990,
'pl' => 1992,
],
'publisher' => [
'us' => 'Workman',
'uk' => 'Gollancz',
'pl' => 'CIA-Books-SVARO',
],
];
$filter = 'title,identifiers/isbn,authors/firstName,*(us,uk),keywords';
try {
$filteredInput = $parser->parse($filter)->filter($input);
print_r($filteredInput);
} catch (ParserException $e) {
echo 'Parser error: ' . $e->getMessage();
} Let's analyze elements of used filter:
titlematches top level element with key title ('Good Omens')identifiers/isbnmatches top level elementidentifiersand then includesisbnelement from matched object ('ISBN 83-85100-63-6')authors/firstNamefinds an array of elements (list) under the keyauthorsand examines all elements, extractingfirstNamefrom each. ('Terry' and 'Neil')*(us,uk)examines all properties and extracts fieldsusanduk. ('1990' and '1990' fromyearelement, 'Workman', 'Gollancz' frompublisher)keywordsdoes not match anything and is silently ignored.
As a result we expect the following output:
Array
(
[title] => Good Omens
[identifiers] => Array
(
[isbn] => ISBN 83-85100-63-6
)
[authors] => Array
(
[0] => Array
(
[firstName] => Terry
)
[1] => Array
(
[firstName] => Neil
)
)
[year] => Array
(
[us] => 1990
[uk] => 1999
)
[publisher] => Array
(
[us] => Workman
[uk] => Gollancz
)
)
ready to serialize to JSON, etc.
Note that library does preserve the structure/nesting of values, but not necessarily types of values - all objects are converted to associative arrays with object's public properties as keys.
aselects keyafrom inputa,b,ccomma separated list of elements: selects keysaandbandca/b/cnested elements: selects keycfrom parent elementbwhich in turn has parent elementaa(b,c)multiple elements: selects elementsbandcfrom parent elementaa(b,c/d)multiple elements: from parentaselect elementband elementdnested inca/*/cwildcard: selects elementcfrom all children of elementa*(b,c)wildcard: selects elementsbandcfrom any parent
Etc. See tests for more examples as well as examples of invalid filters.
Since Google does not provide detailed grammar for their "fields" language, this package uses the following arbitrarily selected rules, that in author's opinion closely resamble intent of original authors.
In EBNF notation:
Mask = MaskElement | MaskElement , "," , Mask ;
MaskElement = ArrayOfMasks | NestedKeys ;
ArrayOfMasks = Key , "(" , Mask , ")" ;
NestedKeys = Key , [ "/" , NestedKeys ] ;
Key = Wildcard | Identifier ;
Identifier = Letter , { Letter | Digit }
Wildcard = "*" ;
Letter = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" |
"N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" |
"a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" |
"n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" |
"_";
Digit = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "0" ;
Library is inspired by:
- Google's API performance tips.
- Similar JavaScript library: nemtsov/json-mask.