Forked from ralphschindler/entity-mapper-via-dynamic-proxy-object.php
Last active
August 29, 2015 14:07
-
-
Save Ocramius/0f60374f35993ea8110c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace EntityMapperFramework { | |
class EntityMapper { | |
public function __construct() { | |
stream_wrapper_register('dynamicproxygenerator', __NAMESPACE__ . '\DynamicProxyGeneratorStream'); | |
} | |
public function createProxy($entity) { | |
$class = get_class($entity); | |
$proxyClass = $class . 'Proxy'; | |
include_once "dynamicproxygenerator://$class?strategy=ProxyProtectedPropertyMapperStrategy"; | |
return (new $proxyClass)->setEntity($entity); | |
} | |
} | |
class DynamicProxyGeneratorStream { | |
protected $position; | |
protected $currentCode; | |
public function stream_open($path, $mode, $options, &$opened_path) { | |
$parsed = parse_url($path); | |
$fqClassName = $parsed['host']; | |
parse_str($parsed['query'], $queryParts); | |
$strategy = $queryParts['strategy']; // strategy could be picked at runtime, or by configuration | |
$ref = new \ReflectionClass($fqClassName); | |
$namespace = $ref->getNamespaceName(); | |
$className = $ref->getShortName(); | |
$this->currentCode = "<?php namespace {$namespace} { " | |
. "class {$className}Proxy extends \\{$fqClassName} {" | |
. "use \EntityMapperFramework\ProxyEntityTrait;" | |
. "use \EntityMapperFramework\\{$strategy}Trait; } }"; | |
$this->position = 0; | |
return true; | |
} | |
public function stream_read($count) { | |
$ret = substr($this->currentCode, $this->position, $count); | |
$this->position += strlen($ret); | |
return $ret; | |
} | |
public function stream_eof() { | |
return $this->position >= strlen($this->currentCode); | |
} | |
public function stream_stat() { | |
$t = time(); | |
return array(0, 0, 0, 0, 0, 0, 0, strlen($this->currentCode), $t, $t, $t, -1, -1); | |
} | |
} | |
trait ProxyEntityTrait { | |
protected $entity; | |
public function setEntity($entity) { | |
$this->entity = $entity; | |
return $this; | |
} | |
} | |
trait ProxyProtectedPropertyMapperStrategyTrait { | |
public function map($mapping, $data) { | |
foreach ($data as $n => $v) { | |
$this->entity->{$mapping[$n]} = $v; | |
} | |
return $this; | |
} | |
} | |
} | |
// Represents the model part of the application | |
namespace MyApplication\Model { | |
class FooEntity /* a POPO Entity, should have no knowledge of persistence layer */ { | |
protected $aNumber; | |
protected $bString; | |
public function getANumber() { return $this->aNumber; } | |
public function getBString() { return $this->bString; } | |
} | |
class MySimpleOrmFooRepository /* implements FooRepository */ { | |
protected $fooMap = ['a_number' => 'aNumber', 'b_string' => 'bString']; | |
protected $proxies = []; | |
public function __construct() { | |
$this->entityMapper = new \EntityMapperFramework\EntityMapper; // should be injected service | |
} | |
public function getFoo() { | |
$foo = new FooEntity; | |
$proxy = $this->entityMapper->createProxy($foo); | |
$this->proxies[spl_object_hash($proxy)] = $proxy; // for future use, like calling Repo::save() | |
$proxy->map($this->fooMap, ['a_number' => 15, 'b_string' => 'The B Team']); | |
return $foo; | |
} | |
} | |
} | |
// Represents the code that calls into the model, like a controller or other service | |
namespace { | |
$repo = new MyApplication\Model\MySimpleOrmFooRepository; | |
$foo = $repo->getFoo(); | |
assert($foo->getANumber() == 15); | |
assert($foo->getBString() == 'The B Team'); | |
echo "Assertions passed\n"; var_dump($foo); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment