Created
December 16, 2010 14:24
-
-
Save webmozart/743439 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 | |
class OptionSupport | |
{ | |
protected static $definitions = array(); | |
public static function getDefinition($class) | |
{ | |
if (!isset(self::$definitions[$class])) { | |
self::$definitions[$class] = new OptionDefinition($class); | |
} | |
return self::$definitions[$class]; | |
} | |
public static function initialize($object, array $options) | |
{ | |
if (!is_object($object)) { | |
throw new LogicException('Can only set options on objects'); | |
} | |
if (count($options) === 0) { | |
return; | |
} | |
self::getDefinition(get_class($object))->setOptions($object, $options); | |
} | |
} | |
class OptionDefinition | |
{ | |
protected $class; | |
protected $requiredOptions = array(); | |
protected $options = array(); | |
public function __construct($class) | |
{ | |
$this->class = $class; | |
$parentClass = get_parent_class($class); | |
if ($parentClass) { | |
$parentDefinition = OptionSupport::getDefinition($parentClass); | |
$this->requiredOptions = $parentDefinition->requiredOptions; | |
$this->options = $parentDefinition->options; | |
} | |
try | |
{ | |
if (isset($class::$options)) { | |
foreach ($class::$options as $option) { | |
if (!isset($this->options[$option])) { | |
$reflProperty = new ReflectionProperty($class, $option); | |
$reflProperty->setAccessible(true); | |
$this->options[$option] = $reflProperty; | |
} | |
} | |
} | |
if (isset($class::$requiredOptions)) { | |
foreach ($class::$requiredOptions as $option) { | |
if (!isset($this->options[$option])) { | |
$reflProperty = new ReflectionProperty($class, $option); | |
$reflProperty->setAccessible(true); | |
$this->options[$option] = $reflProperty; | |
} | |
$this->requiredOptions[$option] = true; | |
} | |
} | |
} catch (\ReflectionException $e) { | |
throw new \LogicException('Invalid option definition: ' . $e->getMessage(), 0, $e); | |
} | |
} | |
public function setOptions($object, array $options) | |
{ | |
if (!$object instanceof $this->class) { | |
throw new \LogicException(sprintf('Cannot add options to an object of the wrong type. Expected %s, got %s', $this->class, get_class($object))); | |
} | |
// check option names | |
if ($diff = array_diff_key($options, $this->options)) { | |
throw new \LogicException(sprintf('%s does not support the following options: "%s".', $this->class, implode('", "', array_keys($diff)))); | |
} | |
// check required options | |
if ($diff = array_diff_key($this->requiredOptions, $options)) { | |
throw new \LogicException(sprintf('%s requires the following options: \'%s\'.', $this->class, implode('", "', array_keys($diff)))); | |
} | |
foreach ($options as $option => $value) { | |
$this->options[$option]->setValue($object, $value); | |
} | |
} | |
} | |
class OptionSupport2 | |
{ | |
protected static $definitions = array(); | |
public static function getDefinition($class) | |
{ | |
if (!isset(self::$definitions[$class])) { | |
self::$definitions[$class] = new OptionDefinition2($class); | |
} | |
return self::$definitions[$class]; | |
} | |
public static function initialize($object, array $options) | |
{ | |
if (!is_object($object)) { | |
throw new LogicException('Can only set options on objects'); | |
} | |
if (count($options) === 0) { | |
return; | |
} | |
self::getDefinition(get_class($object))->setOptions($object, $options); | |
} | |
} | |
class OptionDefinition2 | |
{ | |
protected $class; | |
protected $requiredOptions = array(); | |
protected $options = array(); | |
public function __construct($class) | |
{ | |
$this->class = $class; | |
$parentClass = get_parent_class($class); | |
if ($parentClass) { | |
$parentDefinition = OptionSupport2::getDefinition($parentClass); | |
$this->requiredOptions = $parentDefinition->requiredOptions; | |
$this->options = $parentDefinition->options; | |
} | |
try | |
{ | |
if (method_exists($class, 'getOptions')) { | |
foreach ($class::getOptions() as $option) { | |
if (!isset($this->options[$option])) { | |
$reflProperty = new ReflectionProperty($class, $option); | |
$reflProperty->setAccessible(true); | |
$this->options[$option] = $reflProperty; | |
} | |
} | |
} | |
if (method_exists($class, 'getRequiredOptions')) { | |
foreach ($class::getRequiredOptions() as $option) { | |
if (!isset($this->options[$option])) { | |
$reflProperty = new ReflectionProperty($class, $option); | |
$reflProperty->setAccessible(true); | |
$this->options[$option] = $reflProperty; | |
} | |
$this->requiredOptions[$option] = true; | |
} | |
} | |
} catch (\ReflectionException $e) { | |
throw new \LogicException('Invalid option definition: ' . $e->getMessage(), 0, $e); | |
} | |
} | |
public function setOptions($object, array $options) | |
{ | |
if (!$object instanceof $this->class) { | |
throw new \LogicException(sprintf('Cannot add options to an object of the wrong type. Expected %s, got %s', $this->class, get_class($object))); | |
} | |
// check option names | |
if ($diff = array_diff_key($options, $this->options)) { | |
throw new \LogicException(sprintf('%s does not support the following options: "%s".', $this->class, implode('", "', array_keys($diff)))); | |
} | |
// check required options | |
if ($diff = array_diff_key($this->requiredOptions, $options)) { | |
throw new \LogicException(sprintf('%s requires the following options: \'%s\'.', $this->class, implode('", "', array_keys($diff)))); | |
} | |
foreach ($options as $option => $value) { | |
$this->options[$option]->setValue($object, $value); | |
} | |
} | |
} | |
class A_OptionSupport | |
{ | |
static public $options = array( | |
'a', | |
'b', | |
'c', | |
'd', | |
); | |
private $a = 1; | |
private $b = 2; | |
private $c; | |
private $d; | |
public function __construct(array $options) | |
{ | |
OptionSupport::initialize($this, $options); | |
} | |
} | |
class B_OptionSupport extends A_OptionSupport | |
{ | |
} | |
class C_OptionSupport extends B_OptionSupport | |
{ | |
} | |
class A_OptionSupport2 | |
{ | |
static protected $options = array( | |
'a', | |
'b', | |
'c', | |
'd', | |
); | |
static public function getOptions() | |
{ | |
return static::$options; | |
} | |
private $a = 1; | |
private $b = 2; | |
private $c; | |
private $d; | |
public function __construct(array $options) | |
{ | |
OptionSupport2::initialize($this, $options); | |
} | |
} | |
class B_OptionSupport2 extends A_OptionSupport2 | |
{ | |
} | |
class C_OptionSupport2 extends B_OptionSupport2 | |
{ | |
} | |
define('COUNT', 4); | |
// benchmark OptionSupport | |
echo "\nOptionSupport\n"; | |
echo "-------------\n"; | |
$start1 = microtime(true); | |
new C_OptionSupport(array('c' => 1)); | |
echo 'Object 1: ' . (microtime(true) - $start1) . "ms\n"; | |
$start2 = microtime(true); | |
for ($i = 0; $i < (COUNT - 1); ++$i) { | |
$class = $i % 2 == 0 ? 'B_OptionSupport' : 'C_OptionSupport'; | |
new $class(array('c' => 1)); | |
} | |
echo 'Object 2-' . COUNT . ': ' . (microtime(true) - $start2) . "ms\n"; | |
echo 'Total: ' . (microtime(true) - $start1) . "ms\n"; | |
// benchmark OptionSupport2 | |
echo "\nOptionSupport2\n"; | |
echo "--------------\n"; | |
$start1 = microtime(true); | |
new C_OptionSupport2(array('c' => 1)); | |
echo 'Object 1: ' . (microtime(true) - $start1) . "ms\n"; | |
$start2 = microtime(true); | |
for ($i = 0; $i < (COUNT - 1); ++$i) { | |
$class = $i % 2 == 0 ? 'B_OptionSupport2' : 'C_OptionSupport2'; | |
new $class(array('c' => 1)); | |
} | |
echo 'Object 2-' . COUNT . ': ' . (microtime(true) - $start2) . "ms\n"; | |
echo 'Total: ' . (microtime(true) - $start1) . "ms\n"; |
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
OptionSupport | |
------------- | |
Object 1: 0.000196218490601ms | |
Object 2-4: 0.000143766403198ms | |
Total: 0.000360012054443ms | |
OptionSupport2 | |
-------------- | |
Object 1: 0.000185966491699ms | |
Object 2-4: 0.000142097473145ms | |
Total: 0.000349044799805ms |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment