/** * @return Cli\Command[] */ protected function getDomainCommands() { $cliCommands = []; $domainCommandClassNames = $this->reflectionService->getAllImplementationClassNamesForInterface(Domain\CommandInterface::class); foreach ($domainCommandClassNames as $domainCommandClassName) { if ($this->reflectionService->isClassAbstract($domainCommandClassName) === TRUE) { continue; } $cliCommands[] = $this->buildDomainCommand($domainCommandClassName); } return $cliCommands; }
/** * Get all class names inside this namespace and return them as array. * * @param string $namespace * @return array Array of all class names inside a given namespace. */ protected function getClassNamesInNamespace($namespace) { $affectedViewHelperClassNames = array(); $allViewHelperClassNames = $this->reflectionService->getAllSubClassNamesForClass('TYPO3\\Fluid\\Core\\ViewHelper\\AbstractViewHelper'); foreach ($allViewHelperClassNames as $viewHelperClassName) { if ($this->reflectionService->isClassAbstract($viewHelperClassName)) { continue; } if (strncmp($namespace, $viewHelperClassName, strlen($namespace)) === 0) { $affectedViewHelperClassNames[] = $viewHelperClassName; } } sort($affectedViewHelperClassNames); return $affectedViewHelperClassNames; }
/** * Lists all public controller actions not covered by the active security policy * * @return void */ public function showUnprotectedActionsCommand() { $controllerClassNames = $this->reflectionService->getAllSubClassNamesForClass('TYPO3\\Flow\\Mvc\\Controller\\AbstractController'); $allActionsAreProtected = TRUE; foreach ($controllerClassNames as $controllerClassName) { if ($this->reflectionService->isClassAbstract($controllerClassName)) { continue; } $methodNames = get_class_methods($controllerClassName); $foundUnprotectedAction = FALSE; foreach ($methodNames as $methodName) { if (preg_match('/.*Action$/', $methodName) === 0 || $this->reflectionService->isMethodPublic($controllerClassName, $methodName) === FALSE) { continue; } if ($this->policyService->hasPolicyEntryForMethod($controllerClassName, $methodName) === FALSE) { if ($foundUnprotectedAction === FALSE) { $this->outputLine(PHP_EOL . '<b>' . $controllerClassName . '</b>'); $foundUnprotectedAction = TRUE; $allActionsAreProtected = FALSE; } $this->outputLine(' ' . $methodName); } } } if ($allActionsAreProtected === TRUE) { $this->outputLine('All public controller actions are covered by your security policy. Good job!'); } }
/** * Analyzes the Object Configuration provided by the compiler and builds the necessary PHP code for the proxy classes * to realize dependency injection. * * @return void */ public function build() { $this->objectConfigurations = $this->objectManager->getObjectConfigurations(); foreach ($this->objectConfigurations as $objectName => $objectConfiguration) { $className = $objectConfiguration->getClassName(); if ($className === '' || $this->compiler->hasCacheEntryForClass($className) === true) { continue; } if ($objectName !== $className || $this->reflectionService->isClassAbstract($className)) { continue; } $proxyClass = $this->compiler->getProxyClass($className); if ($proxyClass === false) { continue; } $this->systemLogger->log('Building DI proxy for "' . $className . '".', LOG_DEBUG); $constructorPreCode = ''; $constructorPostCode = ''; $constructorPreCode .= $this->buildSetInstanceCode($objectConfiguration); $constructorPreCode .= $this->buildConstructorInjectionCode($objectConfiguration); $setRelatedEntitiesCode = ''; if (!$this->reflectionService->hasMethod($className, '__sleep')) { $proxyClass->addTraits(['\\TYPO3\\Flow\\Object\\Proxy\\ObjectSerializationTrait']); $sleepMethod = $proxyClass->getMethod('__sleep'); $sleepMethod->addPostParentCallCode($this->buildSerializeRelatedEntitiesCode($objectConfiguration)); $setRelatedEntitiesCode = "\n " . '$this->Flow_setRelatedEntities();' . "\n"; } $wakeupMethod = $proxyClass->getMethod('__wakeup'); $wakeupMethod->addPreParentCallCode($this->buildSetInstanceCode($objectConfiguration)); $wakeupMethod->addPreParentCallCode($setRelatedEntitiesCode); $wakeupMethod->addPostParentCallCode($this->buildLifecycleInitializationCode($objectConfiguration, \TYPO3\Flow\Object\ObjectManagerInterface::INITIALIZATIONCAUSE_RECREATED)); $wakeupMethod->addPostParentCallCode($this->buildLifecycleShutdownCode($objectConfiguration)); $injectPropertiesCode = $this->buildPropertyInjectionCode($objectConfiguration); if ($injectPropertiesCode !== '') { $proxyClass->addTraits(['\\TYPO3\\Flow\\Object\\DependencyInjection\\PropertyInjectionTrait']); $proxyClass->getMethod('Flow_Proxy_injectProperties')->addPreParentCallCode($injectPropertiesCode); $proxyClass->getMethod('Flow_Proxy_injectProperties')->overrideMethodVisibility('private'); $wakeupMethod->addPreParentCallCode(" \$this->Flow_Proxy_injectProperties();\n"); $constructorPostCode .= ' if (\'' . $className . '\' === get_class($this)) {' . "\n"; $constructorPostCode .= ' $this->Flow_Proxy_injectProperties();' . "\n"; $constructorPostCode .= ' }' . "\n"; } $constructorPostCode .= $this->buildLifecycleInitializationCode($objectConfiguration, \TYPO3\Flow\Object\ObjectManagerInterface::INITIALIZATIONCAUSE_CREATED); $constructorPostCode .= $this->buildLifecycleShutdownCode($objectConfiguration); $constructor = $proxyClass->getConstructor(); $constructor->addPreParentCallCode($constructorPreCode); $constructor->addPostParentCallCode($constructorPostCode); if ($this->objectManager->getContext()->isProduction()) { $this->compileStaticMethods($className, $proxyClass); } } }
/** * Analyzes the Object Configuration provided by the compiler and builds the necessary PHP code for the proxy classes * to realize dependency injection. * * @return void */ public function build() { $this->objectConfigurations = $this->objectManager->getObjectConfigurations(); foreach ($this->objectConfigurations as $objectName => $objectConfiguration) { $className = $objectConfiguration->getClassName(); if ($className === '' || $this->compiler->hasCacheEntryForClass($className) === TRUE) { continue; } if ($objectName !== $className || $this->reflectionService->isClassAbstract($className) || $this->reflectionService->isClassFinal($className)) { continue; } $proxyClass = $this->compiler->getProxyClass($className); if ($proxyClass === FALSE) { continue; } $this->systemLogger->log('Building DI proxy for "' . $className . '".', LOG_DEBUG); $constructorPreCode = ''; $constructorPostCode = ''; $constructorPreCode .= $this->buildSetInstanceCode($objectConfiguration); $constructorPreCode .= $this->buildConstructorInjectionCode($objectConfiguration); $wakeupMethod = $proxyClass->getMethod('__wakeup'); $wakeupMethod->addPreParentCallCode($this->buildSetInstanceCode($objectConfiguration)); $wakeupMethod->addPreParentCallCode($this->buildSetRelatedEntitiesCode()); $wakeupMethod->addPostParentCallCode($this->buildLifecycleInitializationCode($objectConfiguration, \TYPO3\Flow\Object\ObjectManagerInterface::INITIALIZATIONCAUSE_RECREATED)); $wakeupMethod->addPostParentCallCode($this->buildLifecycleShutdownCode($objectConfiguration)); $sleepMethod = $proxyClass->getMethod('__sleep'); $sleepMethod->addPostParentCallCode($this->buildSerializeRelatedEntitiesCode($objectConfiguration)); $searchForEntitiesAndStoreIdentifierArrayMethod = $proxyClass->getMethod('searchForEntitiesAndStoreIdentifierArray'); $searchForEntitiesAndStoreIdentifierArrayMethod->setMethodParametersCode('$path, $propertyValue, $originalPropertyName'); $searchForEntitiesAndStoreIdentifierArrayMethod->overrideMethodVisibility('private'); $searchForEntitiesAndStoreIdentifierArrayMethod->addPreParentCallCode($this->buildSearchForEntitiesAndStoreIdentifierArrayCode()); $injectPropertiesCode = $this->buildPropertyInjectionCode($objectConfiguration); if ($injectPropertiesCode !== '') { $proxyClass->getMethod('Flow_Proxy_injectProperties')->addPreParentCallCode($injectPropertiesCode); $proxyClass->getMethod('Flow_Proxy_injectProperties')->overrideMethodVisibility('private'); $wakeupMethod->addPreParentCallCode("\t\t\$this->Flow_Proxy_injectProperties();\n"); $constructorPostCode .= ' if (\'' . $className . '\' === get_class($this)) {' . "\n"; $constructorPostCode .= ' $this->Flow_Proxy_injectProperties();' . "\n"; $constructorPostCode .= ' }' . "\n"; } $constructorPostCode .= $this->buildLifecycleInitializationCode($objectConfiguration, \TYPO3\Flow\Object\ObjectManagerInterface::INITIALIZATIONCAUSE_CREATED); $constructorPostCode .= $this->buildLifecycleShutdownCode($objectConfiguration); $constructor = $proxyClass->getConstructor(); $constructor->addPreParentCallCode($constructorPreCode); $constructor->addPostParentCallCode($constructorPostCode); if ($this->objectManager->getContext()->isProduction()) { $this->compileStaticMethods($className, $proxyClass); } } }
/** * Renders and returns the PHP code for this ProxyClass. * * @return string */ public function render() { $namespace = $this->namespace; $proxyClassName = $this->originalClassName; $originalClassName = $this->originalClassName . \TYPO3\Flow\Object\Proxy\Compiler::ORIGINAL_CLASSNAME_SUFFIX; $abstractKeyword = $this->reflectionService->isClassAbstract($this->fullOriginalClassName) ? 'abstract ' : ''; $constantsCode = $this->renderConstantsCode(); $propertiesCode = $this->renderPropertiesCode(); $methodsCode = isset($this->constructor) ? $this->constructor->render() : ''; foreach ($this->methods as $proxyMethod) { $methodsCode .= $proxyMethod->render(); } if ($methodsCode . $constantsCode === '') { return ''; } $classCode = ($namespace !== '' ? "namespace {$namespace};\n\n" : '') . "use Doctrine\\ORM\\Mapping as ORM;\n" . "use TYPO3\\Flow\\Annotations as Flow;\n" . "\n" . $this->buildClassDocumentation() . $abstractKeyword . "class {$proxyClassName} extends {$originalClassName} implements " . implode(', ', array_unique($this->interfaces)) . " {\n\n" . $constantsCode . $propertiesCode . $methodsCode . "}"; return $classCode; }
/** * @param array $classesSelector * @return array */ protected function getAffectedClassNames(array $classesSelector) { if (isset($classesSelector['parentClassName'])) { $affectedClassNames = $this->reflectionService->getAllSubClassNamesForClass($classesSelector['parentClassName']); } elseif (isset($classesSelector['interface'])) { $affectedClassNames = $this->reflectionService->getAllImplementationClassNamesForInterface($classesSelector['interface']); } elseif (isset($classesSelector['classesContainingMethodsAnnotatedWith'])) { $affectedClassNames = $this->reflectionService->getClassesContainingMethodsAnnotatedWith($classesSelector['classesContainingMethodsAnnotatedWith']); } else { $affectedClassNames = $this->reflectionService->getAllClassNames(); } foreach ($affectedClassNames as $index => $className) { if ($this->reflectionService->isClassAbstract($className) && (!isset($classesSelector['includeAbstractClasses']) || $classesSelector['includeAbstractClasses'] === FALSE)) { unset($affectedClassNames[$index]); } elseif (isset($classesSelector['classNamePattern']) && preg_match($classesSelector['classNamePattern'], $className) === 0) { unset($affectedClassNames[$index]); } } return $affectedClassNames; }
/** * Renders and returns the PHP code for this ProxyClass. * * @return string */ public function render() { $namespace = $this->namespace; $proxyClassName = $this->originalClassName; $originalClassName = $this->originalClassName . \TYPO3\Flow\Object\Proxy\Compiler::ORIGINAL_CLASSNAME_SUFFIX; $classModifier = ''; if ($this->reflectionService->isClassAbstract($this->fullOriginalClassName)) { $classModifier = 'abstract '; } elseif ($this->reflectionService->isClassFinal($this->fullOriginalClassName)) { $classModifier = 'final '; } $constantsCode = $this->renderConstantsCode(); $propertiesCode = $this->renderPropertiesCode(); $traitsCode = $this->renderTraitsCode(); $methodsCode = isset($this->constructor) ? $this->constructor->render() : ''; foreach ($this->methods as $proxyMethod) { $methodsCode .= $proxyMethod->render(); } if ($methodsCode . $constantsCode === '') { return ''; } $classCode = ($namespace !== '' ? 'namespace ' . $namespace . ";\n\n" : '') . "use Doctrine\\ORM\\Mapping as ORM;\n" . "use TYPO3\\Flow\\Annotations as Flow;\n" . "\n" . $this->buildClassDocumentation() . $classModifier . 'class ' . $proxyClassName . ' extends ' . $originalClassName . ' implements ' . implode(', ', array_unique($this->interfaces)) . " {\n\n" . $traitsCode . $constantsCode . $propertiesCode . $methodsCode . '}'; return $classCode; }
/** * Lists all public controller actions not covered by the active security policy * * @return void */ public function showUnprotectedActionsCommand() { $methodPrivileges = array(); foreach ($this->policyService->getRoles(true) as $role) { $methodPrivileges = array_merge($methodPrivileges, $role->getPrivilegesByType(\TYPO3\Flow\Security\Authorization\Privilege\Method\MethodPrivilegeInterface::class)); } $controllerClassNames = $this->reflectionService->getAllSubClassNamesForClass(\TYPO3\Flow\Mvc\Controller\AbstractController::class); $allActionsAreProtected = true; foreach ($controllerClassNames as $controllerClassName) { if ($this->reflectionService->isClassAbstract($controllerClassName)) { continue; } $methodNames = get_class_methods($controllerClassName); $foundUnprotectedAction = false; foreach ($methodNames as $methodName) { if (preg_match('/.*Action$/', $methodName) === 0 || $this->reflectionService->isMethodPublic($controllerClassName, $methodName) === false) { continue; } /** @var MethodPrivilegeInterface $methodPrivilege */ foreach ($methodPrivileges as $methodPrivilege) { if ($methodPrivilege->matchesMethod($controllerClassName, $methodName)) { continue 2; } } if ($foundUnprotectedAction === false) { $this->outputLine(PHP_EOL . '<b>' . $controllerClassName . '</b>'); $foundUnprotectedAction = true; $allActionsAreProtected = false; } $this->outputLine(' ' . $methodName); } } if ($allActionsAreProtected === true) { $this->outputLine('All public controller actions are covered by your security policy. Good job!'); } }
/** * Loads the metadata for the specified class into the provided container. * * @param string $className * @param ClassMetadata $metadata * @return void * @throws MappingException * @throws \UnexpectedValueException * @todo adjust when Doctrine 2.5 is used, see http://www.doctrine-project.org/jira/browse/DDC-93 */ public function loadMetadataForClass($className, ClassMetadata $metadata) { /** * This is the actual type we have at this point, but we cannot change the * signature due to inheritance. * * @var OrmClassMetadata $metadata */ $class = $metadata->getReflectionClass(); $classSchema = $this->getClassSchema($class->getName()); $classAnnotations = $this->reader->getClassAnnotations($class); // Evaluate Entity annotation if (isset($classAnnotations['Doctrine\\ORM\\Mapping\\MappedSuperclass'])) { $mappedSuperclassAnnotation = $classAnnotations['Doctrine\\ORM\\Mapping\\MappedSuperclass']; if ($mappedSuperclassAnnotation->repositoryClass !== null) { $metadata->setCustomRepositoryClass($mappedSuperclassAnnotation->repositoryClass); } $metadata->isMappedSuperclass = true; } elseif (isset($classAnnotations[\TYPO3\Flow\Annotations\Entity::class]) || isset($classAnnotations['Doctrine\\ORM\\Mapping\\Entity'])) { $entityAnnotation = isset($classAnnotations[\TYPO3\Flow\Annotations\Entity::class]) ? $classAnnotations[\TYPO3\Flow\Annotations\Entity::class] : $classAnnotations['Doctrine\\ORM\\Mapping\\Entity']; if ($entityAnnotation->repositoryClass !== null) { $metadata->setCustomRepositoryClass($entityAnnotation->repositoryClass); } elseif ($classSchema->getRepositoryClassName() !== null) { if ($this->reflectionService->isClassImplementationOf($classSchema->getRepositoryClassName(), 'Doctrine\\ORM\\EntityRepository')) { $metadata->setCustomRepositoryClass($classSchema->getRepositoryClassName()); } } if ($entityAnnotation->readOnly) { $metadata->markReadOnly(); } } elseif ($classSchema->getModelType() === ClassSchema::MODELTYPE_VALUEOBJECT) { // also ok... but we make it read-only $metadata->markReadOnly(); } else { throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); } // Evaluate Table annotation $primaryTable = array(); if (isset($classAnnotations['Doctrine\\ORM\\Mapping\\Table'])) { $tableAnnotation = $classAnnotations['Doctrine\\ORM\\Mapping\\Table']; $primaryTable = array('name' => $tableAnnotation->name, 'schema' => $tableAnnotation->schema); if ($tableAnnotation->indexes !== null) { foreach ($tableAnnotation->indexes as $indexAnnotation) { $index = array('columns' => $indexAnnotation->columns); if (!empty($indexAnnotation->name)) { $primaryTable['indexes'][$indexAnnotation->name] = $index; } else { $primaryTable['indexes'][] = $index; } } } if ($tableAnnotation->uniqueConstraints !== null) { foreach ($tableAnnotation->uniqueConstraints as $uniqueConstraint) { $uniqueConstraint = array('columns' => $uniqueConstraint->columns); if (!empty($uniqueConstraint->name)) { $primaryTable['uniqueConstraints'][$uniqueConstraint->name] = $uniqueConstraint; } else { $primaryTable['uniqueConstraints'][] = $uniqueConstraint; } } } if ($tableAnnotation->options !== null) { $primaryTable['options'] = $tableAnnotation->options; } } if (!isset($primaryTable['name'])) { $className = $classSchema->getClassName(); $primaryTable['name'] = $this->inferTableNameFromClassName($className); } // Evaluate NamedNativeQueries annotation if (isset($classAnnotations['Doctrine\\ORM\\Mapping\\NamedNativeQueries'])) { $namedNativeQueriesAnnotation = $classAnnotations['Doctrine\\ORM\\Mapping\\NamedNativeQueries']; foreach ($namedNativeQueriesAnnotation->value as $namedNativeQuery) { $metadata->addNamedNativeQuery(array('name' => $namedNativeQuery->name, 'query' => $namedNativeQuery->query, 'resultClass' => $namedNativeQuery->resultClass, 'resultSetMapping' => $namedNativeQuery->resultSetMapping)); } } // Evaluate SqlResultSetMappings annotation if (isset($classAnnotations['Doctrine\\ORM\\Mapping\\SqlResultSetMappings'])) { $sqlResultSetMappingsAnnotation = $classAnnotations['Doctrine\\ORM\\Mapping\\SqlResultSetMappings']; foreach ($sqlResultSetMappingsAnnotation->value as $resultSetMapping) { $entities = array(); $columns = array(); foreach ($resultSetMapping->entities as $entityResultAnnotation) { $entityResult = array('fields' => array(), 'entityClass' => $entityResultAnnotation->entityClass, 'discriminatorColumn' => $entityResultAnnotation->discriminatorColumn); foreach ($entityResultAnnotation->fields as $fieldResultAnnotation) { $entityResult['fields'][] = array('name' => $fieldResultAnnotation->name, 'column' => $fieldResultAnnotation->column); } $entities[] = $entityResult; } foreach ($resultSetMapping->columns as $columnResultAnnotation) { $columns[] = array('name' => $columnResultAnnotation->name); } $metadata->addSqlResultSetMapping(array('name' => $resultSetMapping->name, 'entities' => $entities, 'columns' => $columns)); } } // Evaluate NamedQueries annotation if (isset($classAnnotations['Doctrine\\ORM\\Mapping\\NamedQueries'])) { $namedQueriesAnnotation = $classAnnotations['Doctrine\\ORM\\Mapping\\NamedQueries']; if (!is_array($namedQueriesAnnotation->value)) { throw new \UnexpectedValueException('@NamedQueries should contain an array of @NamedQuery annotations.'); } foreach ($namedQueriesAnnotation->value as $namedQuery) { if (!$namedQuery instanceof NamedQuery) { throw new \UnexpectedValueException('@NamedQueries should contain an array of @NamedQuery annotations.'); } $metadata->addNamedQuery(array('name' => $namedQuery->name, 'query' => $namedQuery->query)); } } // Evaluate InheritanceType annotation if (isset($classAnnotations['Doctrine\\ORM\\Mapping\\InheritanceType'])) { $inheritanceTypeAnnotation = $classAnnotations['Doctrine\\ORM\\Mapping\\InheritanceType']; $inheritanceType = constant('Doctrine\\ORM\\Mapping\\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($inheritanceTypeAnnotation->value)); if ($inheritanceType !== OrmClassMetadata::INHERITANCE_TYPE_NONE) { // Evaluate DiscriminatorColumn annotation if (isset($classAnnotations['Doctrine\\ORM\\Mapping\\DiscriminatorColumn'])) { $discriminatorColumnAnnotation = $classAnnotations['Doctrine\\ORM\\Mapping\\DiscriminatorColumn']; $discriminatorColumn = array('name' => $discriminatorColumnAnnotation->name, 'type' => $discriminatorColumnAnnotation->type, 'length' => $discriminatorColumnAnnotation->length, 'columnDefinition' => $discriminatorColumnAnnotation->columnDefinition); } else { $discriminatorColumn = array('name' => 'dtype', 'type' => 'string', 'length' => 255); } // Evaluate DiscriminatorMap annotation if (isset($classAnnotations['Doctrine\\ORM\\Mapping\\DiscriminatorMap'])) { $discriminatorMapAnnotation = $classAnnotations['Doctrine\\ORM\\Mapping\\DiscriminatorMap']; $discriminatorMap = $discriminatorMapAnnotation->value; } else { $discriminatorMap = array(); $subclassNames = $this->reflectionService->getAllSubClassNamesForClass($className); if (!$this->reflectionService->isClassAbstract($className)) { $mappedClassName = strtolower(str_replace('Domain_Model_', '', str_replace('\\', '_', $className))); $discriminatorMap[$mappedClassName] = $className; } foreach ($subclassNames as $subclassName) { $mappedSubclassName = strtolower(str_replace('Domain_Model_', '', str_replace('\\', '_', $subclassName))); $discriminatorMap[$mappedSubclassName] = $subclassName; } } if ($discriminatorMap !== array()) { $metadata->setDiscriminatorColumn($discriminatorColumn); $metadata->setDiscriminatorMap($discriminatorMap); } else { $inheritanceType = OrmClassMetadata::INHERITANCE_TYPE_NONE; } } $metadata->setInheritanceType($inheritanceType); } // Evaluate DoctrineChangeTrackingPolicy annotation if (isset($classAnnotations['Doctrine\\ORM\\Mapping\\ChangeTrackingPolicy'])) { $changeTrackingAnnotation = $classAnnotations['Doctrine\\ORM\\Mapping\\ChangeTrackingPolicy']; $metadata->setChangeTrackingPolicy(constant('Doctrine\\ORM\\Mapping\\ClassMetadata::CHANGETRACKING_' . strtoupper($changeTrackingAnnotation->value))); } else { $metadata->setChangeTrackingPolicy(OrmClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT); } // Evaluate annotations on properties/fields try { $this->evaluatePropertyAnnotations($metadata); } catch (MappingException $exception) { throw new MappingException(sprintf('Failure while evaluating property annotations for class "%s": %s', $metadata->getName(), $exception->getMessage()), 1382003497, $exception); } // build unique index for table if (!isset($primaryTable['uniqueConstraints'])) { $idProperties = array_keys($classSchema->getIdentityProperties()); if (array_diff($idProperties, $metadata->getIdentifierFieldNames()) !== array()) { $uniqueIndexName = $this->truncateIdentifier('flow_identity_' . $primaryTable['name']); foreach ($idProperties as $idProperty) { $primaryTable['uniqueConstraints'][$uniqueIndexName]['columns'][] = isset($metadata->columnNames[$idProperty]) ? $metadata->columnNames[$idProperty] : strtolower($idProperty); } } } $metadata->setPrimaryTable($primaryTable); // Evaluate AssociationOverrides annotation $this->evaluateOverridesAnnotations($classAnnotations, $metadata); // Evaluate EntityListeners annotation $this->evaluateEntityListenersAnnotation($class, $metadata, $classAnnotations); // Evaluate @HasLifecycleCallbacks annotation $this->evaluateLifeCycleAnnotations($class, $metadata); }