diff --git a/src/Instantiator/Instantiator.php b/src/Instantiator/Instantiator.php index f1ab8c0..48ee5d9 100644 --- a/src/Instantiator/Instantiator.php +++ b/src/Instantiator/Instantiator.php @@ -113,6 +113,29 @@ public function buildFactory($className) }; } + /** + * Checks if a class is cloneable + * + * @internal + * @private + * + * This method is only exposed as public because of PHP 5.3 compatibility. Do not + * use this method in your own code + * + * @param ReflectionClass $class + * + * @return bool + */ + public function isSafeToClone(ReflectionClass $class) + { + if (method_exists($class, 'isCloneable') && ! $class->isCloneable()) { + return false; + } + + // not cloneable if it implements `__clone`, as we want to avoid calling it + return ! $class->hasMethod('__clone'); + } + /** * @param string $className * @@ -258,16 +281,15 @@ private function getInstantiatorsMap() private function getCloneablesMap() { $cachedInstantiators = $this->getInstantiatorsMap(); + $that = $this; return self::$cachedCloneables = self::$cachedCloneables - ?: new CallbackLazyMap(function ($className) use ($cachedInstantiators) { + ?: new CallbackLazyMap(function ($className) use ($cachedInstantiators, $that) { /* @var $factory Closure */ $factory = $cachedInstantiators->$className; $instance = $factory(); - $reflection = new ReflectionClass($instance); - // not cloneable if it implements `__clone`, as we want to avoid calling it - if ($reflection->hasMethod('__clone')) { + if (! $that->isSafeToClone(new ReflectionClass($className))) { return null; } diff --git a/tests/InstantiatorTest/InstantiatorTest.php b/tests/InstantiatorTest/InstantiatorTest.php index 18fe298..c17172f 100644 --- a/tests/InstantiatorTest/InstantiatorTest.php +++ b/tests/InstantiatorTest/InstantiatorTest.php @@ -169,6 +169,7 @@ public function getInstantiableClasses() array('InstantiatorTestAsset\\SimpleSerializableAsset'), array('InstantiatorTestAsset\\PharExceptionAsset'), array('InstantiatorTestAsset\\UnCloneableAsset'), + array('InstantiatorTestAsset\\XMLReaderAsset'), ); if (\PHP_VERSION_ID === 50429 || \PHP_VERSION_ID === 50513) { diff --git a/tests/InstantiatorTestAsset/XMLReaderAsset.php b/tests/InstantiatorTestAsset/XMLReaderAsset.php new file mode 100644 index 0000000..b42f0f2 --- /dev/null +++ b/tests/InstantiatorTestAsset/XMLReaderAsset.php @@ -0,0 +1,42 @@ +. + */ + +namespace InstantiatorTestAsset; + +use BadMethodCallException; +use XMLReader; + +/** + * Test asset that extends an internal PHP class + * + * @author Dave Marshall + * @link https://github.com/doctrine/instantiator/pull/8 + */ +class XMLReaderAsset extends XMLReader +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +}