maurice2k / querycheck
Evaluate logical JSON queries (MongoDB style)
Installs: 6
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/maurice2k/querycheck
Requires
- php: ^8.3
Requires (Dev)
- phpunit/phpunit: ^11.0
README
QueryCheck is a logical JSON query evaluator which uses the MongoDB query style.
This is a PHP 8.3+ port of the original JavaScript/Node.js querycheck package.
Supported Operators
Comparison Operators
- $eq - Matches values that are equal to a specified value
- $ne - Matches values that are not equal to a specified value
- $gt - Matches values that are greater than a specified value
- $gte - Matches values that are greater than or equal to a specified value
- $lt - Matches values that are less than a specified value
- $lte - Matches values that are less than or equal to a specified value
- $in - Matches any of the values specified in an array
- $regex - Matches values using regular expression pattern matching
Logical Operators
- $and - Joins query clauses with a logical AND
- $or - Joins query clauses with a logical OR
- $not - Inverts the effect of a query expression
Aggregation Expressions ($expr)
- $expr - Allows aggregation expressions within query predicates
The following operators can be used within $expr:
Arithmetic Operators:
- $add - Adds numbers together
- $subtract - Subtracts two numbers
- $multiply - Multiplies numbers together
- $divide - Divides two numbers
- $mod - Returns the modulo of two numbers
Query Operators (for aggregation context):
- $eq - Returns true if values are equal
- $ne - Returns true if values are not equal
- $gt - Returns true if first value is greater than second
- $gte - Returns true if first value is greater than or equal to second
- $lt - Returns true if first value is less than second
- $lte - Returns true if first value is less than or equal to second
Logical Operators:
Conditional Operators:
- $cond - Conditional expression with if/then/else branches
Field References:
Field references within $expr use MongoDB syntax with $ prefix (e.g., $fieldName, $customer.address.city).
Installation
Install with Composer:
composer require maurice2k/querycheck
Simple Example
<?php use Maurice2k\QueryCheck\QueryCheck; $vars = [ 'now' => [ 'isoDate' => '2020-05-21', 'isoTime' => '13:59:48', ] ]; $openingHours = new QueryCheck([ 'now.isoDate' => [ '$not' => [ '$in' => ['2019-12-25', '2019-12-26', '2019-12-31', '2020-01-01'] ] ], 'now.isoTime' => [ '$gt' => '10:00', '$lt' => '18:00' ] ]); if ($openingHours->test($vars)) { echo "We're OPEN!\n"; } else { echo "Sorry, we're CLOSED!\n"; }
Using $expr for Aggregation Expressions
The $expr operator allows you to use aggregation expressions for field-to-field comparisons and calculations:
<?php use Maurice2k\QueryCheck\QueryCheck; // Compare two fields $budget = new QueryCheck([ '$expr' => [ '$gt' => ['$spent', '$budget'] // spent > budget ] ]); $data = ['spent' => 450, 'budget' => 400]; $budget->test($data); // returns true // Arithmetic operations $total = new QueryCheck([ '$expr' => [ '$gte' => [ ['$add' => ['$subtotal', '$tax', '$shipping']], 100 ] ] ]); $data = ['subtotal' => 80, 'tax' => 10, 'shipping' => 15]; $total->test($data); // returns true (80 + 10 + 15 = 105 >= 100) // Conditional expressions $discount = new QueryCheck([ '$expr' => [ '$lt' => [ [ '$cond' => [ 'if' => ['$gte' => ['$qty', 100]], 'then' => ['$multiply' => ['$price', 0.5]], 'else' => ['$multiply' => ['$price', 0.75]] ] ], 60 ] ] ]); $data = ['qty' => 150, 'price' => 100]; $discount->test($data); // returns true (qty >= 100, so discounted price = 100 * 0.5 = 50, and 50 < 60 is true) // Logical operators - $not $notGreater = new QueryCheck([ '$expr' => [ '$not' => [ ['$gt' => ['$qty', 250]] ] ] ]); $data = ['qty' => 200]; $notGreater->test($data); // returns true (qty is NOT > 250) // Logical operators - $in $inStock = new QueryCheck([ '$expr' => [ '$in' => ['$fruit', '$in_stock'] ] ]); $data = ['fruit' => 'banana', 'in_stock' => ['apple', 'banana', 'cherry']]; $inStock->test($data); // returns true (banana is in the in_stock array) // Combining $not and $in $notRestricted = new QueryCheck([ '$expr' => [ '$not' => [ ['$in' => ['$category', '$restricted_categories']] ] ] ]); $data = ['category' => 'electronics', 'restricted_categories' => ['weapons', 'alcohol']]; $notRestricted->test($data); // returns true (electronics is NOT in restricted categories)
Notes:
- Field references in
$exprrequire the$prefix (e.g.,$fieldName). Without the prefix, values are treated as literals. $exprcan be combined with$andand$oroperators to mix regular field queries with aggregation expressions.- The aggregation
$notoperator evaluatesfalse,null, and0as false; all other values (including non-zero numbers and arrays) as true. - The aggregation
$inoperator checks if a value exists in an array, similar to the query$inbut used within expressions.
PHP vs JavaScript Differences
Undefined Values
In JavaScript, accessing a non-existent property returns undefined. In PHP, there is no undefined type.
To handle this difference, the PHP version:
- Undefined in PHP means a key doesn't exist in an array (checked with
!isset()or!array_key_exists()) - When a key doesn't exist, it's treated as
nullinternally, but the comparison logic distinguishes between actualnullvalues and non-existent keys - By default, non-existent keys (undefined) do NOT equal
null - You can use
setUndefinedEqualsNull(true)to make undefined values equal tonull
$qc = new QueryCheck(['nonExistent' => null]); $qc->setUndefinedEqualsNull(true); $qc->test(['someKey' => 'value']); // returns true - nonExistent is undefined, treated as null
Arrays vs Objects
PHP uses arrays for both lists and associative arrays (objects). The package automatically detects:
- Sequential numeric arrays (list-style) are treated as arrays
- Associative arrays are treated as objects/hashes
Advanced Features
Strict Mode
Enable strict type checking:
$qc = new QueryCheck(['age' => '30']); $qc->setStrictMode(true); $qc->test(['age' => 30]); // throws StrictTypeError - strict type mismatch
Custom Operand Evaluator
You can extend QueryCheck with custom operand evaluation functions. The operand evaluator works with both regular query operators and $expr aggregation expressions:
$qc = new QueryCheck([ 'fullName' => ['$concat' => ['$var' => 'firstName'], ' ', ['$var' => 'lastName']] ]); // Or use with $expr: $qc = new QueryCheck([ '$expr' => [ '$gt' => [ ['$multiply' => [['$var' => 'price'], ['$var' => 'multiplier']]], 150 ] ] ]); $qc->setOperandEvaluator(function($operand, $data) use ($qc) { // Custom evaluation logic if (is_array($operand) && isset($operand['$var'])) { return $qc->getVariableValue($operand['$var'], $data); } if (is_array($operand) && isset($operand['$concat'])) { return implode('', array_map( fn($item) => is_string($item) ? $item : $this($item, $data), $operand['$concat'] )); } return $operand; });
Testing
Run the test suite:
composer install ./vendor/bin/phpunit
License
QueryCheck is available under the MIT license.