bognerf / rest-grabber-legacy
Consume REST APIs (a backport compatible with PHP 7.0)
Requires
- php: >=7.0.7
- ext-json: *
- guzzlehttp/guzzle: ~6.0
- psr/simple-cache: ^1.0
- symfony/validator: ^3.4
Requires (Dev)
- phpunit/phpunit: ^6.0
- squizlabs/php_codesniffer: ^3.0
This package is auto-updated.
Last update: 2025-03-29 00:59:00 UTC
README
This is a backport for working with PHP7.0. You should in nearly any case work with bognerf/rest-grabber
Work with REST APIs and assure the presence and validity of certain elements within the JSON response.
Table of Contents
Via Composer
$ composer require bognerf/rest-grabber
Usage
Basic usage without any validation or generation of derived objects (ValueObjects) is quite straightforward:
use Bognerf\RestGrabber\Grabber;
use Bognerf\RestGrabber\Url;
$url = new Url('https://reqres.in/api/unknown/2');
$grabber = new Grabber($url);
$contents = $grabber->get()->grab()->data();
You'll get an array according to the JSON provided by the response of https://reqres.in/api/unknown/2
;
Global validation Rules
You can pass an array with dot notated keys to define obligatory values in the JSON response. Additionally, you can pass an array of constraints the value of the given key should match. RestGrabber relies on Symfony's Constraint library.
use Bognerf\RestGrabber\Grabber;
use Bognerf\RestGrabber\Url;
$url = new Url('https://reqres.in/api/users/2');
$grabber = new Grabber($url);
$grabber->getHandler()->setObligatoryFields([
'data' => [
new \Symfony\Component\Validator\Constraints\Count(['min'=>5])
],
'data.email' => [
new \Symfony\Component\Validator\Constraints\Email()
]
]);
$grabber->get()->grab();
The example above would query https://reqres.in/api/users/2
and make sure that the root level element data
contains at least five sub-elements. Further, as denoted by data.email
an element email
must be present under data
, which syntactically must conform to an email address.
ValueObjects
RestGrabber generates ValueObjects from certain sub-elements in our JSON (possibly even from the root element, if you so wish). For this to work, we must pass a Handler
to our RestGrabber instance. By default, the library provides a generic Handler Bognerf\RestGrabber\Handlers\PlainJson
. All handlers must extend the abstract class Bognerf\RestGrabber\Handler
.
How to use them
Consider the following JSON example 1 (taken from https://reqres.in/api/unknown/):
{
"page": 1,
"per_page": 3,
"total": 12,
"total_pages": 4,
"data": [
{
"id": 1,
"name": "cerulean",
"year": 2000,
"color": "#98B2D1",
"pantone_value": "15-4020"
},
{
"id": 2,
"name": "fuchsia rose",
"year": 2001,
"color": "#C74375",
"pantone_value": "17-2031"
},
{
"id": 3,
"name": "true red",
"year": 2002,
"color": "#BF1932",
"pantone_value": "19-1664"
}
]
}
There are three objects listed under data. With RestGrabber you can define data as a ValueObject root element (deeper nesting is also possible using dot notation, e.g. data.sub-element), to automatically parse them and create a ValueObject from each of the three entries. Let's have a look:
use Bognerf\RestGrabber\Grabber;
use Bognerf\RestGrabber\Url;
$url = new Url('https://reqres.in/api/unknown');
$grabber = new Grabber($url);
$grabber->getHandler()->addValueObjectsRoot([
'data' => []
]);
$grabber->get()->grab();
$valueObjects = $grabber->valueObjects('data');
We rely on the built-in Handler PlainJson
. $valueObjects
is now an array of three ValueObjects, which could be used like so:
foreach ($valueObjects as $vo) {
echo $vo->getName() . PHP_EOL;
}
getName()
is a magic getter function. You could retrieve any element of a ValueObject by get
plus uppercased first letter of the element
.
Additionally, usual magic getters are also available:
foreach ($valueObjects as $vo) {
echo $vo->color . PHP_EOL;
}
JSON example 2
You aren't limited to only a single ValueObject root, there can be more. Consider our now slightly changed JSON:
{
"page": 1,
"per_page": 3,
"total": 12,
"total_pages": 4,
"data": [
{
"id": 1,
"name": "cerulean",
"year": 2000,
"color": "#98B2D1",
"pantone_value": "15-4020"
},
{
"id": 2,
"name": "fuchsia rose",
"year": 2001,
"color": "#C74375",
"pantone_value": "17-2031"
},
{
"id": 3,
"name": "true red",
"year": 2002,
"color": "#BF1932",
"pantone_value": "19-1664"
}
],
"more-data": [
{
"id": 1,
"name": "Florian"
},
{
"id": 2,
"name": "Katharina"
}
]
}
As you can see, we have more than one potential ValueObject roots, namely data and more-data. Simply add more-data as a second ValueObject root:
use Bognerf\RestGrabber\Grabber;
use Bognerf\RestGrabber\Url;
$url = new Url('https://reqres.in/api/unknown');
$grabber = new Grabber($url);
$grabber->getHandler()->addValueObjectsRoot([
'data' => []
]);
$grabber->getHandler()->addValueObjectsRoot([
'more-data' => []
]);
$grabber->get()->grab();
$valueObjectsData = $grabber->getHandler()->valueObjects('data');
$valueObjectsMoreData = $grabber->getHandler()->valueObjects('more-data');
ValueObjects validation rules
Beyond defining obligatory values on a global scale for the whole JSON, you can also define validators for elements in each ValueObject root. Working again with our REST API at https://reqres.in/api/unknown
, a root level element data
having several objects could be defined as the ValueObject's root level. Remember, the root of ValueObjects is also made in dot notation. Let's see how to achieve this:
use Bognerf\RestGrabber\Grabber;
use Bognerf\RestGrabber\Url;
$url = new Url('https://reqres.in/api/unknown');
$grabber = new Grabber($url);
$grabber->getHandler()->addValueObjectsRoot([
'data' => [
'id' => [],
'name' => [
new \Symfony\Component\Validator\Constraints\Length(['min' => 3, 'max' => 128])
],
'pantone_value' => [
new \Symfony\Component\Validator\Constraints\Regex([
'pattern' => '/^([0-9]){2}\-([0-9]){4}$/'
])
]
]
]);
$grabber->get()->grab();
$valueObjects = $grabber->valueObjects('data');
The example above would query https://reqres.in/api/unknown
and validate that the root level element data
exists and contains at least two elements. The root for our ValueObjects will also be set to data
. Within data, each element must have an element id
without further constraints, and an element name
which should be at least three and at most 100 characters long. Finally, $grabber->valueObjects('data)
will return an array of ValueObjects, each of which was checked to fulfill our constraints.
Custom handlers
For certain use cases it'll make sense to create a custom Handler. As an example, we'll use the API of Cloudflare's content delivery network cdnjs.com.
To ensure CDNJS's JSON response contains all critical elements we rely on, create your custom Handler like so (please don't forget to call parent::__construct()
when extending the base Handler's constructor:
class CdnjsHandler extends \Bognerf\RestGrabber\Handler
{
public function __construct()
{
parent::__construct();
$this->obligatoryFields = [
'assets' => [],
'repository.type' => [
new \Symfony\Component\Validator\Constraints\EqualTo(['value' => 'git']),
],
];
$this->addValueObjectsRoot([
'assets' => [
'version' => [
new \Symfony\Component\Validator\Constraints\NotBlank(),
],
'files' => [
new \Symfony\Component\Validator\Constraints\Count(['min' => 2])
]
]
]);
}
}
The charming part is that the handler defines its own obligatory elements and validation rules. We're requiring the JSON response to contain an element assets as well as an element repository.type, which should have the value git. The latter being a great example for dot notated deeper level elements. Compare the actual JSON from CDNJS.
Furthermore, the collection or list under assets will consitute our single ValueObjects root, assuring that each and every one of it contains an element version (which must be present and not blank or empty or null) and an element files, which must be a collection with at least two elements.
Now let's put our new custom handler to service:
use Bognerf\RestGrabber\Grabber;
use Bognerf\RestGrabber\Url;
$url = new Url('https://api.cdnjs.com/libraries/axios');
$grabber = new Grabber($url);
$grabber->setHandler(new CdnjsHandler());
$grabber->get()->grab();
$valueObjects = $grabber->valueObjects('assets');
dump($valueObjects);
Working with Cache
RestGrabber can take a PSR-16 compatible CacheInterface via RestGrabber::setCache()
. We'll use ZendCache, which is why we have to require some dependencies:
$ composer require zendframework/zend-cache
$ composer require zendframework/zend-serializer
But remember, you could use any PSR-16 compatible CacheInterface. It does not have to be Zend's.
$storage = StorageFactory::factory([
'adapter' => [
'name' => 'filesystem',
'options' => [
'cache_dir' => '/tmp/rest-grabber',
],
],
'plugins' => [
'serializer',
],
]);
$cache = new SimpleCacheDecorator($storage);
$url = new Url('https://api.cdnjs.com/libraries/axios');
$grabber = new Grabber($url);
$grabber->setCache($cache);
echo ($grabber->isCached() ? "Hit":"Missed");
$grabber->setHandler(new \RestGrabberTestbed\Handlers\CdnjsHandler());
$grabber->get()->grab();
$valueObjects = $grabber->valueObjects('assets');
echo ($grabber->isCached() ? "Hit":"Missed");
Testing
$ composer test
Security
If you discover any security related issues, please email fb@florianbogner.de instead of using the issue tracker.
Credits
- Florian Bogner (Maintainer)
License
The MIT License (MIT). Please see License File for more information.