/**
  * After returning advice, generates the value hash for the object
  *
  * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint The current join point
  * @return void
  * @Flow\After("classAnnotatedWith(TYPO3\Flow\Annotations\ValueObject) && method(.*->__construct()) && filter(TYPO3\Flow\Persistence\Doctrine\Mapping\Driver\FlowAnnotationDriver)")
  */
 public function generateValueHash(JoinPointInterface $joinPoint)
 {
     $proxy = $joinPoint->getProxy();
     $proxyClassName = get_class($proxy);
     $hashSourceParts = array();
     $classSchema = $this->reflectionService->getClassSchema($proxyClassName);
     foreach ($classSchema->getProperties() as $property => $propertySchema) {
         // Currently, private properties are transient. Should this behaviour change, they need to be included
         // in the value hash generation
         if ($classSchema->isPropertyTransient($property) || $this->reflectionService->isPropertyPrivate($proxyClassName, $property)) {
             continue;
         }
         $propertyValue = ObjectAccess::getProperty($proxy, $property, true);
         if (is_object($propertyValue) === true) {
             // The persistence manager will return NULL if the given object is unknown to persistence
             $propertyValue = $this->persistenceManager->getIdentifierByObject($propertyValue) ?: $propertyValue;
         }
         $hashSourceParts[$property] = $propertyValue;
     }
     ksort($hashSourceParts);
     $hashSourceParts['__class_name__'] = $proxyClassName;
     $serializedSource = $this->useIgBinary === true ? igbinary_serialize($hashSourceParts) : serialize($hashSourceParts);
     $proxy = $joinPoint->getProxy();
     ObjectAccess::setProperty($proxy, 'Persistence_Object_Identifier', sha1($serializedSource), true);
 }
 /**
  * This function tries to find yet unmatched dependencies which need to be injected via "inject*" setter methods.
  *
  * @param array &$objectConfigurations
  * @return void
  * @throws \TYPO3\Flow\Object\Exception if an injected property is private
  */
 protected function autowireProperties(array &$objectConfigurations)
 {
     foreach ($objectConfigurations as $objectConfiguration) {
         $className = $objectConfiguration->getClassName();
         $properties = $objectConfiguration->getProperties();
         $classMethodNames = get_class_methods($className);
         if (!is_array($classMethodNames)) {
             if (!class_exists($className)) {
                 throw new \TYPO3\Flow\Object\Exception\UnknownClassException(sprintf('The class "%s" defined in the object configuration for object "%s", defined in package: %s, does not exist.', $className, $objectConfiguration->getObjectName(), $objectConfiguration->getPackageKey()), 1352371371);
             } else {
                 throw new \TYPO3\Flow\Object\Exception\UnknownClassException(sprintf('Could not autowire properties of class "%s" because names of methods contained in that class could not be retrieved using get_class_methods().', $className), 1352386418);
             }
         }
         foreach ($classMethodNames as $methodName) {
             if (strlen($methodName) > 6 && substr($methodName, 0, 6) === 'inject' && $methodName[6] === strtoupper($methodName[6])) {
                 $propertyName = lcfirst(substr($methodName, 6));
                 $autowiringAnnotation = $this->reflectionService->getMethodAnnotation($className, $methodName, 'TYPO3\\Flow\\Annotations\\Autowiring');
                 if ($autowiringAnnotation !== NULL && $autowiringAnnotation->enabled === FALSE) {
                     continue;
                 }
                 if ($methodName === 'injectSettings') {
                     $packageKey = $objectConfiguration->getPackageKey();
                     if ($packageKey !== NULL) {
                         $properties[$propertyName] = new ConfigurationProperty($propertyName, $packageKey, ConfigurationProperty::PROPERTY_TYPES_SETTING);
                     }
                 } else {
                     if (array_key_exists($propertyName, $properties)) {
                         continue;
                     }
                     $methodParameters = $this->reflectionService->getMethodParameters($className, $methodName);
                     if (count($methodParameters) !== 1) {
                         $this->systemLogger->log(sprintf('Could not autowire property %s because %s() expects %s instead of exactly 1 parameter.', "{$className}::{$propertyName}", $methodName, count($methodParameters) ?: 'none'), LOG_DEBUG);
                         continue;
                     }
                     $methodParameter = array_pop($methodParameters);
                     if ($methodParameter['class'] === NULL) {
                         $this->systemLogger->log(sprintf('Could not autowire property %s because the method parameter in %s() contained no class type hint.', "{$className}::{$propertyName}", $methodName), LOG_DEBUG);
                         continue;
                     }
                     $properties[$propertyName] = new ConfigurationProperty($propertyName, $methodParameter['class'], ConfigurationProperty::PROPERTY_TYPES_OBJECT);
                 }
             }
         }
         foreach ($this->reflectionService->getPropertyNamesByAnnotation($className, 'TYPO3\\Flow\\Annotations\\Inject') as $propertyName) {
             if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) {
                 $exceptionMessage = 'The property "' . $propertyName . '" in class "' . $className . '" must not be private when annotated for injection.';
                 throw new \TYPO3\Flow\Object\Exception($exceptionMessage, 1328109641);
             }
             if (!array_key_exists($propertyName, $properties)) {
                 $objectName = trim(implode('', $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var')), ' \\');
                 $properties[$propertyName] = new ConfigurationProperty($propertyName, $objectName, ConfigurationProperty::PROPERTY_TYPES_OBJECT);
             }
         }
         $objectConfiguration->setProperties($properties);
     }
 }
 /**
  * This function tries to find yet unmatched dependencies which need to be injected via "inject*" setter methods.
  *
  * @param array &$objectConfigurations
  * @return void
  * @throws \TYPO3\Flow\Object\Exception if an injected property is private
  */
 protected function autowireProperties(array &$objectConfigurations)
 {
     /** @var Configuration $objectConfiguration */
     foreach ($objectConfigurations as $objectConfiguration) {
         $className = $objectConfiguration->getClassName();
         $properties = $objectConfiguration->getProperties();
         if ($className === '') {
             continue;
         }
         $classMethodNames = get_class_methods($className);
         if (!is_array($classMethodNames)) {
             if (!class_exists($className)) {
                 throw new \TYPO3\Flow\Object\Exception\UnknownClassException(sprintf('The class "%s" defined in the object configuration for object "%s", defined in package: %s, does not exist.', $className, $objectConfiguration->getObjectName(), $objectConfiguration->getPackageKey()), 1352371371);
             } else {
                 throw new \TYPO3\Flow\Object\Exception\UnknownClassException(sprintf('Could not autowire properties of class "%s" because names of methods contained in that class could not be retrieved using get_class_methods().', $className), 1352386418);
             }
         }
         foreach ($classMethodNames as $methodName) {
             if (isset($methodName[6]) && strpos($methodName, 'inject') === 0 && $methodName[6] === strtoupper($methodName[6])) {
                 $propertyName = lcfirst(substr($methodName, 6));
                 $autowiringAnnotation = $this->reflectionService->getMethodAnnotation($className, $methodName, \TYPO3\Flow\Annotations\Autowiring::class);
                 if ($autowiringAnnotation !== NULL && $autowiringAnnotation->enabled === FALSE) {
                     continue;
                 }
                 if ($methodName === 'injectSettings') {
                     $packageKey = $objectConfiguration->getPackageKey();
                     if ($packageKey !== NULL) {
                         $properties[$propertyName] = new ConfigurationProperty($propertyName, array('type' => ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'path' => $packageKey), ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION);
                     }
                 } else {
                     if (array_key_exists($propertyName, $properties)) {
                         continue;
                     }
                     $methodParameters = $this->reflectionService->getMethodParameters($className, $methodName);
                     if (count($methodParameters) !== 1) {
                         $this->systemLogger->log(sprintf('Could not autowire property %s because %s() expects %s instead of exactly 1 parameter.', $className . '::' . $propertyName, $methodName, count($methodParameters) ?: 'none'), LOG_DEBUG);
                         continue;
                     }
                     $methodParameter = array_pop($methodParameters);
                     if ($methodParameter['class'] === NULL) {
                         $this->systemLogger->log(sprintf('Could not autowire property %s because the method parameter in %s() contained no class type hint.', $className . '::' . $propertyName, $methodName), LOG_DEBUG);
                         continue;
                     }
                     $properties[$propertyName] = new ConfigurationProperty($propertyName, $methodParameter['class'], ConfigurationProperty::PROPERTY_TYPES_OBJECT);
                 }
             }
         }
         foreach ($this->reflectionService->getPropertyNamesByAnnotation($className, \TYPO3\Flow\Annotations\Inject::class) as $propertyName) {
             if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) {
                 throw new \TYPO3\Flow\Object\Exception(sprintf('The property "%%s" in class "%s" must not be private when annotated for injection.', $propertyName, $className), 1328109641);
             }
             if (!array_key_exists($propertyName, $properties)) {
                 /** @var Inject $injectAnnotation */
                 $injectAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, \TYPO3\Flow\Annotations\Inject::class);
                 // TODO: Should be removed with 3.2. Inject settings by Inject-Annotation is deprecated since 3.0. Injecting settings by annotation should be done using the InjectConfiguration annotation
                 if ($injectAnnotation->setting !== NULL) {
                     $packageKey = $injectAnnotation->package !== NULL ? $injectAnnotation->package : $objectConfiguration->getPackageKey();
                     $configurationPath = rtrim($packageKey . '.' . $injectAnnotation->setting, '.');
                     $properties[$propertyName] = new ConfigurationProperty($propertyName, array('type' => ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'path' => $configurationPath), ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION);
                 } else {
                     $objectName = trim(implode('', $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var')), ' \\');
                     $configurationProperty = new ConfigurationProperty($propertyName, $objectName, ConfigurationProperty::PROPERTY_TYPES_OBJECT, NULL, $injectAnnotation->lazy);
                     $properties[$propertyName] = $configurationProperty;
                 }
             }
         }
         foreach ($this->reflectionService->getPropertyNamesByAnnotation($className, \TYPO3\Flow\Annotations\InjectConfiguration::class) as $propertyName) {
             if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) {
                 throw new \TYPO3\Flow\Object\Exception(sprintf('The property "%s" in class "%s" must not be private when annotated for configuration injection.', $propertyName, $className), 1416765599);
             }
             if (array_key_exists($propertyName, $properties)) {
                 continue;
             }
             /** @var InjectConfiguration $injectConfigurationAnnotation */
             $injectConfigurationAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, \TYPO3\Flow\Annotations\InjectConfiguration::class);
             if ($injectConfigurationAnnotation->type === ConfigurationManager::CONFIGURATION_TYPE_SETTINGS) {
                 $packageKey = $injectConfigurationAnnotation->package !== NULL ? $injectConfigurationAnnotation->package : $objectConfiguration->getPackageKey();
                 $configurationPath = rtrim($packageKey . '.' . $injectConfigurationAnnotation->path, '.');
             } else {
                 if ($injectConfigurationAnnotation->package !== NULL) {
                     throw new \TYPO3\Flow\Object\Exception(sprintf('The InjectConfiguration annotation for property "%s" in class "%s" specifies a "package" key for configuration type "%s", but this is only supported for injection of "Settings".', $propertyName, $className, $injectConfigurationAnnotation->type), 1420811958);
                 }
                 $configurationPath = $injectConfigurationAnnotation->path;
             }
             $properties[$propertyName] = new ConfigurationProperty($propertyName, array('type' => $injectConfigurationAnnotation->type, 'path' => $configurationPath), ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION);
         }
         $objectConfiguration->setProperties($properties);
     }
 }