/** * Reflects the given class and stores the results in this service's properties. * * @param string $className Full qualified name of the class to reflect * @return void * @throws Exception\InvalidClassException */ protected function reflectClass($className) { $this->log(sprintf('Reflecting class %s', $className), LOG_DEBUG); $className = $this->cleanClassName($className); if (strpos($className, 'TYPO3\\Flow\\Persistence\\Doctrine\\Proxies') === 0 && in_array(\Doctrine\ORM\Proxy\Proxy::class, class_implements($className))) { // Somebody tried to reflect a doctrine proxy, which will have severe side effects. // see bug http://forge.typo3.org/issues/29449 for details. throw new Exception\InvalidClassException('The class with name "' . $className . '" is a Doctrine proxy. It is not supported to reflect doctrine proxy classes.', 1314944681); } $class = new ClassReflection($className); if (!isset($this->classReflectionData[$className])) { $this->classReflectionData[$className] = []; } if ($class->isAbstract() || $class->isInterface()) { $this->classReflectionData[$className][self::DATA_CLASS_ABSTRACT] = true; } if ($class->isFinal()) { $this->classReflectionData[$className][self::DATA_CLASS_FINAL] = true; } /** @var $parentClass ClassReflection */ foreach ($this->getParentClasses($class) as $parentClass) { $this->addParentClass($className, $parentClass); } /** @var $interface ClassReflection */ foreach ($class->getInterfaces() as $interface) { $this->addImplementedInterface($className, $interface); } foreach ($this->annotationReader->getClassAnnotations($class) as $annotation) { $annotationClassName = get_class($annotation); $this->annotatedClasses[$annotationClassName][$className] = true; $this->classReflectionData[$className][self::DATA_CLASS_ANNOTATIONS][] = $annotation; } /** @var $property PropertyReflection */ foreach ($class->getProperties() as $property) { $this->reflectClassProperty($className, $property); } foreach ($class->getMethods() as $method) { $this->reflectClassMethod($className, $method); } // Sort reflection data so that the cache data is deterministic. This is // important for comparisons when checking if classes have changed in a // Development context. ksort($this->classReflectionData); $this->updatedReflectionData[$className] = true; }
/** * Reflects the given class and stores the results in this service's properties. * * @param string $className Full qualified name of the class to reflect * @return void * @throws \TYPO3\Flow\Reflection\Exception\InvalidClassException */ protected function reflectClass($className) { $this->log(sprintf('Reflecting class %s', $className), LOG_DEBUG); $className = trim($className, '\\'); if (strpos($className, 'TYPO3\\Flow\\Persistence\\Doctrine\\Proxies') === 0 && array_search('Doctrine\\ORM\\Proxy\\Proxy', class_implements($className))) { // Somebody tried to reflect a doctrine proxy, which will have severe side effects. // see bug http://forge.typo3.org/issues/29449 for details. throw new Exception\InvalidClassException('The class with name "' . $className . '" is a Doctrine proxy. It is not supported to reflect doctrine proxy classes.', 1314944681); } try { $class = new ClassReflection($className); } catch (Exception\ClassLoadingForReflectionFailedException $exception) { $this->markClassUnconfigurable($className); $this->log('Could not reflect "' . $className . '" because the class could not be loaded.', LOG_DEBUG); return; } if (!isset($this->classReflectionData[$className])) { $this->classReflectionData[$className] = array(); } if ($class->isAbstract()) { $this->classReflectionData[$className][self::DATA_CLASS_ABSTRACT] = TRUE; } if ($class->isFinal()) { $this->classReflectionData[$className][self::DATA_CLASS_FINAL] = TRUE; } /** @var $parentClass \TYPO3\Flow\Reflection\ClassReflection */ foreach ($this->getParentClasses($class) as $parentClass) { $parentClassName = $parentClass->getName(); if (!isset($this->classReflectionData[$parentClassName])) { $this->reflectClass($parentClassName); } $this->classReflectionData[$parentClassName][self::DATA_CLASS_SUBCLASSES][$className] = TRUE; } /** @var $interface \TYPO3\Flow\Reflection\ClassReflection */ foreach ($class->getInterfaces() as $interface) { if (!isset($this->classReflectionData[$className][self::DATA_CLASS_ABSTRACT])) { $interfaceName = $interface->getName(); if (!isset($this->classReflectionData[$interfaceName])) { $this->reflectClass($interfaceName); } $this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS][$className] = TRUE; } } foreach ($this->annotationReader->getClassAnnotations($class) as $annotation) { $annotationClassName = get_class($annotation); $this->annotatedClasses[$annotationClassName][$className] = TRUE; $this->classReflectionData[$className][self::DATA_CLASS_ANNOTATIONS][] = $annotation; } /** @var $property \TYPO3\Flow\Reflection\PropertyReflection */ foreach ($class->getProperties() as $property) { $propertyName = $property->getName(); $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName] = array(); $visibility = $property->isPublic() ? self::VISIBILITY_PUBLIC : ($property->isProtected() ? self::VISIBILITY_PROTECTED : self::VISIBILITY_PRIVATE); $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_VISIBILITY] = $visibility; foreach ($property->getTagsValues() as $tag => $values) { if (array_search($tag, $this->settings['reflection']['ignoredTags']) === FALSE) { if ($tag === 'var' && isset($values[0])) { if ($property->getDeclaringClass()->getName() !== $className && isset($this->classReflectionData[$property->getDeclaringClass()->getName()][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES][$tag])) { $values = $this->classReflectionData[$property->getDeclaringClass()->getName()][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES][$tag]; } else { $values[0] = $this->expandType($class, $values[0]); } } $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES][$tag] = $values; } } foreach ($this->annotationReader->getPropertyAnnotations($property, $propertyName) as $annotation) { $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_ANNOTATIONS][get_class($annotation)][] = $annotation; } } /** @var $method \TYPO3\Flow\Reflection\MethodReflection */ foreach ($class->getMethods() as $method) { $methodName = $method->getName(); if ($method->isFinal()) { $this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_FINAL] = TRUE; } if ($method->isStatic()) { $this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_STATIC] = TRUE; } $visibility = $method->isPublic() ? self::VISIBILITY_PUBLIC : ($method->isProtected() ? self::VISIBILITY_PROTECTED : self::VISIBILITY_PRIVATE); $this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY] = $visibility; foreach ($this->getMethodAnnotations($className, $methodName) as $methodAnnotation) { $this->classesByMethodAnnotations[get_class($methodAnnotation)][$className] = $methodName; } $paramAnnotations = $method->isTaggedWith('param') ? $method->getTagValues('param') : array(); /** @var $parameter \TYPO3\Flow\Reflection\ParameterReflection */ foreach ($method->getParameters() as $parameter) { $this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_PARAMETERS][$parameter->getName()] = $this->convertParameterReflectionToArray($parameter, $method); if ($this->settings['reflection']['logIncorrectDocCommentHints'] === TRUE) { if (isset($paramAnnotations[$parameter->getPosition()])) { $parameterAnnotation = explode(' ', $paramAnnotations[$parameter->getPosition()], 3); if (count($parameterAnnotation) < 2) { $this->log(' Wrong @param use for "' . $method->getName() . '::' . $parameter->getName() . '": "' . implode(' ', $parameterAnnotation) . '"', LOG_DEBUG); } else { if (isset($this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_PARAMETERS][$parameter->getName()][self::DATA_PARAMETER_TYPE]) && $this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_PARAMETERS][$parameter->getName()][self::DATA_PARAMETER_TYPE] !== ltrim($parameterAnnotation[0], '\\')) { $this->log(' Wrong type in @param for "' . $method->getName() . '::' . $parameter->getName() . '": "' . $parameterAnnotation[0] . '"', LOG_DEBUG); } if ($parameter->getName() !== ltrim($parameterAnnotation[1], '$&')) { $this->log(' Wrong name in @param for "' . $method->getName() . '::$' . $parameter->getName() . '": "' . $parameterAnnotation[1] . '"', LOG_DEBUG); } } } else { $this->log(' Missing @param for "' . $method->getName() . '::$' . $parameter->getName(), LOG_DEBUG); } } } } // Sort reflection data so that the cache data is deterministic. This is // important for comparisons when checking if classes have changed in a // Development context. ksort($this->classReflectionData); $this->updatedReflectionData[$className] = TRUE; }