Skip to content

Commit

Permalink
start implement Ocramius#64
Browse files Browse the repository at this point in the history
  • Loading branch information
VolCh committed Jun 13, 2018
1 parent f009a17 commit eeb8b55
Show file tree
Hide file tree
Showing 13 changed files with 580 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace GeneratedHydratorBenchmark;

/**
* Benchmark class that contains only private properties hydration
*
* @BeforeMethods({"setUp"})
*/
class FileClosureAllPrivateClassHydrationBench extends RuntimeClosureHydrationBench
{
public function setUp() : void
{
$this->createHydrator(AllPrivateClass::class);
$this->createData();
$this->object = new AllPrivateClass();
}

/**
* @Revs(100)
* @Iterations(200)
*/
public function benchConsume() : void
{
($this->hydrator)($this->object, $this->data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace GeneratedHydratorBenchmark;

/**
* Benchmark class that contains only public properties hydration
*
* @BeforeMethods({"setUp"})
*/
class FileClosureAllPublicClassHydrationBench extends RuntimeClosureHydrationBench
{
public function setUp() : void
{
$this->createHydrator(AllPublicClass::class);
$this->createData();
$this->object = new AllPublicClass();
}

/**
* @Revs(100)
* @Iterations(200)
*/
public function benchConsume() : void
{
($this->hydrator)($this->object, $this->data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace GeneratedHydratorBenchmark;

use GeneratedHydrator\ClosureGenerator\GenerateFileHydrator;
use GeneratedHydrator\ClosureGenerator\GenerateRuntimeHydrator;

class FileClosureHydrationBench extends HydrationBench
{
/** @var callable */
protected $hydrator;

/** @var mixed[] */
protected $data;

/** @var mixed */
protected $object;

/**
* Create and set the hydrator
*/
protected function createHydrator(string $class) : void
{
$this->hydrator = (new GenerateFileHydrator())($class);
}

/**
* Populate test data array
*/
protected function createData() : void
{
$this->data = [
'foo' => 'some foo string',
'bar' => 42,
'baz' => new \DateTime(),
'someFooProperty' => [12, 13, 14],
'someBarProperty' => 12354.4578,
'someBazProperty' => new \stdClass(),
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace GeneratedHydratorBenchmark;

/**
* Benchmark class that contains only private properties hydration
*
* @BeforeMethods({"setUp"})
*/
class RuntimeClosureAllPrivateClassHydrationBench extends RuntimeClosureHydrationBench
{
public function setUp() : void
{
$this->createHydrator(AllPrivateClass::class);
$this->createData();
$this->object = new AllPrivateClass();
}

/**
* @Revs(100)
* @Iterations(200)
*/
public function benchConsume() : void
{
($this->hydrator)($this->object, $this->data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace GeneratedHydratorBenchmark;

/**
* Benchmark class that contains only public properties hydration
*
* @BeforeMethods({"setUp"})
*/
class RuntimeClosureAllPublicClassHydrationBench extends RuntimeClosureHydrationBench
{
public function setUp() : void
{
$this->createHydrator(AllPublicClass::class);
$this->createData();
$this->object = new AllPublicClass();
}

/**
* @Revs(100)
* @Iterations(200)
*/
public function benchConsume() : void
{
($this->hydrator)($this->object, $this->data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace GeneratedHydratorBenchmark;

use GeneratedHydrator\ClosureGenerator\GenerateRuntimeHydrator;

class RuntimeClosureHydrationBench extends HydrationBench
{
/** @var callable */
protected $hydrator;

/** @var mixed[] */
protected $data;

/** @var mixed */
protected $object;

/**
* Create and set the hydrator
*/
protected function createHydrator(string $class) : void
{
$this->hydrator = (new GenerateRuntimeHydrator())($class);
}

/**
* Populate test data array
*/
protected function createData() : void
{
$this->data = [
'foo' => 'some foo string',
'bar' => 42,
'baz' => new \DateTime(),
'someFooProperty' => [12, 13, 14],
'someBarProperty' => 12354.4578,
'someBazProperty' => new \stdClass(),
];
}
}
10 changes: 10 additions & 0 deletions src/GeneratedHydrator/ClosureGenerator/GenerateExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace GeneratedHydrator\ClosureGenerator;

interface GenerateExtractor
{
public function __invoke(string $className): callable;
}
111 changes: 111 additions & 0 deletions src/GeneratedHydrator/ClosureGenerator/GenerateFileHydrator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

declare(strict_types=1);

namespace GeneratedHydrator\ClosureGenerator;

use ReflectionClass;
use ReflectionException;
use RuntimeException;
use function class_exists;
use function file_exists;
use function file_put_contents;
use function sha1;
use function sys_get_temp_dir;

final class GenerateFileHydrator implements GenerateHydrator
{
/** @var string */
private $targetDir;

/** @var callable[] */
private static $hydratorsCache;

/** @var ReflectionClass[] */
private static $classesCache;

public function __construct(?string $targetDir = null)
{
$this->targetDir = $targetDir ?? sys_get_temp_dir() . DIRECTORY_SEPARATOR;
}

public function __invoke(string $className): callable
{
if (isset(self::$hydratorsCache[$className])) {
return self::$hydratorsCache[$className];
}

$classId = str_replace('\\', '_', $className);
$filename = $this->targetDir . $classId . '.php';
$hydratorName = "hydrator_" . $classId;
if (! file_exists($filename)) {
$content = <<<PHP
<?php
declare(strict_types=1);
namespace GeneratedHydrator\Hydrators;
\$hydrator_{$classId} = \Closure::bind(function (\\{$className} \$object, array \$data) : void {
PHP;

foreach ($this->getPropertyNames($className) as $propertyName) {
$content .= " \$object->{$propertyName} = \$data['{$propertyName}'];" . PHP_EOL;
}
$content .= <<<PHP
}, null, \\{$className}::class);
PHP;
file_put_contents($filename, $content);
}
require_once $filename;

return self::$hydratorsCache[$className] = $$hydratorName;
}

private function generateParentHydrator($className): ?callable
{
$parent = $this->getClass($className)->getParentClass();
if ($parent === false) {
return null;
}

return $this($parent->getName());
}

/**
* @return string[]
*/
private function getPropertyNames(string $className): array
{
$class = $this->getClass($className);
$propertyNames = [];
foreach ($class->getProperties() as $property) {
if ($property->isStatic()) {
continue;
}
$propertyNames[] = $property->getName();
}
return $propertyNames;
}

private function getClass(string $className): ReflectionClass
{
if (isset(self::$classesCache[$className])) {
return self::$classesCache[$className];
}

if (!class_exists($className, true)) {
throw new RuntimeException("Class $className not found");
}

try {
$class = new ReflectionClass($className);
} catch (ReflectionException $e) {
throw new RuntimeException("Can't reflect class $className", 0, $e);
}

return self::$classesCache[$className] = $class;
}
}
10 changes: 10 additions & 0 deletions src/GeneratedHydrator/ClosureGenerator/GenerateHydrator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace GeneratedHydrator\ClosureGenerator;

interface GenerateHydrator
{
public function __invoke(string $className): callable;
}
Loading

0 comments on commit eeb8b55

Please sign in to comment.