# Generated Hydrator

GeneratedHydrator is a library about high performance transition of data from arrays to objects and from objects
to arrays.

| Tests   | Releases   | Downloads  | Dependencies  |
|---|---|---|---|
| [![Build Status](https://travis-ci.org/Ocramius/GeneratedHydrator.png?branch=master)](https://travis-ci.org/Ocramius/GeneratedHydrator) [![Code Coverage](https://scrutinizer-ci.com/g/Ocramius/GeneratedHydrator/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Ocramius/GeneratedHydrator/?branch=master) [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/Ocramius/GeneratedHydrator/badges/quality-score.png?s=822ec900346db3405fc50f929de2f6ca209d9726)](https://scrutinizer-ci.com/g/Ocramius/GeneratedHydrator/)   | [![Latest Stable Version](https://poser.pugx.org/ocramius/generated-hydrator/v/stable.png)](https://packagist.org/packages/ocramius/generated-hydrator) [![Latest Unstable Version](https://poser.pugx.org/ocramius/generated-hydrator/v/unstable.png)](https://packagist.org/packages/ocramius/generated-hydrator)  | [![Total Downloads](https://poser.pugx.org/ocramius/generated-hydrator/downloads.png)](https://packagist.org/packages/ocramius/generated-hydrator)  |[![Dependency Status](https://www.versioneye.com/package/php--ocramius--generated-hydrator/badge.png)](https://www.versioneye.com/package/php--ocramius--generated-hydrator)   |

## What does this thing do?

A [hydrator](http://framework.zend.com/manual/current/en/modules/zend.stdlib.hydrator.html) is an object capable of
extracting data from other objects, or filling them with data.

A hydrator performs following operations:

 * Convert `Object` to `array`
 * Put data from an `array` into an `Object`

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 `Zend\Hydrator\HydratorInterface`.

## Usage

Here's an example of how you can create and use a hydrator created by GeneratedHydrator:

```php
<?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 `Zend\Hydrator` compared to a hydrator built
by GeneratedHydrator:

```php
<?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 Zend\Hydrator\ClassMethods(),
    new Zend\Hydrator\Reflection(),
    new Zend\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:

```php
0.028156042098999s
2.606673002243s
0.56710886955261s
0.60278487205505s
```

As you can see, the generated hydrator is 20 times faster than `Zend\Hydrator\Reflection`
and `Zend\Hydrator\ArraySerializable`, and more than 90 times faster than
`Zend\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:

 1. pre-generating your hydrators
 2. 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:

```php
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`:

```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:

```php
$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](https://github.com/Ocramius/GeneratedHydrator/blob/master/CONTRIBUTING.md) contents
if you wish to help out!
