/** * @param string $className * @param PropertyReflection $property * @param string $tagName * @param array $tagValues * @return array */ protected function reflectPropertyTag($className, PropertyReflection $property, $tagName, $tagValues) { if ($this->isTagIgnored($tagName)) { return null; } if ($tagName !== 'var' || !isset($tagValues[0])) { return $tagValues; } $propertyName = $property->getName(); $propertyDeclaringClass = $property->getDeclaringClass(); if ($propertyDeclaringClass->getName() !== $className && isset($this->classReflectionData[$propertyDeclaringClass->getName()][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES][$tagName])) { $tagValues = $this->classReflectionData[$propertyDeclaringClass->getName()][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES][$tagName]; } else { $tagValues[0] = $this->expandType($propertyDeclaringClass, $tagValues[0]); } return $tagValues; }
/** * Set a property for a given object. * Tries to set the property the following ways: * - if target is an array, set value * - if super cow powers should be used, set value through reflection * - if public setter method exists, call it. * - if public property exists, set it directly. * - if the target object is an instance of ArrayAccess, it sets the property * on it without checking if it existed. * - else, return FALSE * * @param mixed $subject The target object or array * @param string|integer $propertyName Name or index of the property to set * @param mixed $propertyValue Value of the property * @param boolean $forceDirectAccess directly access property using reflection(!) * @return boolean TRUE if the property could be set, FALSE otherwise * @throws \InvalidArgumentException in case $object was not an object or $propertyName was not a string */ public static function setProperty(&$subject, $propertyName, $propertyValue, $forceDirectAccess = false) { if (is_array($subject)) { $subject[$propertyName] = $propertyValue; return true; } if (!is_object($subject)) { throw new \InvalidArgumentException('subject must be an object or array, ' . gettype($subject) . ' given.', 1237301368); } if (!is_string($propertyName) && !is_integer($propertyName)) { throw new \InvalidArgumentException('Given property name/index is not of type string or integer.', 1231178878); } if ($forceDirectAccess === true) { if (property_exists(get_class($subject), $propertyName)) { $propertyReflection = new PropertyReflection(get_class($subject), $propertyName); $propertyReflection->setValue($subject, $propertyValue); } else { $subject->{$propertyName} = $propertyValue; } } elseif (is_callable(array($subject, $setterMethodName = self::buildSetterMethodName($propertyName)))) { $subject->{$setterMethodName}($propertyValue); } elseif ($subject instanceof \ArrayAccess) { $subject[$propertyName] = $propertyValue; } elseif (array_key_exists($propertyName, get_object_vars($subject))) { $subject->{$propertyName} = $propertyValue; } else { return false; } return true; }
/** * Builds methods for a single AOP proxy class for the specified class. * * @param string $targetClassName Name of the class to create a proxy class file for * @param array &$aspectContainers The array of aspect containers from the AOP Framework * @return boolean TRUE if the proxy class could be built, FALSE otherwise. */ public function buildProxyClass($targetClassName, array &$aspectContainers) { $interfaceIntroductions = $this->getMatchingInterfaceIntroductions($aspectContainers, $targetClassName); $introducedInterfaces = $this->getInterfaceNamesFromIntroductions($interfaceIntroductions); $propertyIntroductions = $this->getMatchingPropertyIntroductions($aspectContainers, $targetClassName); $methodsFromTargetClass = $this->getMethodsFromTargetClass($targetClassName); $methodsFromIntroducedInterfaces = $this->getIntroducedMethodsFromInterfaceIntroductions($interfaceIntroductions, $targetClassName); $interceptedMethods = array(); $this->addAdvicedMethodsToInterceptedMethods($interceptedMethods, array_merge($methodsFromTargetClass, $methodsFromIntroducedInterfaces), $targetClassName, $aspectContainers); $this->addIntroducedMethodsToInterceptedMethods($interceptedMethods, $methodsFromIntroducedInterfaces); if (count($interceptedMethods) < 1 && count($introducedInterfaces) < 1 && count($propertyIntroductions) < 1) { return FALSE; } $proxyClass = $this->compiler->getProxyClass($targetClassName); if ($proxyClass === FALSE) { return FALSE; } $proxyClass->addInterfaces($introducedInterfaces); /** @var $propertyIntroduction PropertyIntroduction */ foreach ($propertyIntroductions as $propertyIntroduction) { $propertyName = $propertyIntroduction->getPropertyName(); $declaringAspectClassName = $propertyIntroduction->getDeclaringAspectClassName(); $possiblePropertyTypes = $this->reflectionService->getPropertyTagValues($declaringAspectClassName, $propertyName, 'var'); if (count($possiblePropertyTypes) > 0 && !$this->reflectionService->isPropertyAnnotatedWith($declaringAspectClassName, $propertyName, 'TYPO3\\Flow\\Annotations\\Transient')) { $classSchema = $this->reflectionService->getClassSchema($targetClassName); if ($classSchema !== NULL) { $classSchema->addProperty($propertyName, $possiblePropertyTypes[0]); } } $propertyReflection = new PropertyReflection($declaringAspectClassName, $propertyName); $propertyReflection->setIsAopIntroduced(TRUE); $this->reflectionService->reflectClassProperty($targetClassName, $propertyReflection, new ClassReflection($declaringAspectClassName)); $proxyClass->addProperty($propertyName, 'NULL', $propertyIntroduction->getPropertyVisibility(), $propertyIntroduction->getPropertyDocComment()); } $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->addPreParentCallCode("\t\tif (method_exists(get_parent_class(\$this), 'Flow_Aop_Proxy_buildMethodsAndAdvicesArray') && is_callable('parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray')) parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n"); $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->addPreParentCallCode($this->buildMethodsAndAdvicesArrayCode($interceptedMethods)); $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->overrideMethodVisibility('protected'); $callBuildMethodsAndAdvicesArrayCode = "\n\t\t\$this->Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n"; $proxyClass->getConstructor()->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); $proxyClass->getMethod('__wakeup')->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); if (!$this->reflectionService->hasMethod($targetClassName, '__wakeup')) { $proxyClass->getMethod('__wakeup')->addPostParentCallCode("\t\tif (method_exists(get_parent_class(\$this), '__wakeup') && is_callable('parent::__wakeup')) parent::__wakeup();\n"); } // FIXME this can be removed again once Doctrine is fixed (see fixMethodsAndAdvicesArrayForDoctrineProxiesCode()) $proxyClass->getMethod('Flow_Aop_Proxy_fixMethodsAndAdvicesArrayForDoctrineProxies')->addPreParentCallCode($this->fixMethodsAndAdvicesArrayForDoctrineProxiesCode()); // FIXME this can be removed again once Doctrine is fixed (see fixInjectedPropertiesForDoctrineProxiesCode()) $proxyClass->getMethod('Flow_Aop_Proxy_fixInjectedPropertiesForDoctrineProxies')->addPreParentCallCode($this->fixInjectedPropertiesForDoctrineProxiesCode()); $this->buildGetAdviceChainsMethodCode($targetClassName); $this->buildInvokeJoinPointMethodCode($targetClassName); $this->buildMethodsInterceptorCode($targetClassName, $interceptedMethods); $proxyClass->addProperty('Flow_Aop_Proxy_targetMethodsAndGroupedAdvices', 'array()'); $proxyClass->addProperty('Flow_Aop_Proxy_groupedAdviceChains', 'array()'); $proxyClass->addProperty('Flow_Aop_Proxy_methodIsInAdviceMode', 'array()'); return TRUE; }
/** * Builds methods for a single AOP proxy class for the specified class. * * @param string $targetClassName Name of the class to create a proxy class file for * @param array &$aspectContainers The array of aspect containers from the AOP Framework * @return boolean TRUE if the proxy class could be built, FALSE otherwise. */ public function buildProxyClass($targetClassName, array &$aspectContainers) { $interfaceIntroductions = $this->getMatchingInterfaceIntroductions($aspectContainers, $targetClassName); $introducedInterfaces = $this->getInterfaceNamesFromIntroductions($interfaceIntroductions); $introducedTraits = $this->getMatchingTraitNamesFromIntroductions($aspectContainers, $targetClassName); $propertyIntroductions = $this->getMatchingPropertyIntroductions($aspectContainers, $targetClassName); $methodsFromTargetClass = $this->getMethodsFromTargetClass($targetClassName); $methodsFromIntroducedInterfaces = $this->getIntroducedMethodsFromInterfaceIntroductions($interfaceIntroductions, $targetClassName); $interceptedMethods = array(); $this->addAdvicedMethodsToInterceptedMethods($interceptedMethods, array_merge($methodsFromTargetClass, $methodsFromIntroducedInterfaces), $targetClassName, $aspectContainers); $this->addIntroducedMethodsToInterceptedMethods($interceptedMethods, $methodsFromIntroducedInterfaces); if (count($interceptedMethods) < 1 && count($introducedInterfaces) < 1 && count($propertyIntroductions) < 1) { return false; } $proxyClass = $this->compiler->getProxyClass($targetClassName); if ($proxyClass === false) { return false; } $proxyClass->addInterfaces($introducedInterfaces); $proxyClass->addTraits($introducedTraits); /** @var $propertyIntroduction PropertyIntroduction */ foreach ($propertyIntroductions as $propertyIntroduction) { $propertyName = $propertyIntroduction->getPropertyName(); $declaringAspectClassName = $propertyIntroduction->getDeclaringAspectClassName(); $possiblePropertyTypes = $this->reflectionService->getPropertyTagValues($declaringAspectClassName, $propertyName, 'var'); if (count($possiblePropertyTypes) > 0 && !$this->reflectionService->isPropertyAnnotatedWith($declaringAspectClassName, $propertyName, \TYPO3\Flow\Annotations\Transient::class)) { $classSchema = $this->reflectionService->getClassSchema($targetClassName); if ($classSchema !== null) { $classSchema->addProperty($propertyName, $possiblePropertyTypes[0]); } } $propertyReflection = new PropertyReflection($declaringAspectClassName, $propertyName); $propertyReflection->setIsAopIntroduced(true); $this->reflectionService->reflectClassProperty($targetClassName, $propertyReflection, new ClassReflection($declaringAspectClassName)); $proxyClass->addProperty($propertyName, var_export($propertyIntroduction->getInitialValue(), true), $propertyIntroduction->getPropertyVisibility(), $propertyIntroduction->getPropertyDocComment()); } $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->addPreParentCallCode(" if (method_exists(get_parent_class(), 'Flow_Aop_Proxy_buildMethodsAndAdvicesArray') && is_callable('parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray')) parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n"); $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->addPreParentCallCode($this->buildMethodsAndAdvicesArrayCode($interceptedMethods)); $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->overrideMethodVisibility('protected'); $callBuildMethodsAndAdvicesArrayCode = "\n \$this->Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n"; $proxyClass->getConstructor()->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); $proxyClass->getMethod('__wakeup')->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); $proxyClass->getMethod('__clone')->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); if (!$this->reflectionService->hasMethod($targetClassName, '__wakeup')) { $proxyClass->getMethod('__wakeup')->addPostParentCallCode(" if (method_exists(get_parent_class(), '__wakeup') && is_callable('parent::__wakeup')) parent::__wakeup();\n"); } $proxyClass->addTraits(['\\' . AdvicesTrait::class]); $this->buildMethodsInterceptorCode($targetClassName, $interceptedMethods); $proxyClass->addProperty('Flow_Aop_Proxy_targetMethodsAndGroupedAdvices', 'array()'); $proxyClass->addProperty('Flow_Aop_Proxy_groupedAdviceChains', 'array()'); $proxyClass->addProperty('Flow_Aop_Proxy_methodIsInAdviceMode', 'array()'); return true; }
/** * @param string $className * @param PropertyReflection $property * @return integer visibility */ public function reflectClassProperty($className, PropertyReflection $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 $tagName => $tagValues) { if ($this->isTagIgnored($tagName)) { continue; } if ($tagName === 'var' && isset($tagValues[0])) { if ($property->getDeclaringClass()->getName() !== $className && isset($this->classReflectionData[$property->getDeclaringClass()->getName()][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES][$tagName])) { $tagValues = $this->classReflectionData[$property->getDeclaringClass()->getName()][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES][$tagName]; } else { $tagValues[0] = $this->expandType($property->getDeclaringClass(), $tagValues[0]); } } $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES][$tagName] = $tagValues; } foreach ($this->annotationReader->getPropertyAnnotations($property, $propertyName) as $annotation) { $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_ANNOTATIONS][get_class($annotation)][] = $annotation; } return $visibility; }