4 * This file is part of the Prophecy.
5 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
6 * Marcello Duarte <marcello.duarte@gmail.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Prophecy\Doubler;
14 use Doctrine\Instantiator\Instantiator;
15 use Prophecy\Doubler\ClassPatch\ClassPatchInterface;
16 use Prophecy\Doubler\Generator\ClassMirror;
17 use Prophecy\Doubler\Generator\ClassCreator;
18 use Prophecy\Exception\InvalidArgumentException;
22 * Cached class doubler.
23 * Prevents mirroring/creation of the same structure twice.
25 * @author Konstantin Kudryashov <ever.zet@gmail.com>
34 * @var ClassPatchInterface[]
36 private $patches = array();
39 * @var \Doctrine\Instantiator\Instantiator
41 private $instantiator;
44 * Initializes doubler.
46 * @param ClassMirror $mirror
47 * @param ClassCreator $creator
48 * @param NameGenerator $namer
50 public function __construct(ClassMirror $mirror = null, ClassCreator $creator = null,
51 NameGenerator $namer = null)
53 $this->mirror = $mirror ?: new ClassMirror;
54 $this->creator = $creator ?: new ClassCreator;
55 $this->namer = $namer ?: new NameGenerator;
59 * Returns list of registered class patches.
61 * @return ClassPatchInterface[]
63 public function getClassPatches()
65 return $this->patches;
69 * Registers new class patch.
71 * @param ClassPatchInterface $patch
73 public function registerClassPatch(ClassPatchInterface $patch)
75 $this->patches[] = $patch;
77 @usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) {
78 return $patch2->getPriority() - $patch1->getPriority();
83 * Creates double from specific class or/and list of interfaces.
85 * @param ReflectionClass $class
86 * @param ReflectionClass[] $interfaces Array of ReflectionClass instances
87 * @param array $args Constructor arguments
89 * @return DoubleInterface
91 * @throws \Prophecy\Exception\InvalidArgumentException
93 public function double(ReflectionClass $class = null, array $interfaces, array $args = null)
95 foreach ($interfaces as $interface) {
96 if (!$interface instanceof ReflectionClass) {
97 throw new InvalidArgumentException(sprintf(
98 "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n".
99 "a second argument to `Doubler::double(...)`, but got %s.",
100 is_object($interface) ? get_class($interface).' class' : gettype($interface)
105 $classname = $this->createDoubleClass($class, $interfaces);
106 $reflection = new ReflectionClass($classname);
108 if (null !== $args) {
109 return $reflection->newInstanceArgs($args);
111 if ((null === $constructor = $reflection->getConstructor())
112 || ($constructor->isPublic() && !$constructor->isFinal())) {
113 return $reflection->newInstance();
116 if (!$this->instantiator) {
117 $this->instantiator = new Instantiator();
120 return $this->instantiator->instantiate($classname);
124 * Creates double class and returns its FQN.
126 * @param ReflectionClass $class
127 * @param ReflectionClass[] $interfaces
131 protected function createDoubleClass(ReflectionClass $class = null, array $interfaces)
133 $name = $this->namer->name($class, $interfaces);
134 $node = $this->mirror->reflect($class, $interfaces);
136 foreach ($this->patches as $patch) {
137 if ($patch->supports($node)) {
138 $patch->apply($node);
142 $this->creator->create($name, $node);