ocramius/generated-hydrator

一个对象水化器,允许非常快速地将数组转换为对象以及从对象转换为数组


README

GeneratedHydrator 是一个关于从数组到对象以及从对象到数组高性能转换的库。

这个库是做什么的?

水化器是一个对象,可以从其他对象中提取数据或将数据填入其中。

水化器执行以下操作

  • Object 转换为 array
  • 将数据从 array 放入 Object

GeneratedHydrator 使用代理来实例化非常快速的水化器,因为这将允许访问对象被水化器处理保护的属性。

此外,GeneratedHydrator 的水化器实现了 Laminas\Hydrator\HydratorInterface

安装

要安装 GeneratedHydrator,请安装 Composer 并运行以下命令

composer require ocramius/generated-hydrator

使用方法

以下是如何创建和使用由 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]

性能比较

GeneratedHydrator 生成的水化器非常非常快。以下是 Laminas\Hydrator 的各种水化器与由 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);
}

这将产生类似以下内容

0.028156042098999s
2.606673002243s
0.56710886955261s
0.60278487205505s

如你所见,生成的水化器比 Laminas\Hydrator\ReflectionLaminas\Hydrator\ArraySerializable 快 20 倍,比 Laminas\Hydrator\ClassMethods 快 90 多倍。

针对生产环境的调优

默认情况下,GeneratedHydrator 将在每次新请求时生成水化器。虽然这相对较快,但它将导致 I/O 操作,并且您可以通过预先生成水化器并让您的应用程序在每次运行时自动加载它们而不是生成新的来达到更好的性能。

避免重新生成涉及

  1. 预先生成您的水化器
  2. 确保您的自动加载器知道它们

以下说明假设您正在使用 Composer。

预先生成您的水化器

没有内置的方式来批量生成所有所需的水化器,因此您将需要自己完成。

以下是一个可以用来完成此操作的简单代码片段

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();
}

只需将您需要水化器的所有类添加到 $classes 数组中,并让您的部署过程运行此脚本。完成时,您需要的所有水化器都将位于 /path/to/target-dir 中。

使自动加载器了解您的水化器

使用您预先生成的水化器就像将生成目标目录添加到您的 composer.json 中一样简单。

{
    "autoload": {
        "classmap": [
            "/path/to/target-dir"
        ]
    }
}

在生成您的水化器后,让您的部署脚本运行 composer dump-autoload 以重新生成您的自动加载器。从现在起,GeneratedHydrator 将在生成的类已存在的情况下跳过代码生成和 I/O。

后备自动加载器

或者,GeneratedHydrator 随带一个内置的自动加载器,您可以在自己的应用程序中注册。这简化了部署,但速度略慢。

$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.md 的内容!