|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2009 Christopher Hlubek <hlubek@networkteam.com> 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * 00017 * This script is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 * GNU General Public License for more details. 00021 * 00022 * This copyright notice MUST APPEAR in all copies of the script! 00023 ***************************************************************/ 00024 00025 /** 00026 * A backport of the FLOW3 reflection service for aquiring reflection based information. 00027 * Most of the code is based on the FLOW3 reflection service. 00028 * 00029 * @package Extbase 00030 * @subpackage Reflection 00031 * @version $Id: Service.php 1789 2010-01-18 21:31:59Z jocrau $ 00032 * @api 00033 */ 00034 class Tx_Extbase_Reflection_Service implements t3lib_Singleton { 00035 00036 /** 00037 * Whether this service has been initialized. 00038 * 00039 * @var boolean 00040 */ 00041 protected $initialized = FALSE; 00042 00043 /** 00044 * @var t3lib_cache_frontend_VariableFrontend 00045 */ 00046 protected $dataCache; 00047 00048 /** 00049 * Whether class alterations should be detected on each initialization. 00050 * 00051 * @var boolean 00052 */ 00053 protected $detectClassChanges = FALSE; 00054 00055 /** 00056 * All available class names to consider. Class name = key, value is the 00057 * UNIX timestamp the class was reflected. 00058 * 00059 * @var array 00060 */ 00061 protected $reflectedClassNames = array(); 00062 00063 /** 00064 * Array of tags and the names of classes which are tagged with them. 00065 * 00066 * @var array 00067 */ 00068 protected $taggedClasses = array(); 00069 00070 /** 00071 * Array of class names and their tags and values. 00072 * 00073 * @var array 00074 */ 00075 protected $classTagsValues = array(); 00076 00077 /** 00078 * Array of class names, method names and their tags and values. 00079 * 00080 * @var array 00081 */ 00082 protected $methodTagsValues = array(); 00083 00084 /** 00085 * Array of class names, method names, their parameters and additional 00086 * information about the parameters. 00087 * 00088 * @var array 00089 */ 00090 protected $methodParameters = array(); 00091 00092 /** 00093 * Array of class names and names of their properties. 00094 * 00095 * @var array 00096 */ 00097 protected $classPropertyNames = array(); 00098 00099 /** 00100 * Array of class names, property names and their tags and values. 00101 * 00102 * @var array 00103 */ 00104 protected $propertyTagsValues = array(); 00105 00106 /** 00107 * List of tags which are ignored while reflecting class and method annotations. 00108 * 00109 * @var array 00110 */ 00111 protected $ignoredTags = array('package', 'subpackage', 'license', 'copyright', 'author', 'version', 'const'); 00112 00113 /** 00114 * Indicates whether the Reflection cache needs to be updated. 00115 * 00116 * This flag needs to be set as soon as new Reflection information was 00117 * created. 00118 * 00119 * @see reflectClass() 00120 * @see getMethodReflection() 00121 * 00122 * @var boolean 00123 */ 00124 protected $dataCacheNeedsUpdate = FALSE; 00125 00126 /** 00127 * Local cache for Class schemata 00128 * @var array 00129 */ 00130 protected $classSchemata = array(); 00131 00132 /** 00133 * @var Tx_Extbase_Configuration_ConfigurationManagerInterface 00134 */ 00135 protected $configurationManager; 00136 00137 /** 00138 * @var string 00139 */ 00140 protected $cacheIdentifier; 00141 00142 /** 00143 * @param Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager 00144 * @return void 00145 */ 00146 public function injectConfigurationManager(Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager) { 00147 $this->configurationManager = $configurationManager; 00148 } 00149 00150 /** 00151 * Sets the data cache. 00152 * 00153 * The cache must be set before initializing the Reflection Service. 00154 * 00155 * @param t3lib_cache_frontend_VariableFrontend $dataCache Cache for the Reflection service 00156 * @return void 00157 */ 00158 public function setDataCache(t3lib_cache_frontend_VariableFrontend $dataCache) { 00159 $this->dataCache = $dataCache; 00160 } 00161 00162 /** 00163 * Initializes this service 00164 * 00165 * @return void 00166 */ 00167 public function initialize() { 00168 if ($this->initialized) { 00169 throw new Tx_Extbase_Reflection_Exception('The Reflection Service can only be initialized once.', 1232044696); 00170 } 00171 $frameworkConfiguration = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK); 00172 $this->cacheIdentifier = 'ReflectionData_' . $frameworkConfiguration['extensionName']; 00173 00174 $this->loadFromCache(); 00175 00176 $this->initialized = TRUE; 00177 } 00178 00179 /** 00180 * Returns whether the Reflection Service is initialized. 00181 * 00182 * @return boolean true if the Reflection Service is initialized, otherwise false 00183 */ 00184 public function isInitialized() { 00185 return $this->initialized; 00186 } 00187 00188 /** 00189 * Shuts the Reflection Service down. 00190 * 00191 * @return void 00192 */ 00193 public function shutdown() { 00194 if ($this->dataCacheNeedsUpdate) { 00195 $this->saveToCache(); 00196 } 00197 $this->initialized = FALSE; 00198 } 00199 00200 /** 00201 * Returns the names of all properties of the specified class 00202 * 00203 * @param string $className Name of the class to return the property names of 00204 * @return array An array of property names or an empty array if none exist 00205 */ 00206 public function getClassPropertyNames($className) { 00207 if (!isset($this->reflectedClassNames[$className])) $this->reflectClass($className); 00208 return (isset($this->classPropertyNames[$className])) ? $this->classPropertyNames[$className] : array(); 00209 } 00210 00211 /** 00212 * Returns the class schema for the given class 00213 * 00214 * @param mixed $classNameOrObject The class name or an object 00215 * @return Tx_Extbase_Reflection_ClassSchema 00216 * @author Karsten Dambekalns <karsten@typo3.org> 00217 */ 00218 public function getClassSchema($classNameOrObject) { 00219 $className = is_object($classNameOrObject) ? get_class($classNameOrObject) : $classNameOrObject; 00220 if (isset($this->classSchemata[$className])) { 00221 return $this->classSchemata[$className]; 00222 } else { 00223 return $this->buildClassSchema($className); 00224 } 00225 00226 } 00227 00228 /** 00229 * Returns all tags and their values the specified method is tagged with 00230 * 00231 * @param string $className Name of the class containing the method 00232 * @param string $methodName Name of the method to return the tags and values of 00233 * @return array An array of tags and their values or an empty array of no tags were found 00234 */ 00235 public function getMethodTagsValues($className, $methodName) { 00236 if (!isset($this->methodTagsValues[$className][$methodName])) { 00237 $this->methodTagsValues[$className][$methodName] = array(); 00238 $method = $this->getMethodReflection($className, $methodName); 00239 foreach ($method->getTagsValues() as $tag => $values) { 00240 if (array_search($tag, $this->ignoredTags) === FALSE) { 00241 $this->methodTagsValues[$className][$methodName][$tag] = $values; 00242 } 00243 } 00244 } 00245 return $this->methodTagsValues[$className][$methodName]; 00246 } 00247 00248 00249 /** 00250 * Returns an array of parameters of the given method. Each entry contains 00251 * additional information about the parameter position, type hint etc. 00252 * 00253 * @param string $className Name of the class containing the method 00254 * @param string $methodName Name of the method to return parameter information of 00255 * @return array An array of parameter names and additional information or an empty array of no parameters were found 00256 */ 00257 public function getMethodParameters($className, $methodName) { 00258 if (!isset($this->methodParameters[$className][$methodName])) { 00259 $method = $this->getMethodReflection($className, $methodName); 00260 $this->methodParameters[$className][$methodName] = array(); 00261 foreach($method->getParameters() as $parameterPosition => $parameter) { 00262 $this->methodParameters[$className][$methodName][$parameter->getName()] = $this->convertParameterReflectionToArray($parameter, $parameterPosition, $method); 00263 } 00264 } 00265 return $this->methodParameters[$className][$methodName]; 00266 } 00267 00268 /** 00269 * Returns all tags and their values the specified class property is tagged with 00270 * 00271 * @param string $className Name of the class containing the property 00272 * @param string $propertyName Name of the property to return the tags and values of 00273 * @return array An array of tags and their values or an empty array of no tags were found 00274 */ 00275 public function getPropertyTagsValues($className, $propertyName) { 00276 if (!isset($this->reflectedClassNames[$className])) $this->reflectClass($className); 00277 if (!isset($this->propertyTagsValues[$className])) return array(); 00278 return (isset($this->propertyTagsValues[$className][$propertyName])) ? $this->propertyTagsValues[$className][$propertyName] : array(); 00279 } 00280 00281 /** 00282 * Returns the values of the specified class property tag 00283 * 00284 * @param string $className Name of the class containing the property 00285 * @param string $propertyName Name of the tagged property 00286 * @param string $tag Tag to return the values of 00287 * @return array An array of values or an empty array if the tag was not found 00288 * @author Robert Lemke <robert@typo3.org> 00289 * @api 00290 */ 00291 public function getPropertyTagValues($className, $propertyName, $tag) { 00292 if (!isset($this->reflectedClassNames[$className])) $this->reflectClass($className); 00293 if (!isset($this->propertyTagsValues[$className][$propertyName])) return array(); 00294 return (isset($this->propertyTagsValues[$className][$propertyName][$tag])) ? $this->propertyTagsValues[$className][$propertyName][$tag] : array(); 00295 } 00296 00297 /** 00298 * Tells if the specified class is known to this reflection service and 00299 * reflection information is available. 00300 * 00301 * @param string $className Name of the class 00302 * @return boolean If the class is reflected by this service 00303 * @author Robert Lemke <robert@typo3.org> 00304 * @api 00305 */ 00306 public function isClassReflected($className) { 00307 return isset($this->reflectedClassNames[$className]); 00308 } 00309 00310 /** 00311 * Tells if the specified class is tagged with the given tag 00312 * 00313 * @param string $className Name of the class 00314 * @param string $tag Tag to check for 00315 * @return boolean TRUE if the class is tagged with $tag, otherwise FALSE 00316 * @author Robert Lemke <robert@typo3.org> 00317 * @api 00318 */ 00319 public function isClassTaggedWith($className, $tag) { 00320 if ($this->initialized === FALSE) return FALSE; 00321 if (!isset($this->reflectedClassNames[$className])) $this->reflectClass($className); 00322 if (!isset($this->classTagsValues[$className])) return FALSE; 00323 return isset($this->classTagsValues[$className][$tag]); 00324 } 00325 00326 /** 00327 * Tells if the specified class property is tagged with the given tag 00328 * 00329 * @param string $className Name of the class 00330 * @param string $propertyName Name of the property 00331 * @param string $tag Tag to check for 00332 * @return boolean TRUE if the class property is tagged with $tag, otherwise FALSE 00333 * @author Robert Lemke <robert@typo3.org> 00334 * @api 00335 */ 00336 public function isPropertyTaggedWith($className, $propertyName, $tag) { 00337 if (!isset($this->reflectedClassNames[$className])) $this->reflectClass($className); 00338 if (!isset($this->propertyTagsValues[$className])) return FALSE; 00339 if (!isset($this->propertyTagsValues[$className][$propertyName])) return FALSE; 00340 return isset($this->propertyTagsValues[$className][$propertyName][$tag]); 00341 } 00342 00343 /** 00344 * Reflects the given class and stores the results in this service's properties. 00345 * 00346 * @param string $className Full qualified name of the class to reflect 00347 * @return void 00348 */ 00349 protected function reflectClass($className) { 00350 $class = new Tx_Extbase_Reflection_ClassReflection($className); 00351 $this->reflectedClassNames[$className] = time(); 00352 00353 foreach ($class->getTagsValues() as $tag => $values) { 00354 if (array_search($tag, $this->ignoredTags) === FALSE) { 00355 $this->taggedClasses[$tag][] = $className; 00356 $this->classTagsValues[$className][$tag] = $values; 00357 } 00358 } 00359 00360 foreach ($class->getProperties() as $property) { 00361 $propertyName = $property->getName(); 00362 $this->classPropertyNames[$className][] = $propertyName; 00363 00364 foreach ($property->getTagsValues() as $tag => $values) { 00365 if (array_search($tag, $this->ignoredTags) === FALSE) { 00366 $this->propertyTagsValues[$className][$propertyName][$tag] = $values; 00367 } 00368 } 00369 } 00370 00371 foreach ($class->getMethods() as $method) { 00372 $methodName = $method->getName(); 00373 foreach ($method->getTagsValues() as $tag => $values) { 00374 if (array_search($tag, $this->ignoredTags) === FALSE) { 00375 $this->methodTagsValues[$className][$methodName][$tag] = $values; 00376 } 00377 } 00378 00379 foreach ($method->getParameters() as $parameterPosition => $parameter) { 00380 $this->methodParameters[$className][$methodName][$parameter->getName()] = $this->convertParameterReflectionToArray($parameter, $parameterPosition, $method); 00381 } 00382 } 00383 ksort($this->reflectedClassNames); 00384 00385 $this->dataCacheNeedsUpdate = TRUE; 00386 } 00387 00388 /** 00389 * Builds class schemata from classes annotated as entities or value objects 00390 * 00391 * @return Tx_Extbase_Reflection_ClassSchema The class schema 00392 */ 00393 protected function buildClassSchema($className) { 00394 if (!class_exists($className)) { 00395 return NULL; 00396 } 00397 $classSchema = new Tx_Extbase_Reflection_ClassSchema($className); 00398 if (is_subclass_of($className, 'Tx_Extbase_DomainObject_AbstractEntity')) { 00399 $classSchema->setModelType(Tx_Extbase_Reflection_ClassSchema::MODELTYPE_ENTITY); 00400 00401 $possibleRepositoryClassName = str_replace('_Model_', '_Repository_', $className) . 'Repository'; 00402 if (class_exists($possibleRepositoryClassName)) { 00403 $classSchema->setAggregateRoot(TRUE); 00404 } 00405 } elseif (is_subclass_of($className, 'Tx_Extbase_DomainObject_AbstractValueObject')) { 00406 $classSchema->setModelType(Tx_Extbase_Reflection_ClassSchema::MODELTYPE_VALUEOBJECT); 00407 } else { 00408 return NULL; 00409 } 00410 00411 foreach ($this->getClassPropertyNames($className) as $propertyName) { 00412 if (!$this->isPropertyTaggedWith($className, $propertyName, 'transient') && $this->isPropertyTaggedWith($className, $propertyName, 'var')) { 00413 $cascadeTagValues = $this->getPropertyTagValues($className, $propertyName, 'cascade'); 00414 $classSchema->addProperty($propertyName, implode(' ', $this->getPropertyTagValues($className, $propertyName, 'var')), $this->isPropertyTaggedWith($className, $propertyName, 'lazy'), $cascadeTagValues[0]); 00415 } 00416 if ($this->isPropertyTaggedWith($className, $propertyName, 'uuid')) { 00417 $classSchema->setUUIDPropertyName($propertyName); 00418 } 00419 if ($this->isPropertyTaggedWith($className, $propertyName, 'identity')) { 00420 $classSchema->markAsIdentityProperty($propertyName); 00421 } 00422 } 00423 $this->classSchemata[$className] = $classSchema; 00424 $this->dataCacheNeedsUpdate = TRUE; 00425 return $classSchema; 00426 } 00427 00428 /** 00429 * Converts the given parameter reflection into an information array 00430 * 00431 * @param ReflectionParameter $parameter The parameter to reflect 00432 * @return array Parameter information array 00433 */ 00434 protected function convertParameterReflectionToArray(ReflectionParameter $parameter, $parameterPosition, ReflectionMethod $method = NULL) { 00435 $parameterInformation = array( 00436 'position' => $parameterPosition, 00437 'byReference' => $parameter->isPassedByReference() ? TRUE : FALSE, 00438 'array' => $parameter->isArray() ? TRUE : FALSE, 00439 'optional' => $parameter->isOptional() ? TRUE : FALSE, 00440 'allowsNull' => $parameter->allowsNull() ? TRUE : FALSE 00441 ); 00442 00443 $parameterClass = $parameter->getClass(); 00444 $parameterInformation['class'] = ($parameterClass !== NULL) ? $parameterClass->getName() : NULL; 00445 if ($parameter->isDefaultValueAvailable()) { 00446 $parameterInformation['defaultValue'] = $parameter->getDefaultValue(); 00447 } 00448 if ($parameterClass !== NULL) { 00449 $parameterInformation['type'] = $parameterClass->getName(); 00450 } elseif ($method !== NULL) { 00451 $methodTagsAndValues = $this->getMethodTagsValues($method->getDeclaringClass()->getName(), $method->getName()); 00452 if (isset($methodTagsAndValues['param']) && isset($methodTagsAndValues['param'][$parameterPosition])) { 00453 $explodedParameters = explode(' ', $methodTagsAndValues['param'][$parameterPosition]); 00454 if (count($explodedParameters) >= 2) { 00455 $parameterInformation['type'] = $explodedParameters[0]; 00456 } 00457 } 00458 } 00459 if (isset($parameterInformation['type']) && $parameterInformation['type']{0} === '\\') { 00460 $parameterInformation['type'] = substr($parameterInformation['type'], 1); 00461 } 00462 return $parameterInformation; 00463 } 00464 00465 /** 00466 * Returns the Reflection of a method. 00467 * 00468 * @param string $className Name of the class containing the method 00469 * @param string $methodName Name of the method to return the Reflection for 00470 * @return Tx_Extbase_Reflection_MethodReflection the method Reflection object 00471 */ 00472 protected function getMethodReflection($className, $methodName) { 00473 if (!isset($this->methodReflections[$className][$methodName])) { 00474 $this->methodReflections[$className][$methodName] = new Tx_Extbase_Reflection_MethodReflection($className, $methodName); 00475 $this->dataCacheNeedsUpdate = TRUE; 00476 } 00477 return $this->methodReflections[$className][$methodName]; 00478 } 00479 00480 /** 00481 * Tries to load the reflection data from this service's cache. 00482 * 00483 * @return void 00484 */ 00485 protected function loadFromCache() { 00486 if ($this->dataCache->has($this->cacheIdentifier)) { 00487 $data = $this->dataCache->get($this->cacheIdentifier); 00488 foreach ($data as $propertyName => $propertyValue) { 00489 $this->$propertyName = $propertyValue; 00490 } 00491 } 00492 } 00493 00494 /** 00495 * Exports the internal reflection data into the ReflectionData cache. 00496 * 00497 * @return void 00498 */ 00499 protected function saveToCache() { 00500 if (!is_object($this->dataCache)) { 00501 throw new Tx_Extbase_Reflection_Exception( 00502 'A cache must be injected before initializing the Reflection Service.', 00503 1232044697 00504 ); 00505 } 00506 00507 $data = array(); 00508 $propertyNames = array( 00509 'reflectedClassNames', 00510 'classPropertyNames', 00511 'classTagsValues', 00512 'methodTagsValues', 00513 'methodParameters', 00514 'propertyTagsValues', 00515 'taggedClasses', 00516 'classSchemata' 00517 ); 00518 foreach ($propertyNames as $propertyName) { 00519 $data[$propertyName] = $this->$propertyName; 00520 } 00521 $this->dataCache->set($this->cacheIdentifier, $data); 00522 } 00523 00524 } 00525 ?>
1.8.0