Completed
Push — feat-events-tags ( 999033...033a2d )
by Julien
04:43
created

ClassDefinition::invoke()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 5

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 30
ccs 21
cts 21
cp 1
rs 8.439
cc 5
eloc 19
nc 5
nop 2
crap 5
1
<?php
2
/**
3
 * Fwk
4
 *
5
 * Copyright (c) 2011-2012, Julien Ballestracci <[email protected]>.
6
 * All rights reserved.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
12
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
13
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
15
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
16
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
17
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
21
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
 * POSSIBILITY OF SUCH DAMAGE.
23
 *
24
 * PHP Version 5.3
25
 *
26
 * @category  DependencyInjection
27
 * @package   Fwk\Di
28
 * @author    Julien Ballestracci <[email protected]>
29
 * @copyright 2011-2014 Julien Ballestracci <[email protected]>
30
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
31
 * @link      http://www.nitronet.org/fwk
32
 */
33
namespace Fwk\Di\Definitions;
34
35
use Fwk\Di\Container;
36
use Fwk\Di\Exception;
37
use Fwk\Di\Exceptions\InvalidClassDefinitionException;
38
use Fwk\Di\InvokableInterface;
39
use Fwk\Di\Exceptions;
40
41
/**
42
 * ClassDefinition
43
 * 
44
 * Represents a Definition returning an instance of some class.
45
 *
46
 * @category Definition
47
 * @package  Fwk\Di
48
 * @author   Julien Ballestracci <[email protected]>
49
 * @license  http://www.opensource.org/licenses/bsd-license.php  BSD License
50
 * @link     http://www.nitronet.org/fwk
51
 */
52
class ClassDefinition extends AbstractDefinition implements InvokableInterface
53
{
54
    /**
55
     * Name of the class to be instanciated
56
     * @var string
57
     */
58
    protected $className;
59
    
60
    /**
61
     * List of class' methods to be called after instanciation
62
     * @var array<string>
63
     */
64
    protected $methodCalls  = array();
65
    
66
    /**
67
     * Constructor
68
     * 
69
     * @param string       $className Name of the class to be instanciated
70
     * @param array<mixed> $arguments List of (constructor) arguments
71
     * 
72
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
73
     */
74 21
    public function __construct($className, array $arguments = array())
75
    {
76 21
        $this->className    = $className;
77 21
        $this->arguments    = $arguments;
78 21
    }
79
    
80
    /**
81
     * Instanciates $this->className and return the instance.
82
     * 
83
     * @param Container   $container The Di Container
84
     * @param null|string $name      Name of the current definition (if any)
85
     * 
86
     * @return object
87
     * @throws Exceptions\InvalidClassDefinitionException
88
     */
89 20
    public function invoke(Container $container, $name = null)
90
    {
91 20
        if ($this->className instanceof InvokableInterface) {
92
            try {
93 2
                $this->className = $this->className->invoke($container, $name);
94 2
            } catch(Exception $exp) {
95 1
                throw new InvalidClassDefinitionException($this->className, $name, $exp);
96
            }
97 1
        } 
98
        
99 19
        if (!is_string($this->className)) {
100 1
            throw new InvalidClassDefinitionException(
101 1
                '???', 
102 1
                new \InvalidArgumentException(
103 1
                    sprintf(
104
                        'Classname must be a string or a Fwk\Di\Reference ' .
105 1
                        'instance (' . (is_object($this->className) 
106 1
                            ? get_class($this->className) 
107 1
                            : gettype($this->className)
108 1
                        ) . ' given)'
109 1
                    )
110 1
                )
111 1
            );
112
        }
113
        
114 18
        $instance = $this->newInstance($container, $name);
115 16
        $this->executeMethodCalls($instance, $container, $name);
116
        
117 15
        return $instance;
118
    }
119
    
120
    /**
121
     * Executes registered methods (in the order they were added)
122
     * 
123
     * @param object      $instance   The class instance
124
     * @param Container   $container  The Di Container
125
     * @param null|string $definition Name of the current definition
126
     * 
127
     * @return void
128
     * @throws Exceptions\InvalidClassDefinitionException
129
     */
130 16
    protected function executeMethodCalls($instance, Container $container, 
131
        $definition = null
132
    ) {
133 16
        foreach ($this->methodCalls as $methodCall) {
134 4
            $callable = $methodCall->getCallable();
135 4
            if (is_string($callable)) {
136 4
                $callable = $container->propertizeString($callable);
137 4
            }
138
            
139 4
            if (!is_callable($callable)) {
140 4
                $callable = array($instance, $callable);
141 4
            }
142 4
            $methodCall->setCallable($callable);
143 4
            $methodCall->invoke($container, $definition);
144 3
            $methodCall->setCallable($callable);
145 15
        }
146 15
    }
147
    
148
    /**
149
     * Instanciates the class ($this->className) 
150
     * 
151
     * @param Container   $container  The Di Container
152
     * @param null|string $definition Name of the current definition (if any)
153
     * 
154
     * @return object
155
     * @throws Exceptions\ClassNotFoundException
156
     * @throws Exceptions\InvalidClassDefinitionException
157
     */
158 18
    protected function newInstance(Container $container, $definition = null)
159
    {
160 18
        if (is_string($this->className) && strpos($this->className, ':') >= 0) {
161 18
            $this->className = $container->propertizeString($this->className);
162 18
        }
163
        
164 18
        if (!class_exists($this->className, true)) {
165 1
            throw new Exceptions\ClassNotFoundException($this->className);
166
        }
167
        
168 17
        $reflect    = new \ReflectionClass($this->className);
169 17
        if (null !== $reflect->getConstructor()) {
170 8
            $args = array();
171
            try {
172 8
                $args = $this->getConstructorArguments($container, $definition);
173 8
            } catch(Exception $exp) {
174 1
                throw new InvalidClassDefinitionException(
175 1
                    $this->className, 
176 1
                    $definition, 
177
                    $exp
178 1
                );
179
            }
180
            
181 7
            $return = $reflect->newInstanceArgs($args);
182 7
        } else {
183 10
            $return = new $this->className();
184
        }
185
        
186 16
        return $return;
187
    }
188
    
189
    /**
190
     * Returns the Class name
191
     * 
192
     * @return string
193
     */
194 2
    public function getClassName()
195
    {
196 2
        return $this->className;
197
    }
198
    
199
    /**
200
     * Defines the Class name
201
     * 
202
     * @param string $className The class name
203
     * 
204
     * @return void
205
     */
206 2
    public function setClassName($className)
207
    {
208 2
        $this->className = $className;
209 2
    }
210
211
    /**
212
     * Adds a method to be called right after the class was instanciated
213
     * 
214
     * @param string       $methodName The name of the method to be called
215
     * @param array<mixed> $arguments  List of arguments (optional)
216
     * 
217
     * @return ClassDefinition 
218
     */
219 8
    public function addMethodCall($methodName, array $arguments = array())
220
    {
221 8
        $this->methodCalls[] = new CallableDefinition(
222 8
            $methodName, 
223
            $arguments
224 8
        );
225
226 8
        return $this;
227
    }
228
    
229
    /**
230
     * Removes a call to a specified method
231
     * 
232
     * @param string $methodName The method name
233
     * 
234
     * @return ClassDefinition 
235
     */
236 1
    public function removeMethodClass($methodName)
237
    {
238 1
        $this->methodCalls = array_filter(
239 1
            $this->methodCalls, 
240 1
            function ($call) use ($methodName) {
241 1
                return $methodName !== $call->getCallable();
242
            }
243 1
        );
244
        
245 1
        return $this;
246
    }
247
}