Generated Hydrator
GeneratedHydrator is a library about high performance transition of data from arrays to objects and from objects to arrays.
Tests | Releases | Downloads |
---|---|---|
What does this thing do?
A hydrator is an object capable of extracting data from other objects, or filling them with data.
A hydrator performs following operations:
- Convert
Object
toarray
- Put data from an
array
into anObject
GeneratedHydrator uses proxying to instantiate very fast hydrators, since this will allow access to protected properties of the object to be handled by the hydrator.
Also, a hydrator of GeneratedHydrator implements Laminas\Hydrator\HydratorInterface
.
Installation
To install GeneratedHydrator, install Composer and issue the following command:
$ composer require ocramius/generated-hydrator
Usage
Here's an example of how you can create and use a hydrator created by GeneratedHydrator:
<?php
use GeneratedHydrator\Configuration;
require_once __DIR__ . '/vendor/autoload.php';
class Example
{
public $foo = 1;
protected $bar = 2;
protected $baz = 3;
}
$config = new Configuration('Example');
$hydratorClass = $config->createFactory()->getHydratorClass();
$hydrator = new $hydratorClass();
$object = new Example();
var_dump($hydrator->extract($object)); // ['foo' => 1, 'bar' => 2, 'baz' => 3]
$hydrator->hydrate(
['foo' => 4, 'bar' => 5, 'baz' => 6],
$object
);
var_dump($hydrator->extract($object)); // ['foo' => 4, 'bar' => 5, 'baz' => 6]
Performance comparison
A hydrator generated by GeneratedHydrator is very, very, very fast.
Here's the performance of the various hydrators of Laminas\Hydrator
compared to a hydrator built
by GeneratedHydrator:
<?php
require_once __DIR__ . '/vendor/autoload.php';
$iterations = 10000;
class Example
{
public $foo;
public $bar;
public $baz;
public function setFoo($foo) { $this->foo = $foo; }
public function setBar($bar) { $this->bar = $bar; }
public function setBaz($baz) { $this->baz = $baz; }
public function getFoo() { return $this->foo; }
public function getBar() { return $this->bar; }
public function getBaz() { return $this->baz; }
public function exchangeArray($data) {
$this->foo = $data['foo']; $this->bar = $data['bar']; $this->baz = $data['baz'];
}
public function getArrayCopy() {
return array('foo' => $this->foo, 'bar' => $this->bar, 'baz' => $this->baz);
}
}
$object = new Example();
$data = array('foo' => 1, 'bar' => 2, 'baz' => 3);
$config = new GeneratedHydrator\Configuration('Example');
$hydratorClass = $config->createFactory()->getHydratorClass();
$hydrators = array(
new $hydratorClass(),
new Laminas\Hydrator\ClassMethods(),
new Laminas\Hydrator\Reflection(),
new Laminas\Hydrator\ArraySerializable(),
);
foreach ($hydrators as $hydrator) {
$start = microtime(true);
for ($i = 0; $i < $iterations; $i += 1) {
$hydrator->hydrate($data, $object);
$hydrator->extract($object);
}
var_dump(microtime(true) - $start);
}
This will produce something like following:
0.028156042098999s
2.606673002243s
0.56710886955261s
0.60278487205505s
As you can see, the generated hydrator is 20 times faster than Laminas\Hydrator\Reflection
and Laminas\Hydrator\ArraySerializable
, and more than 90 times faster than
Laminas\Hydrator\ClassMethods
.
Tuning for Production
By default, GeneratedHydrator will generate hydrators on every new request. While this is relatively fast, it will cause I/O operations, and you can achieve even better performance by pre-generating your hydrators and telling your application to autoload them instead of generating new ones at each run.
Avoiding regeneration involves:
- pre-generating your hydrators
- ensuring that your autoloader is aware of them
The instructions that follow assume you are using Composer.
Pre-generating your hydrators
There is no built-in way to bulk-generate all required hydrators, so you will need to do so on your own.
Here is a simple snippet you can use to accomplish this:
require '/path/to/vendor/autoload.php'; // composer autoloader
// classes for which we want to pre-generate the hydrators
$classes = [
\My\Namespace\ClassOne::class,
\My\Namespace\ClassTwo::class,
\My\Namespace\ClassThree::class,
];
foreach ($classes as $class) {
$config = new \GeneratedHydrator\Configuration($class);
$config->setGeneratedClassesTargetDir('/path/to/target-dir');
$config->createFactory()->getHydratorClass();
}
Just add all the classes for which you need hydrators to the $classes
array,
and have your deployment process run this script.
When complete, all of the hydrators you need will be available in /path/to/target-dir
.
Making the autoloader aware of your hydrators
Using your pre-generated hydrators is as simple as adding the generation target
directory to your composer.json
:
{
"autoload": {
"classmap": [
"/path/to/target-dir"
]
}
}
After generating your hydrators, have your deployment script run composer dump-autoload
to regenerate your autoloader.
From now on, GeneratedHydrator
will skip code generation and I/O if a generated class already
exists.
Fallback autoloader
Alternatively, GeneratedHydrator
comes with a built-in autoloader that you can register
on your own. This simplifies deployment, but is a bit slower:
$config = new \GeneratedHydrator\Configuration(\My\Namespace\ClassOne::class);
spl_autoload_register($config->getGeneratedClassAutoloader());
// now simply use your hydrator, and if possible, code generation will be skipped:
$hydratorName = $config->createFactory()->getHydratorClass();
$hydrator = new $hydratorName();
Contributing
Please read the CONTRIBUTING.md contents if you wish to help out!