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

ClassDefinition.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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