[TASK] Extbase (Object): Adjust doc comments
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Object / Container / Container.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2010 Extbase Team
6 * All rights reserved
7 *
8 * This class is a backport of the corresponding class of FLOW3.
9 * All credits go to the v5 team.
10 *
11 * This script is part of the TYPO3 project. The TYPO3 project is
12 * free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * The GNU General Public License can be found at
18 * http://www.gnu.org/copyleft/gpl.html.
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * Internal TYPO3 Dependency Injection container
30 *
31 * @author Daniel Pötzinger
32 * @author Sebastian Kurfürst
33 */
34 class Tx_Extbase_Object_Container_Container implements t3lib_Singleton {
35
36 /**
37 * internal cache for classinfos
38 *
39 * @var Tx_Extbase_Object_Container_ClassInfoCache
40 */
41 private $cache;
42
43 /**
44 * registered alternative implementations of a class
45 * e.g. used to know the class for a AbstractClass or a Dependency
46 *
47 * @var array
48 */
49 private $alternativeImplementation;
50
51 /**
52 * reference to the classinfofactory, that analyses dependencys
53 *
54 * @var Tx_Extbase_Object_Container_ClassInfoFactory
55 */
56 private $classInfoFactory;
57
58 /**
59 * holds references of singletons
60 *
61 * @var array
62 */
63 private $singletonInstances = array();
64
65 /**
66 * Constructor is protected since container should
67 * be a singleton.
68 *
69 * @see getContainer()
70 * @param void
71 * @return void
72 */
73 public function __construct() {
74 $this->classInfoFactory = t3lib_div::makeInstance('Tx_Extbase_Object_Container_ClassInfoFactory');
75 $this->cache = t3lib_div::makeInstance('Tx_Extbase_Object_Container_ClassInfoCache');
76 }
77
78 /**
79 * Main method which should be used to get an instance of the wished class
80 * specified by $className.
81 *
82 * @param string $className
83 * @param array $givenConstructorArguments the list of constructor arguments as array
84 * @return object the built object
85 */
86 public function getInstance($className, $givenConstructorArguments = array()) {
87 $className = $this->getImplementationClassName($className);
88
89 if ($className === 'Tx_Extbase_Object_Container_Container') {
90 return $this;
91 }
92
93 if (isset($this->singletonInstances[$className])) {
94 if (count($givenConstructorArguments) > 0) {
95 throw new Tx_Extbase_Object_Exception('Object "' . $className . '" fetched from singleton cache, thus, explicit constructor arguments are not allowed.', 1292857934);
96 }
97 return $this->singletonInstances[$className];
98 }
99 $classInfo = $this->getClassInfo($className);
100
101 $instance = $this->instanciateObject($className, $classInfo, $givenConstructorArguments);
102 $this->injectDependencies($instance, $classInfo);
103
104 if (method_exists($instance, 'initializeObject') && is_callable(array($instance, 'initializeObject'))) {
105 $instance->initializeObject();
106 }
107
108 return $instance;
109 }
110
111 /**
112 * Instanciates an object, possibly setting the constructor dependencies.
113 * Additionally, directly registers all singletons in the singleton registry,
114 * such that circular references of singletons are correctly instanciated.
115 *
116 * @param string $className
117 * @param Tx_Extbase_Object_Container_ClassInfo $classInfo
118 * @param array $givenConstructorArguments
119 * @return object the new instance
120 */
121 protected function instanciateObject($className, Tx_Extbase_Object_Container_ClassInfo $classInfo, array $givenConstructorArguments) {
122 $classIsSingleton = $this->isSingleton($className);
123
124 if ($classIsSingleton && count($givenConstructorArguments) > 0) {
125 throw new Tx_Extbase_Object_Exception('Object "' . $className . '" has explicit constructor arguments but is a singleton; this is not allowed.', 1292858051);
126 }
127
128 $constructorArguments = $this->getConstructorArguments($classInfo->getConstructorArguments(), $givenConstructorArguments, $level);
129 array_unshift($constructorArguments, $className);
130 $instance = call_user_func_array(array('t3lib_div', 'makeInstance'), $constructorArguments);
131
132 if ($classIsSingleton) {
133 $this->singletonInstances[$className] = $instance;
134 }
135 return $instance;
136 }
137
138 /**
139 * Inject setter-dependencies into $instance
140 *
141 * @param object $instance
142 * @param Tx_Extbase_Object_Container_ClassInfo $classInfo
143 * @return void
144 */
145 protected function injectDependencies($instance, Tx_Extbase_Object_Container_ClassInfo $classInfo) {
146 if (!$classInfo->hasInjectMethods()) return;
147
148 foreach ($classInfo->getInjectMethods() as $injectMethodName => $classNameToInject) {
149
150 $instanceToInject = $this->getInstance($classNameToInject);
151 if (!$instanceToInject instanceof t3lib_Singleton) {
152 throw new Tx_Extbase_Object_Exception_WrongScope('Setter dependencies can only be singletons', 1292860859);
153 }
154 $instance->$injectMethodName($instanceToInject);
155 }
156 }
157
158 /**
159 * register a classname that should be used if a dependency is required.
160 * e.g. used to define default class for a interface
161 *
162 * @param string $className
163 * @param string $alternativeClassName
164 */
165 public function registerImplementation($className,$alternativeClassName) {
166 $this->alternativeImplementation[$className] = $alternativeClassName;
167 }
168
169 /**
170 * gets array of parameter that can be used to call a constructor
171 *
172 * @param array $constructorArgumentInformation
173 * @param array $givenConstructorArguments
174 * @return array
175 */
176 private function getConstructorArguments(array $constructorArgumentInformation, array $givenConstructorArguments, $level) {
177 $parameters=array();
178 foreach ($constructorArgumentInformation as $argumentInformation) {
179 $argumentName = $argumentInformation['name'];
180
181 // We have a dependency we can automatically wire,
182 // AND the class has NOT been explicitely passed in
183 if (isset($argumentInformation['dependency']) && !(count($givenConstructorArguments) && is_a($givenConstructorArguments[0], $argumentInformation['dependency']))) {
184 // Inject parameter
185 if (!$this->isSingleton($argumentInformation['dependency'])) {
186 throw new Tx_Extbase_Object_Exception_WrongScope('Constructor dependencies can only be singletons', 1292860858);
187 }
188 $parameter = $this->getInstance($argumentInformation['dependency']);
189 } elseif (count($givenConstructorArguments)) {
190 // EITHER:
191 // No dependency injectable anymore, but we still have
192 // an explicit constructor argument
193 // OR:
194 // the passed constructor argument matches the type for the dependency
195 // injection, and thus the passed constructor takes precendence over
196 // autowiring.
197 $parameter = array_shift($givenConstructorArguments);
198 } elseif (isset($argumentInformation['defaultValue'])) {
199 // no value to set anymore, we take default value
200 $parameter = $argumentInformation['defaultValue'];
201 } else {
202 throw new InvalidArgumentException('not a correct info array of constructor dependencies was passed!');
203 }
204 $parameters[] = $parameter;
205 }
206 return $parameters;
207 }
208
209 /**
210 * @param string/object $object
211 * @return boolean TRUE if the object is a singleton, FALSE if it is a prototype.
212 */
213 protected function isSingleton($object) {
214 return in_array('t3lib_Singleton', class_implements($object));
215 }
216 /**
217 * Returns the class name for a new instance, taking into account the
218 * class-extension API.
219 *
220 * @param string Base class name to evaluate
221 * @return string Final class name to instantiate with "new [classname]"
222 */
223 protected function getImplementationClassName($className) {
224 if (isset($this->alternativeImplementation[$className])) {
225 $className = $this->alternativeImplementation[$className];
226 }
227
228 if (substr($className, -9) === 'Interface') {
229 $className = substr($className, 0, -9);
230 }
231
232 return $className;
233 }
234
235 /**
236 * Gets Classinfos for the className - using the cache and the factory
237 *
238 * @param string $className
239 * @return Tx_Extbase_Object_Container_ClassInfo
240 */
241 private function getClassInfo($className) {
242 // we also need to make sure that the cache is returning a vaild object
243 // in case something went wrong with unserialization etc..
244 if (!$this->cache->has($className) || !is_object($this->cache->get($className))) {
245 $this->cache->set($className, $this->classInfoFactory->buildClassInfoFromClassName($className));
246 }
247 return $this->cache->get($className);
248 }
249 }