https://github.com/coduo/php-matcher
The easiest way to match data structures like JSON/PlainText/XML against readable patterns. Sandbox:
https://github.com/coduo/php-matcher
Last synced: about 2 months ago
JSON representation
The easiest way to match data structures like JSON/PlainText/XML against readable patterns. Sandbox:
- Host: GitHub
- URL: https://github.com/coduo/php-matcher
- Owner: coduo
- License: mit
- Created: 2014-04-15T12:33:45.000Z (about 11 years ago)
- Default Branch: 6.x
- Last Pushed: 2025-03-01T11:52:52.000Z (4 months ago)
- Last Synced: 2025-04-20T02:08:28.578Z (3 months ago)
- Language: PHP
- Homepage: https://php-matcher.norbert.tech/
- Size: 7.82 MB
- Stars: 825
- Watchers: 20
- Forks: 86
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# PHP Matcher
[](https://shepherd.dev/coduo/php-matcher)
Library created for testing all kinds of JSON/XML/TXT/Scalar values against patterns.
API:
```php
PHPMatcher::match($value = '{"foo": "bar"}', $pattern = '{"foo": "@string@"}') : bool;
PHPMatcher::backtrace() : Backtrace;
PHPMatcher::error() : ?string;
```It was built to simplify API's functional testing.
* [](https://github.com/coduo/php-matcher/actions/workflows/test-suite.yml) - [6.x README](https://github.com/coduo/php-matcher/tree/6.x/README.md) PHP >= 8.1 <= 8.3
* [5.x README](https://github.com/coduo/php-matcher/tree/5.x/README.md) PHP >= 7.2 < 8.0
* [5.0 README](https://github.com/coduo/php-matcher/tree/5.0/README.md) PHP >= 7.2 < 8.0
* [4.0.* README](https://github.com/coduo/php-matcher/tree/4.0/README.md) PHP >= 7.2 < 8.0
* [3.2.* README](https://github.com/coduo/php-matcher/tree/3.2/README.md) PHP >= 7.0 < 8.0
* [3.1.* README](https://github.com/coduo/php-matcher/tree/3.1/README.md) PHP >= 7.0 < 8.0[](https://packagist.org/packages/coduo/php-matcher)
[](https://packagist.org/packages/coduo/php-matcher)
[](https://packagist.org/packages/coduo/php-matcher)
[](https://packagist.org/packages/coduo/php-matcher)## Sandbox
Feel free to play first with [Sandbox](https://php-matcher.norbert.tech/)
## Installation
Require new dev dependency using composer:
```
composer require --dev "coduo/php-matcher"
```## Basic usage
### Direct PHPMatcher usage
```php
match("lorem ipsum dolor", "@string@");if (!$match) {
echo "Error: " . $matcher->error();
echo "Backtrace: \n";
echo (string) $matcher->backtrace();
}
```### PHPUnit extending PHPMatcherTestCase
```php
assertMatchesPattern('{"name": "@string@"}', '{"name": "Norbert"}');
}
}
```### PHPUnit using PHPMatcherAssertions trait
```php
assertMatchesPattern('{"name": "@string@"}', '{"name": "Norbert"}');
}
}
```### Available patterns
* ``@string@``
* ``@integer@``
* ``@number@``
* ``@double@``
* ``@boolean@``
* ``@time@``
* ``@date@``
* ``@datetime@``
* ``@timezone@`` || ``@tz``
* ``@array@``
* ``@array_previous@`` - match next array element using pattern from previous element
* ``@array_previous_repeat@`` - match all remaining array elements using pattern from previous element
* ``@...@`` - *unbounded array*, once used matcher will skip any further array elements
* ``@null@``
* ``@*@`` || ``@wildcard@``
* ``expr(expression)`` - **optional**, requires `symfony/expression-language: ^2.3|^3.0|^4.0|^5.0` to be present
* ``@uuid@``
* ``@ulid@``
* ``@json@``
* ``@string@||@integer@`` - string OR integer### Available pattern expanders
* ``startsWith($stringBeginning, $ignoreCase = false)``
* ``endsWith($stringEnding, $ignoreCase = false)``
* ``contains($string, $ignoreCase = false)``
* ``notContains($string, $ignoreCase = false)``
* ``isDateTime()``
* ``isInDateFormat($format)`` - example `"@[email protected]('Y-m-d H:i:s')`
* ``before(string $date)`` - example ``"@[email protected]().before(\"2020-01-01 00:00:00\")"``
* ``after(string $date)`` - example ``"@[email protected]().after(\"2020-01-01 00:00:00\")"``
* ``isTzOffset()``
* ``isTzIdentifier()``
* ``isTzAbbreviation()``
* ``isEmail()``
* ``isUrl()``
* ``isIp()``
* ``isEmpty()``
* ``isNotEmpty()``
* ``lowerThan($boundry)``
* ``greaterThan($boundry)``
* ``inArray($value)`` - example ``"@[email protected](\"ROLE_USER\")"``
* ``hasProperty($propertyName)`` - example ``"@[email protected](\"property_name\")"``
* ``oneOf(...$expanders)`` - example ``"@[email protected](contains('foo'), contains('bar'), contains('baz'))"``
* ``matchRegex($regex)`` - example ``"@[email protected]('/^lorem.+/')"``
* ``optional()`` - work's only with ``ArrayMatcher``, ``JsonMatcher`` and ``XmlMatcher``
* ``count()`` - work's only with ``ArrayMatcher`` - example ``"@[email protected](5)"``
* ``repeat($pattern, $isStrict = true)`` - example ``'@[email protected]({"name": "foe"})'`` or ``"@[email protected]('@string@')"``
* ``match($pattern)`` - example ``{"image":"@[email protected]({\"url\":\"@[email protected]()\"})"}``## Example usage
### Scalar matching
```php
match(1, 1);
$matcher->match('string', 'string');
```### String matching
```php
match('Norbert', '@string@');
$matcher->match("lorem ipsum dolor", "@[email protected]('lorem').contains('ipsum').endsWith('dolor')");```
### Time matching
```php
match('00:00:00', '@time@');
$matcher->match('00:01:00.000000', '@time@');
$matcher->match('00:01:00', '@[email protected]("00:00:00")');
$matcher->match('00:00:00', '@[email protected]("01:00:00")');```
### Date matching
```php
match('2014-08-19', '@date@');
$matcher->match('2020-01-11', '@date@');
$matcher->match('2014-08-19', '@[email protected]("2016-08-19")');
$matcher->match('2014-08-19', '@[email protected]("today").after("+ 100year")');```
### DateTime matching
```php
match('2014-08-19', '@datetime@');
$matcher->match('2020-01-11 00:00:00', '@datetime@');
$matcher->match('2014-08-19', '@[email protected]("2016-08-19")');
$matcher->match('2014-08-19', '@[email protected]("today").after("+ 100year")');```
### TimeZone matching
```php
match('Europe/Warsaw', '@timezone@');
$matcher->match('Europe/Warsaw', '@tz@');
$matcher->match('GMT', '@tz@');
$matcher->match('01:00', '@tz@');
$matcher->match('01:00', '@[email protected]()');
$matcher->match('GMT', '@[email protected]()');
$matcher->match('Europe/Warsaw', '@[email protected]()');
```### Integer matching
```php
match(100, '@integer@');
$matcher->match(100, '@[email protected](200).greaterThan(10)');```
### Number matching
```php
match(100, '@number@');
$matcher->match('200', '@number@');
$matcher->match(1.25, '@number@');
$matcher->match('1.25', '@number@');
$matcher->match(0b10100111001, '@number@');
```### Double matching
```php
match(10.1, "@double@");
$matcher->match(10.1, "@[email protected](50.12).greaterThan(10)");
```### Boolean matching
```php
match(true, "@boolean@");
$matcher->match(false, "@boolean@");
```### Wildcard matching
```php
match("@integer@", "@*@");
$matcher->match("foobar", "@*@");
$matcher->match(true, "@*@");
$matcher->match(6.66, "@*@");
$matcher->match(array("bar"), "@wildcard@");
$matcher->match(new \stdClass, "@wildcard@");
```### Expression matching
```php
match(new \DateTime('2014-04-01'), "expr(value.format('Y-m-d') == '2014-04-01'");
$matcher->match("Norbert", "expr(value === 'Norbert')");
```### UUID matching
```php
match('9f4db639-0e87-4367-9beb-d64e3f42ae18', '@uuid@');
```### ULID matching
```php
match('01BX5ZZKBKACTAV9WEVGEMMVS0', '@ulid@');
```### Array matching
```php
match(
array(
'users' => array(
array(
'id' => 1,
'firstName' => 'Norbert',
'lastName' => 'Orzechowicz',
'roles' => array('ROLE_USER'),
'position' => 'Developer',
),
array(
'id' => 2,
'firstName' => 'Michał',
'lastName' => 'Dąbrowski',
'roles' => array('ROLE_USER')
),
array(
'id' => 3,
'firstName' => 'Johnny',
'lastName' => 'DąbrowsBravoki',
'roles' => array('ROLE_HANDSOME_GUY')
)
),
true,
6.66
),
array(
'users' => array(
array(
'id' => '@[email protected](0)',
'firstName' => '@string@',
'lastName' => 'Orzechowicz',
'roles' => '@array@',
'position' => '@[email protected]()'
),
array(
'id' => '@integer@',
'firstName' => '@string@',
'lastName' => 'Dąbrowski',
'roles' => '@array@'
),
'@...@'
),
'@boolean@',
'@double@'
)
);
```### Array Previous
> @array_previous@ can also be used when matching JSON's and XML's
```php
match(
array(
'users' => array(
array(
'id' => 1,
'firstName' => 'Norbert',
'lastName' => 'Orzechowicz',
'roles' => array('ROLE_USER'),
'position' => 'Developer',
),
array(
'id' => 2,
'firstName' => 'Michał',
'lastName' => 'Dąbrowski',
'roles' => array('ROLE_USER')
),
array(
'id' => 3,
'firstName' => 'Johnny',
'lastName' => 'DąbrowsBravoki',
'roles' => array('ROLE_HANDSOME_GUY')
)
),
true,
6.66
),
array(
'users' => array(
array(
'id' => '@[email protected](0)',
'firstName' => '@string@',
'lastName' => 'Orzechowicz',
'roles' => '@array@',
'position' => '@[email protected]()'
),
'@array_previous@',
'@array_previous@'
),
'@boolean@',
'@double@'
)
);
```### Array Previous Repeat
> @array_previous_repeat@ can also be used when matching JSON's and XML's
```php
match(
array(
'users' => array(
array(
'id' => 1,
'firstName' => 'Norbert',
'lastName' => 'Orzechowicz',
'roles' => array('ROLE_USER'),
'position' => 'Developer',
),
array(
'id' => 2,
'firstName' => 'Michał',
'lastName' => 'Dąbrowski',
'roles' => array('ROLE_USER')
),
array(
'id' => 3,
'firstName' => 'Johnny',
'lastName' => 'DąbrowsBravoki',
'roles' => array('ROLE_HANDSOME_GUY')
)
),
true,
6.66
),
array(
'users' => array(
array(
'id' => '@[email protected](0)',
'firstName' => '@string@',
'lastName' => 'Orzechowicz',
'roles' => '@array@',
'position' => '@[email protected]()'
),
'@array_previous_repeat@'
),
'@boolean@',
'@double@'
)
);
```### Json matching
```php
match(
'{
"users":[
{
"firstName": "Norbert",
"lastName": "Orzechowicz",
"created": "2014-01-01",
"roles":["ROLE_USER", "ROLE_DEVELOPER"]
}
]
}',
'{
"users":[
{
"firstName": "@string@",
"lastName": "@string@",
"created": "@[email protected]()",
"roles": "@array@",
"position": "@[email protected]()"
}
]
}'
);
```### Json matching with unbounded arrays and objects
```php
match(
'{
"users":[
{
"firstName": "Norbert",
"lastName": "Orzechowicz",
"created": "2014-01-01",
"roles":["ROLE_USER", "ROLE_DEVELOPER"],
"attributes": {
"isAdmin": false,
"dateOfBirth": null,
"hasEmailVerified": true
},
"avatar": {
"url": "http://avatar-image.com/avatar.png"
}
},
{
"firstName": "Michał",
"lastName": "Dąbrowski",
"created": "2014-01-01",
"roles":["ROLE_USER", "ROLE_DEVELOPER", "ROLE_ADMIN"],
"attributes": {
"isAdmin": true,
"dateOfBirth": null,
"hasEmailVerified": true
},
"avatar": null
}
]
}',
'{
"users":[
{
"firstName": "@string@",
"lastName": "@string@",
"created": "@[email protected]()",
"roles": [
"ROLE_USER",
"@...@"
],
"attributes": {
"isAdmin": @boolean@,
"@*@": "@*@"
},
"avatar": "@[email protected]({\"url\":\"@[email protected]()\"})"
}
,
@...@
]
}'
);
```### Xml matching
**Optional** - requires `openlss/lib-array2xml: ^1.0` to be present.
```php
match(<<
IBM
Any Value
XML
,
<<
@string@
@string@
@[email protected]()
XML
);
```Example scenario for api in behat using mongo.
---
``` cucumber
@profile, @user
Feature: Listing user toysAs a user
I want to list my toysBackground:
Given I send and accept JSONScenario: Listing toys
Given the following users exist:
| firstName | lastName |
| Chuck | Norris |And the following toys user "Chuck Norris" exist:
| name |
| Barbie |
| GI Joe |
| Optimus Prime |When I set valid authorization code oauth header for user "Chuck Norris"
And I send a GET request on "/api/toys"
Then the response status code should be 200
And the JSON response should match:
"""
[
{
"id": "@string@",
"name": "Barbie",
"_links: "@*@"
},
{
"id": "@string@",
"name": "GI Joe",
"_links": "@*@"
},
{
"id": "@string@",
"name": "Optimus Prime",
"_links": "@*@"
}
]
"""
```## PHPUnit integration
The `assertMatchesPattern()` is a handy assertion that matches values in PHPUnit tests.
To use it either include the `Coduo\PHPMatcher\PHPUnit\PHPMatcherAssertions` trait,
or extend the `Coduo\PHPMatcher\PHPUnit\PHPMatcherTestCase`:```php
namespace Coduo\PHPMatcher\Tests\PHPUnit;use Coduo\PHPMatcher\PHPUnit\PHPMatcherAssertions;
use PHPUnit\Framework\TestCase;class PHPMatcherAssertionsTest extends TestCase
{
use PHPMatcherAssertions;public function test_it_asserts_if_a_value_matches_the_pattern()
{
$this->assertMatchesPattern('@string@', 'foo');
}
}
```The `matchesPattern()` method can be used in PHPUnit stubs or mocks:
```php
$mock = $this->createMock(Foo::class);
$mock->method('bar')
->with($this->matchesPattern('@string@'))
->willReturn('foo');
```## License
This library is distributed under the MIT license. Please see the LICENSE file.
## Credits
This lib was inspired by [JSON Expressions gem](https://github.com/chancancode/json_expressions) &&
[Behat RestExtension ](https://github.com/jakzal/RestExtension)