/** * Checks if the specified class and method matches against the filter * * @param string $className Name of the class to check against * @param string $methodName Name of the method to check against * @param string $methodDeclaringClassName Name of the class the method was originally declared in * @param mixed $pointcutQueryIdentifier Some identifier for this query - must at least differ from a previous identifier. Used for circular reference detection. * @return boolean true if the class / method match, otherwise false */ public function matches($className, $methodName, $methodDeclaringClassName, $pointcutQueryIdentifier) { $valueObjectAnnotation = $this->reflectionService->getClassAnnotation($className, Flow\ValueObject::class); if ($valueObjectAnnotation !== null && $valueObjectAnnotation->embedded === true) { return true; } return false; }
/** * Returns a proxy class object for the specified original class. * * If no such proxy class has been created yet by this renderer, * this function will create one and register it for later use. * * If the class is not proxable, FALSE will be returned * * @param string $fullClassName Name of the original class * @return ProxyClass|boolean */ public function getProxyClass($fullClassName) { if (interface_exists($fullClassName) || in_array(BaseTestCase::class, class_parents($fullClassName))) { return false; } if (class_exists($fullClassName) === false) { return false; } $classReflection = new \ReflectionClass($fullClassName); if ($classReflection->isInternal() === true) { return false; } $proxyAnnotation = $this->reflectionService->getClassAnnotation($fullClassName, Flow\Proxy::class); if ($proxyAnnotation !== null && $proxyAnnotation->enabled === false) { return false; } if (in_array(substr($fullClassName, 0, $this->blacklistedSubPackagesLength), $this->blacklistedSubPackages)) { return false; } // Annotation classes (like \Neos\Flow\Annotations\Entity) must never be proxied because that would break the Doctrine AnnotationParser if ($classReflection->isFinal() && preg_match('/^\\s?\\*\\s?\\@Annotation\\s/m', $classReflection->getDocComment()) === 1) { return false; } if (!isset($this->proxyClasses[$fullClassName])) { $this->proxyClasses[$fullClassName] = new ProxyClass($fullClassName); $this->proxyClasses[$fullClassName]->injectReflectionService($this->reflectionService); } return $this->proxyClasses[$fullClassName]; }
/** * Builds a raw configuration array by parsing possible scope and autowiring * annotations from the given class or interface. * * @param string $className * @param array $rawObjectConfiguration * @return array */ protected function enhanceRawConfigurationWithAnnotationOptions($className, array $rawObjectConfiguration) { if ($this->reflectionService->isClassAnnotatedWith($className, Flow\Scope::class)) { $rawObjectConfiguration['scope'] = $this->reflectionService->getClassAnnotation($className, Flow\Scope::class)->value; } if ($this->reflectionService->isClassAnnotatedWith($className, Flow\Autowiring::class)) { $rawObjectConfiguration['autowiring'] = $this->reflectionService->getClassAnnotation($className, Flow\Autowiring::class)->enabled; } return $rawObjectConfiguration; }
/** * Evaluate the property annotations and amend the metadata accordingly. * * @param ORM\ClassMetadataInfo $metadata * @return void * @throws ORM\MappingException */ protected function evaluatePropertyAnnotations(ORM\ClassMetadataInfo $metadata) { $className = $metadata->name; $class = $metadata->getReflectionClass(); $classSchema = $this->getClassSchema($className); foreach ($class->getProperties() as $property) { if (!$classSchema->hasProperty($property->getName()) || $classSchema->isPropertyTransient($property->getName()) || $metadata->isMappedSuperclass && !$property->isPrivate() || $metadata->isInheritedField($property->getName()) || $metadata->isInheritedAssociation($property->getName()) || $metadata->isInheritedEmbeddedClass($property->getName())) { continue; } $propertyMetaData = $classSchema->getProperty($property->getName()); $mapping = []; $mapping['fieldName'] = $property->getName(); $mapping['columnName'] = strtolower($property->getName()); $mapping['targetEntity'] = $propertyMetaData['type']; $joinColumns = $this->evaluateJoinColumnAnnotations($property); // Field can only be annotated with one of: // @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Column (optional) if ($oneToOneAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OneToOne::class)) { if ($this->reader->getPropertyAnnotation($property, ORM\Id::class) !== null) { $mapping['id'] = true; } if ($oneToOneAnnotation->targetEntity) { $mapping['targetEntity'] = $oneToOneAnnotation->targetEntity; } if ($oneToOneAnnotation->inversedBy !== null || $oneToOneAnnotation->mappedBy === null) { $mapping['joinColumns'] = $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property); } $mapping['mappedBy'] = $oneToOneAnnotation->mappedBy; $mapping['inversedBy'] = $oneToOneAnnotation->inversedBy; if ($oneToOneAnnotation->cascade) { $mapping['cascade'] = $oneToOneAnnotation->cascade; } elseif ($this->isValueObject($mapping['targetEntity'], $className)) { $mapping['cascade'] = ['persist']; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) { $mapping['cascade'] = ['all']; } if ($oneToOneAnnotation->orphanRemoval) { $mapping['orphanRemoval'] = $oneToOneAnnotation->orphanRemoval; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false && $this->isValueObject($mapping['targetEntity'], $className) === false) { $mapping['orphanRemoval'] = true; } $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnotation->fetch); $metadata->mapOneToOne($mapping); } elseif ($oneToManyAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OneToMany::class)) { $mapping['mappedBy'] = $oneToManyAnnotation->mappedBy; if ($oneToManyAnnotation->targetEntity) { $mapping['targetEntity'] = $oneToManyAnnotation->targetEntity; } elseif (isset($propertyMetaData['elementType'])) { $mapping['targetEntity'] = $propertyMetaData['elementType']; } if ($oneToManyAnnotation->cascade) { $mapping['cascade'] = $oneToManyAnnotation->cascade; } elseif ($this->isValueObject($mapping['targetEntity'], $className)) { $mapping['cascade'] = ['persist']; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) { $mapping['cascade'] = ['all']; } $mapping['indexBy'] = $oneToManyAnnotation->indexBy; if ($oneToManyAnnotation->orphanRemoval) { $mapping['orphanRemoval'] = $oneToManyAnnotation->orphanRemoval; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false && $this->isValueObject($mapping['targetEntity'], $className) === false) { $mapping['orphanRemoval'] = true; } $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnotation->fetch); if ($orderByAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OrderBy::class)) { $mapping['orderBy'] = $orderByAnnotation->value; } $metadata->mapOneToMany($mapping); } elseif ($manyToOneAnnotation = $this->reader->getPropertyAnnotation($property, ORM\ManyToOne::class)) { if ($this->reader->getPropertyAnnotation($property, ORM\Id::class) !== null) { $mapping['id'] = true; } if ($manyToOneAnnotation->targetEntity) { $mapping['targetEntity'] = $manyToOneAnnotation->targetEntity; } $mapping['joinColumns'] = $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property); if ($manyToOneAnnotation->cascade) { $mapping['cascade'] = $manyToOneAnnotation->cascade; } elseif ($this->isValueObject($mapping['targetEntity'], $className)) { $mapping['cascade'] = ['persist']; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) { $mapping['cascade'] = ['all']; } $mapping['inversedBy'] = $manyToOneAnnotation->inversedBy; $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnotation->fetch); $metadata->mapManyToOne($mapping); } elseif ($manyToManyAnnotation = $this->reader->getPropertyAnnotation($property, ORM\ManyToMany::class)) { if ($manyToManyAnnotation->targetEntity) { $mapping['targetEntity'] = $manyToManyAnnotation->targetEntity; } elseif (isset($propertyMetaData['elementType'])) { $mapping['targetEntity'] = $propertyMetaData['elementType']; } /** @var ORM\JoinTable $joinTableAnnotation */ if ($joinTableAnnotation = $this->reader->getPropertyAnnotation($property, ORM\JoinTable::class)) { $joinTable = $this->evaluateJoinTableAnnotation($joinTableAnnotation, $property, $className, $mapping); } else { $joinColumns = [['name' => null, 'referencedColumnName' => null]]; $joinTable = ['name' => $this->inferJoinTableNameFromClassAndPropertyName($className, $property->getName()), 'joinColumns' => $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property, self::MAPPING_MM_REGULAR), 'inverseJoinColumns' => $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property)]; } $mapping['joinTable'] = $joinTable; $mapping['mappedBy'] = $manyToManyAnnotation->mappedBy; $mapping['inversedBy'] = $manyToManyAnnotation->inversedBy; if ($manyToManyAnnotation->cascade) { $mapping['cascade'] = $manyToManyAnnotation->cascade; } elseif ($this->isValueObject($mapping['targetEntity'], $className)) { $mapping['cascade'] = ['persist']; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) { $mapping['cascade'] = ['all']; } $mapping['indexBy'] = $manyToManyAnnotation->indexBy; $mapping['orphanRemoval'] = $manyToManyAnnotation->orphanRemoval; $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnotation->fetch); if ($orderByAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OrderBy::class)) { $mapping['orderBy'] = $orderByAnnotation->value; } $metadata->mapManyToMany($mapping); } elseif ($embeddedAnnotation = $this->reader->getPropertyAnnotation($property, ORM\Embedded::class)) { if ($embeddedAnnotation->class) { $mapping['class'] = $embeddedAnnotation->class; } else { // This will not happen currently, because "class" argument is required. It would be nice if that could be changed though. $mapping['class'] = $mapping['targetEntity']; } $mapping['columnPrefix'] = $embeddedAnnotation->columnPrefix; $metadata->mapEmbedded($mapping); } else { $mapping['nullable'] = false; /** @var ORM\Column $columnAnnotation */ if ($columnAnnotation = $this->reader->getPropertyAnnotation($property, ORM\Column::class)) { $mapping = $this->addColumnToMappingArray($columnAnnotation, $mapping); } if (!isset($mapping['type'])) { switch ($propertyMetaData['type']) { case 'DateTime': $mapping['type'] = 'datetime'; break; case 'string': case 'integer': case 'boolean': case 'float': case 'array': $mapping['type'] = $propertyMetaData['type']; break; default: if (strpos($propertyMetaData['type'], '\\') !== false) { if ($this->reflectionService->isClassAnnotatedWith($propertyMetaData['type'], Flow\ValueObject::class)) { $valueObjectAnnotation = $this->reflectionService->getClassAnnotation($propertyMetaData['type'], Flow\ValueObject::class); if ($valueObjectAnnotation->embedded === true) { $mapping['class'] = $propertyMetaData['type']; $mapping['columnPrefix'] = $mapping['columnName']; $metadata->mapEmbedded($mapping); // Leave switch and continue with next property continue 2; } $mapping['type'] = 'object'; } elseif (class_exists($propertyMetaData['type'])) { throw ORM\MappingException::missingRequiredOption($property->getName(), 'OneToOne', sprintf('The property "%s" in class "%s" has a non standard data type and doesn\'t define the type of the relation. You have to use one of these annotations: @OneToOne, @OneToMany, @ManyToOne, @ManyToMany', $property->getName(), $className)); } } else { throw ORM\MappingException::propertyTypeIsRequired($className, $property->getName()); } } } if ($this->reader->getPropertyAnnotation($property, ORM\Id::class) !== null) { $mapping['id'] = true; } if ($generatedValueAnnotation = $this->reader->getPropertyAnnotation($property, ORM\GeneratedValue::class)) { $metadata->setIdGeneratorType(constant('Doctrine\\ORM\\Mapping\\ClassMetadata::GENERATOR_TYPE_' . strtoupper($generatedValueAnnotation->strategy))); } if ($this->reflectionService->isPropertyAnnotatedWith($className, $property->getName(), ORM\Version::class)) { $metadata->setVersionMapping($mapping); } $metadata->mapField($mapping); // Check for SequenceGenerator/TableGenerator definition if ($seqGeneratorAnnotation = $this->reader->getPropertyAnnotation($property, ORM\SequenceGenerator::class)) { $metadata->setSequenceGeneratorDefinition(['sequenceName' => $seqGeneratorAnnotation->sequenceName, 'allocationSize' => $seqGeneratorAnnotation->allocationSize, 'initialValue' => $seqGeneratorAnnotation->initialValue]); } elseif ($this->reader->getPropertyAnnotation($property, ORM\TableGenerator::class) !== null) { throw ORM\MappingException::tableIdGeneratorNotImplemented($className); } elseif ($customGeneratorAnnotation = $this->reader->getPropertyAnnotation($property, ORM\CustomIdGenerator::class)) { $metadata->setCustomGeneratorDefinition(['class' => $customGeneratorAnnotation->class]); } } // Evaluate @Cache annotation if (($cacheAnnotation = $this->reader->getPropertyAnnotation($property, ORM\Cache::class)) !== null) { $metadata->enableAssociationCache($mapping['fieldName'], array('usage' => constant('Doctrine\\ORM\\Mapping\\ClassMetadata::CACHE_USAGE_' . $cacheAnnotation->usage), 'region' => $cacheAnnotation->region)); } } }
/** * Creates and returns an aspect from the annotations found in a class which * is tagged as an aspect. The object acting as an advice will already be * fetched (and therefore instantiated if necessary). * * @param string $aspectClassName Name of the class which forms the aspect, contains advices etc. * @return mixed The aspect container containing one or more advisors or FALSE if no container could be built * @throws Aop\Exception */ protected function buildAspectContainer($aspectClassName) { $aspectContainer = new AspectContainer($aspectClassName); $methodNames = get_class_methods($aspectClassName); foreach ($methodNames as $methodName) { foreach ($this->reflectionService->getMethodAnnotations($aspectClassName, $methodName) as $annotation) { $annotationClass = get_class($annotation); switch ($annotationClass) { case Flow\Around::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new Aop\Advice\AroundAdvice($aspectClassName, $methodName); $pointcut = new Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case Flow\Before::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new Aop\Advice\BeforeAdvice($aspectClassName, $methodName); $pointcut = new Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case Flow\AfterReturning::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new Aop\Advice\AfterReturningAdvice($aspectClassName, $methodName); $pointcut = new Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case Flow\AfterThrowing::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new Aop\Advice\AfterThrowingAdvice($aspectClassName, $methodName); $pointcut = new Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case Flow\After::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new Aop\Advice\AfterAdvice($aspectClassName, $methodName); $pointcut = new Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case Flow\Pointcut::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->expression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $pointcut = new Aop\Pointcut\Pointcut($annotation->expression, $pointcutFilterComposite, $aspectClassName, $methodName); $aspectContainer->addPointcut($pointcut); break; } } } $introduceAnnotation = $this->reflectionService->getClassAnnotation($aspectClassName, Flow\Introduce::class); if ($introduceAnnotation !== null) { if ($introduceAnnotation->interfaceName === null && $introduceAnnotation->traitName === null) { throw new Aop\Exception('The introduction in class "' . $aspectClassName . '" does neither contain an interface name nor a trait name, at least one is required.', 1172694761); } $pointcutFilterComposite = $this->pointcutExpressionParser->parse($introduceAnnotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $introduceAnnotation->interfaceName, Flow\Introduce::class)); $pointcut = new Aop\Pointcut\Pointcut($introduceAnnotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); if ($introduceAnnotation->interfaceName !== null) { $introduction = new Aop\InterfaceIntroduction($aspectClassName, $introduceAnnotation->interfaceName, $pointcut); $aspectContainer->addInterfaceIntroduction($introduction); } if ($introduceAnnotation->traitName !== null) { $introduction = new TraitIntroduction($aspectClassName, $introduceAnnotation->traitName, $pointcut); $aspectContainer->addTraitIntroduction($introduction); } } foreach ($this->reflectionService->getClassPropertyNames($aspectClassName) as $propertyName) { $introduceAnnotation = $this->reflectionService->getPropertyAnnotation($aspectClassName, $propertyName, Flow\Introduce::class); if ($introduceAnnotation !== null) { $pointcutFilterComposite = $this->pointcutExpressionParser->parse($introduceAnnotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $propertyName, Flow\Introduce::class)); $pointcut = new Aop\Pointcut\Pointcut($introduceAnnotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $introduction = new PropertyIntroduction($aspectClassName, $propertyName, $pointcut); $aspectContainer->addPropertyIntroduction($introduction); } } if (count($aspectContainer->getAdvisors()) < 1 && count($aspectContainer->getPointcuts()) < 1 && count($aspectContainer->getInterfaceIntroductions()) < 1 && count($aspectContainer->getTraitIntroductions()) < 1 && count($aspectContainer->getPropertyIntroductions()) < 1) { throw new Aop\Exception('The class "' . $aspectClassName . '" is tagged to be an aspect but doesn\'t contain advices nor pointcut or introduction declarations.', 1169124534); } return $aspectContainer; }