1<?php
2
3/*
4 * This file is part of the Prophecy.
5 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
6 *     Marcello Duarte <marcello.duarte@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Prophecy\Doubler;
13
14use Prophecy\Exception\Doubler\DoubleException;
15use Prophecy\Exception\Doubler\ClassNotFoundException;
16use Prophecy\Exception\Doubler\InterfaceNotFoundException;
17use ReflectionClass;
18
19/**
20 * Lazy double.
21 * Gives simple interface to describe double before creating it.
22 *
23 * @author Konstantin Kudryashov <ever.zet@gmail.com>
24 */
25class LazyDouble
26{
27    private $doubler;
28    private $class;
29    private $interfaces = array();
30    private $arguments  = null;
31    private $double;
32
33    /**
34     * Initializes lazy double.
35     *
36     * @param Doubler $doubler
37     */
38    public function __construct(Doubler $doubler)
39    {
40        $this->doubler = $doubler;
41    }
42
43    /**
44     * Tells doubler to use specific class as parent one for double.
45     *
46     * @param string|ReflectionClass $class
47     *
48     * @throws \Prophecy\Exception\Doubler\ClassNotFoundException
49     * @throws \Prophecy\Exception\Doubler\DoubleException
50     */
51    public function setParentClass($class)
52    {
53        if (null !== $this->double) {
54            throw new DoubleException('Can not extend class with already instantiated double.');
55        }
56
57        if (!$class instanceof ReflectionClass) {
58            if (!class_exists($class)) {
59                throw new ClassNotFoundException(sprintf('Class %s not found.', $class), $class);
60            }
61
62            $class = new ReflectionClass($class);
63        }
64
65        $this->class = $class;
66    }
67
68    /**
69     * Tells doubler to implement specific interface with double.
70     *
71     * @param string|ReflectionClass $interface
72     *
73     * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException
74     * @throws \Prophecy\Exception\Doubler\DoubleException
75     */
76    public function addInterface($interface)
77    {
78        if (null !== $this->double) {
79            throw new DoubleException(
80                'Can not implement interface with already instantiated double.'
81            );
82        }
83
84        if (!$interface instanceof ReflectionClass) {
85            if (!interface_exists($interface)) {
86                throw new InterfaceNotFoundException(
87                    sprintf('Interface %s not found.', $interface),
88                    $interface
89                );
90            }
91
92            $interface = new ReflectionClass($interface);
93        }
94
95        $this->interfaces[] = $interface;
96    }
97
98    /**
99     * Sets constructor arguments.
100     *
101     * @param array $arguments
102     */
103    public function setArguments(array $arguments = null)
104    {
105        $this->arguments = $arguments;
106    }
107
108    /**
109     * Creates double instance or returns already created one.
110     *
111     * @return DoubleInterface
112     */
113    public function getInstance()
114    {
115        if (null === $this->double) {
116            if (null !== $this->arguments) {
117                return $this->double = $this->doubler->double(
118                    $this->class, $this->interfaces, $this->arguments
119                );
120            }
121
122            $this->double = $this->doubler->double($this->class, $this->interfaces);
123        }
124
125        return $this->double;
126    }
127}
128