Chris@0: Chris@0: * Marcello Duarte Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Prophecy\Doubler; Chris@0: Chris@0: use Doctrine\Instantiator\Instantiator; Chris@0: use Prophecy\Doubler\ClassPatch\ClassPatchInterface; Chris@0: use Prophecy\Doubler\Generator\ClassMirror; Chris@0: use Prophecy\Doubler\Generator\ClassCreator; Chris@0: use Prophecy\Exception\InvalidArgumentException; Chris@0: use ReflectionClass; Chris@0: Chris@0: /** Chris@0: * Cached class doubler. Chris@0: * Prevents mirroring/creation of the same structure twice. Chris@0: * Chris@0: * @author Konstantin Kudryashov Chris@0: */ Chris@0: class Doubler Chris@0: { Chris@0: private $mirror; Chris@0: private $creator; Chris@0: private $namer; Chris@0: Chris@0: /** Chris@0: * @var ClassPatchInterface[] Chris@0: */ Chris@0: private $patches = array(); Chris@0: Chris@0: /** Chris@0: * @var \Doctrine\Instantiator\Instantiator Chris@0: */ Chris@0: private $instantiator; Chris@0: Chris@0: /** Chris@0: * Initializes doubler. Chris@0: * Chris@0: * @param ClassMirror $mirror Chris@0: * @param ClassCreator $creator Chris@0: * @param NameGenerator $namer Chris@0: */ Chris@0: public function __construct(ClassMirror $mirror = null, ClassCreator $creator = null, Chris@0: NameGenerator $namer = null) Chris@0: { Chris@0: $this->mirror = $mirror ?: new ClassMirror; Chris@0: $this->creator = $creator ?: new ClassCreator; Chris@0: $this->namer = $namer ?: new NameGenerator; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns list of registered class patches. Chris@0: * Chris@0: * @return ClassPatchInterface[] Chris@0: */ Chris@0: public function getClassPatches() Chris@0: { Chris@0: return $this->patches; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Registers new class patch. Chris@0: * Chris@0: * @param ClassPatchInterface $patch Chris@0: */ Chris@0: public function registerClassPatch(ClassPatchInterface $patch) Chris@0: { Chris@0: $this->patches[] = $patch; Chris@0: Chris@0: @usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) { Chris@0: return $patch2->getPriority() - $patch1->getPriority(); Chris@0: }); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates double from specific class or/and list of interfaces. Chris@0: * Chris@0: * @param ReflectionClass $class Chris@0: * @param ReflectionClass[] $interfaces Array of ReflectionClass instances Chris@0: * @param array $args Constructor arguments Chris@0: * Chris@0: * @return DoubleInterface Chris@0: * Chris@0: * @throws \Prophecy\Exception\InvalidArgumentException Chris@0: */ Chris@0: public function double(ReflectionClass $class = null, array $interfaces, array $args = null) Chris@0: { Chris@0: foreach ($interfaces as $interface) { Chris@0: if (!$interface instanceof ReflectionClass) { Chris@0: throw new InvalidArgumentException(sprintf( Chris@0: "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". Chris@0: "a second argument to `Doubler::double(...)`, but got %s.", Chris@0: is_object($interface) ? get_class($interface).' class' : gettype($interface) Chris@0: )); Chris@0: } Chris@0: } Chris@0: Chris@0: $classname = $this->createDoubleClass($class, $interfaces); Chris@0: $reflection = new ReflectionClass($classname); Chris@0: Chris@0: if (null !== $args) { Chris@0: return $reflection->newInstanceArgs($args); Chris@0: } Chris@0: if ((null === $constructor = $reflection->getConstructor()) Chris@0: || ($constructor->isPublic() && !$constructor->isFinal())) { Chris@0: return $reflection->newInstance(); Chris@0: } Chris@0: Chris@0: if (!$this->instantiator) { Chris@0: $this->instantiator = new Instantiator(); Chris@0: } Chris@0: Chris@0: return $this->instantiator->instantiate($classname); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates double class and returns its FQN. Chris@0: * Chris@0: * @param ReflectionClass $class Chris@0: * @param ReflectionClass[] $interfaces Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) Chris@0: { Chris@0: $name = $this->namer->name($class, $interfaces); Chris@0: $node = $this->mirror->reflect($class, $interfaces); Chris@0: Chris@0: foreach ($this->patches as $patch) { Chris@0: if ($patch->supports($node)) { Chris@0: $patch->apply($node); Chris@0: } Chris@0: } Chris@0: Chris@0: $this->creator->create($name, $node); Chris@0: Chris@0: return $name; Chris@0: } Chris@0: }