Completed
Push — master ( e401e5...bd7189 )
by Julien
01:48
created

ClassDefinition::newInstance()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 30
rs 8.439
cc 6
eloc 19
nc 8
nop 2
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\DefinitionInterface;
37
use Fwk\Di\Exception;
38
use Fwk\Di\Exceptions\InvalidClassDefinitionException;
39
use Fwk\Di\Exceptions;
40
use Fwk\Di\InvokableInterface;
41
42
/**
43
 * ClassDefinition
44
 * 
45
 * Represents a Definition returning an instance of some class.
46
 *
47
 * @category Definition
48
 * @package  Fwk\Di
49
 * @author   Julien Ballestracci <[email protected]>
50
 * @license  http://www.opensource.org/licenses/bsd-license.php  BSD License
51
 * @link     http://www.nitronet.org/fwk
52
 */
53
class ClassDefinition extends AbstractArgsDefinition implements DefinitionInterface
54
{
55
    /**
56
     * Name of the class to be instanciated
57
     * @var string
58
     */
59
    protected $className;
60
    
61
    /**
62
     * List of class' methods to be called after instanciation
63
     * @var array<string>
64
     */
65
    protected $methodCalls  = array();
66
    
67
    /**
68
     * Constructor
69
     * 
70
     * @param string       $className Name of the class to be instanciated
71
     * @param array<mixed> $arguments List of (constructor) arguments
72
     * 
73
     * @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...
74
     */
75
    public function __construct($className, array $arguments = array())
76
    {
77
        $this->className    = $className;
78
        $this->arguments    = $arguments;
79
    }
80
    
81
    /**
82
     * Instanciates $this->className and return the instance.
83
     * 
84
     * @param Container   $container The Di Container
85
     * @param null|string $name      Name of the current definition (if any)
86
     * 
87
     * @return object
88
     * @throws Exceptions\InvalidClassDefinitionException
89
     */
90
    public function invoke(Container $container, $name = null)
91
    {
92
        if ($this->className instanceof InvokableInterface) {
93
            try {
94
                $this->className = $this->className->invoke($container, $name);
95
            } catch(Exception $exp) {
96
                throw new InvalidClassDefinitionException($this->className, $name, $exp);
97
            }
98
        } 
99
        
100
        if (!is_string($this->className)) {
101
            throw new InvalidClassDefinitionException(
102
                '???', 
103
                new \InvalidArgumentException(
104
                    sprintf(
105
                        'Classname must be a string or a Fwk\Di\Reference ' .
106
                        'instance (' . (is_object($this->className) 
107
                            ? get_class($this->className) 
108
                            : gettype($this->className)
109
                        ) . ' given)'
110
                    )
111
                )
112
            );
113
        }
114
        
115
        $instance = $this->newInstance($container, $name);
116
        $this->executeMethodCalls($instance, $container, $name);
117
        
118
        return $instance;
119
    }
120
    
121
    /**
122
     * Executes registered methods (in the order they were added)
123
     * 
124
     * @param object      $instance   The class instance
125
     * @param Container   $container  The Di Container
126
     * @param null|string $definition Name of the current definition
127
     * 
128
     * @return void
129
     * @throws Exceptions\InvalidClassDefinitionException
130
     */
131
    protected function executeMethodCalls($instance, Container $container, 
132
        $definition = null
133
    ) {
134
        foreach ($this->methodCalls as $methodCall) {
135
            $callable = $methodCall->getCallable();
136
            if (is_string($callable)) {
137
                $callable = $container->propertizeString($callable);
138
            }
139
            
140
            if (!is_callable($callable)) {
141
                $callable = array($instance, $callable);
142
            }
143
            $methodCall->setCallable($callable);
144
            $methodCall->invoke($container, $definition);
145
            $methodCall->setCallable($callable);
146
        }
147
    }
148
    
149
    /**
150
     * Instanciates the class ($this->className) 
151
     * 
152
     * @param Container   $container  The Di Container
153
     * @param null|string $definition Name of the current definition (if any)
154
     * 
155
     * @return object
156
     * @throws Exceptions\ClassNotFoundException
157
     * @throws Exceptions\InvalidClassDefinitionException
158
     */
159
    protected function newInstance(Container $container, $definition = null)
160
    {
161
        if (is_string($this->className) && strpos($this->className, ':') >= 0) {
162
            $this->className = $container->propertizeString($this->className);
163
        }
164
        
165
        if (!class_exists($this->className, true)) {
166
            throw new Exceptions\ClassNotFoundException($this->className);
167
        }
168
        
169
        $reflect    = new \ReflectionClass($this->className);
170
        if (null !== $reflect->getConstructor()) {
171
            $args = array();
172
            try {
173
                $args = $this->getConstructorArguments($container, $definition);
174
            } catch(Exception $exp) {
175
                throw new InvalidClassDefinitionException(
176
                    $this->className, 
177
                    $definition, 
178
                    $exp
179
                );
180
            }
181
            
182
            $return = $reflect->newInstanceArgs($args);
183
        } else {
184
            $return = new $this->className();
185
        }
186
        
187
        return $return;
188
    }
189
    
190
    /**
191
     * Returns the Class name
192
     * 
193
     * @return string
194
     */
195
    public function getClassName()
196
    {
197
        return $this->className;
198
    }
199
    
200
    /**
201
     * Defines the Class name
202
     * 
203
     * @param string $className The class name
204
     * 
205
     * @return void
206
     */
207
    public function setClassName($className)
208
    {
209
        $this->className = $className;
210
    }
211
212
    /**
213
     * Adds a method to be called right after the class was instanciated
214
     * 
215
     * @param string       $methodName The name of the method to be called
216
     * @param array<mixed> $arguments  List of arguments (optional)
217
     * 
218
     * @return ClassDefinition 
219
     */
220
    public function addMethodCall($methodName, array $arguments = array())
221
    {
222
        $this->methodCalls[] = new CallableDefinition(
223
            $methodName, 
224
            $arguments
225
        );
226
227
        return $this;
228
    }
229
    
230
    /**
231
     * Removes a call to a specified method
232
     * 
233
     * @param string $methodName The method name
234
     * 
235
     * @return ClassDefinition 
236
     */
237
    public function removeMethodClass($methodName)
238
    {
239
        $this->methodCalls = array_filter(
240
            $this->methodCalls, 
241
            function ($call) use ($methodName) {
242
                return $methodName !== $call->getCallable();
243
            }
244
        );
245
        
246
        return $this;
247
    }
248
}