[FEATURE] Backport Object Type Converter from Flow 14/21114/2
authorHelmut Hummel <helmut.hummel@typo3.org>
Fri, 3 May 2013 19:17:37 +0000 (21:17 +0200)
committerHelmut Hummel <helmut.hummel@typo3.org>
Mon, 27 May 2013 22:51:52 +0000 (00:51 +0200)
In Flow there is a Type Converter which can map array sources
to not persistent objects. This is very useful if you need
transitional objects built from request arguments.

Backporting this converter needs some minor modifications
in the reflection service, which are included in this commit
as well as registering the new converter.

Resolves: #48548
Releases: 6.2

Change-Id: Ic88b732076ae19ece490cf1376b2d1bbcaf1ebff
Reviewed-on: https://review.typo3.org/21114
Reviewed-by: Anja Leichsenring
Tested-by: Anja Leichsenring
Reviewed-by: Daniel Hürtgen
Tested-by: Daniel Hürtgen
Reviewed-by: Helmut Hummel
Tested-by: Helmut Hummel
typo3/sysext/extbase/Classes/Property/PropertyMappingConfiguration.php
typo3/sysext/extbase/Classes/Property/TypeConverter/ObjectConverter.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Property/TypeConverter/PersistentObjectConverter.php
typo3/sysext/extbase/Classes/Reflection/ReflectionService.php
typo3/sysext/extbase/Tests/Fixture/ValueObject.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/ObjectConverterTest.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/PersistentObjectConverterTest.php
typo3/sysext/extbase/Tests/Unit/Reflection/ReflectionServiceTest.php
typo3/sysext/extbase/ext_localconf.php

index f47a060..feab33d 100644 (file)
@@ -236,7 +236,7 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
                        return $this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER];
                }
 
-               return new \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration();
+               return new PropertyMappingConfiguration();
        }
 
        /**
diff --git a/typo3/sysext/extbase/Classes/Property/TypeConverter/ObjectConverter.php b/typo3/sysext/extbase/Classes/Property/TypeConverter/ObjectConverter.php
new file mode 100644 (file)
index 0000000..d6d0176
--- /dev/null
@@ -0,0 +1,242 @@
+<?php
+namespace TYPO3\CMS\Extbase\Property\TypeConverter;
+
+/*                                                                        *
+ * This script belongs to the Extbase framework                           *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License as published by the *
+ * Free Software Foundation, either version 3 of the License, or (at your *
+ * option) any later version.                                             *
+ *                                                                        *
+ * This script is distributed in the hope that it will be useful, but     *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-    *
+ * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser       *
+ * General Public License for more details.                               *
+ *                                                                        *
+ * You should have received a copy of the GNU Lesser General Public       *
+ * License along with the script.                                         *
+ * If not, see http://www.gnu.org/licenses/lgpl.html                      *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+/**
+ * This converter transforms arrays to simple objects (POPO) by setting properties.
+ *
+ * @api
+ */
+class ObjectConverter extends AbstractTypeConverter implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * @var integer
+        */
+       const CONFIGURATION_TARGET_TYPE = 3;
+
+       /**
+        * @var integer
+        */
+       const CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED = 4;
+
+       /**
+        * @var array
+        */
+       protected $sourceTypes = array('array');
+
+       /**
+        * @var string
+        */
+       protected $targetType = 'object';
+
+       /**
+        * @var integer
+        */
+       protected $priority = 0;
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
+        * @inject
+        */
+       protected $objectManager;
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
+        * @inject
+        */
+       protected $reflectionService;
+
+       /**
+        * Only convert non-persistent types
+        *
+        * @param mixed $source
+        * @param string $targetType
+        * @return boolean
+        */
+       public function canConvertFrom($source, $targetType) {
+               return !is_subclass_of($targetType, 'TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
+       }
+
+       /**
+        * Convert all properties in the source array
+        *
+        * @param mixed $source
+        * @return array
+        */
+       public function getSourceChildPropertiesToBeConverted($source) {
+               if (isset($source['__type'])) {
+                       unset($source['__type']);
+               }
+               return $source;
+       }
+
+       /**
+        * The type of a property is determined by the reflection service.
+        *
+        * @param string $targetType
+        * @param string $propertyName
+        * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
+        * @return string
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
+        */
+       public function getTypeOfChildProperty($targetType, $propertyName, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration) {
+               $configuredTargetType = $configuration->getConfigurationFor($propertyName)->getConfigurationValue('TYPO3\\CMS\\Extbase\\Property\\TypeConverter\\ObjectConverter', self::CONFIGURATION_TARGET_TYPE);
+               if ($configuredTargetType !== NULL) {
+                       return $configuredTargetType;
+               }
+
+               if ($this->reflectionService->hasMethod($targetType, \TYPO3\CMS\Extbase\Reflection\ObjectAccess::buildSetterMethodName($propertyName))) {
+                       $methodParameters = $this->reflectionService->getMethodParameters($targetType, \TYPO3\CMS\Extbase\Reflection\ObjectAccess::buildSetterMethodName($propertyName));
+                       $methodParameter = current($methodParameters);
+                       if (!isset($methodParameter['type'])) {
+                               throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException('Setter for property "' . $propertyName . '" had no type hint or documentation in target object of type "' . $targetType . '".', 1303379158);
+                       } else {
+                               return $methodParameter['type'];
+                       }
+               } else {
+                       $methodParameters = $this->reflectionService->getMethodParameters($targetType, '__construct');
+                       if (isset($methodParameters[$propertyName]) && isset($methodParameters[$propertyName]['type'])) {
+                               return $methodParameters[$propertyName]['type'];
+                       } else {
+                               throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException('Property "' . $propertyName . '" had no setter or constructor argument in target object of type "' . $targetType . '".', 1303379126);
+                       }
+               }
+       }
+
+       /**
+        * Convert an object from $source to an object.
+        *
+        * @param mixed $source
+        * @param string $targetType
+        * @param array $convertedChildProperties
+        * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
+        * @return object the target type
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidDataTypeException
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
+        */
+       public function convertFrom($source, $targetType, array $convertedChildProperties = array(), \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = NULL) {
+               $object = $this->buildObject($convertedChildProperties, $targetType);
+               foreach ($convertedChildProperties as $propertyName => $propertyValue) {
+                       $result = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::setProperty($object, $propertyName, $propertyValue);
+                       if ($result === FALSE) {
+                               $exceptionMessage = sprintf(
+                                       'Property "%s" having a value of type "%s" could not be set in target object of type "%s". Make sure that the property is accessible properly, for example via an appropriate setter method.',
+                                       $propertyName,
+                                       (is_object($propertyValue) ? get_class($propertyValue) : gettype($propertyValue)),
+                                       $targetType
+                               );
+                               throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException($exceptionMessage, 1304538165);
+                       }
+               }
+
+               return $object;
+       }
+
+       /**
+        * Determines the target type based on the source's (optional) __type key.
+        *
+        * @param mixed $source
+        * @param string $originalTargetType
+        * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
+        * @return string
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidDataTypeException
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
+        * @throws \InvalidArgumentException
+        */
+       public function getTargetTypeForSource($source, $originalTargetType, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = NULL) {
+               $targetType = $originalTargetType;
+
+               if (is_array($source) && array_key_exists('__type', $source)) {
+                       $targetType = $source['__type'];
+
+                       if ($configuration === NULL) {
+                               throw new \InvalidArgumentException('A property mapping configuration must be given, not NULL.', 1326277369);
+                       }
+                       if ($configuration->getConfigurationValue('TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter', self::CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED) !== TRUE) {
+                               throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException('Override of target type not allowed. To enable this, you need to set the PropertyMappingConfiguration Value "CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED" to TRUE.', 1317050430);
+                       }
+
+                               // FIXME: The following check and the checkInheritanceChainWithoutIsA() method should be removed if we raise the PHP requirement to 5.3.9 or higher
+                       if (version_compare(phpversion(), '5.3.8', '>')) {
+                               if ($targetType !== $originalTargetType && is_a($targetType, $originalTargetType, TRUE) === FALSE) {
+                                       throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidDataTypeException('The given type "' . $targetType . '" is not a subtype of "' . $originalTargetType . '".', 1317048056);
+                               }
+                       } else {
+                               $targetType = $this->checkInheritanceChainWithoutIsA($targetType, $originalTargetType);
+                       }
+               }
+
+               return $targetType;
+       }
+
+       /**
+        * Builds a new instance of $objectType with the given $possibleConstructorArgumentValues. If
+        * constructor argument values are missing from the given array the method
+        * looks for a default value in the constructor signature. Furthermore, the constructor arguments are removed from $possibleConstructorArgumentValues
+        *
+        * @param array &$possibleConstructorArgumentValues
+        * @param string $objectType
+        * @return object The created instance
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException if a required constructor argument is missing
+        */
+       protected function buildObject(array &$possibleConstructorArgumentValues, $objectType) {
+               if ($this->reflectionService->hasMethod($objectType, '__construct')) {
+                       $constructorSignature = $this->reflectionService->getMethodParameters($objectType, '__construct');
+                       $constructorArguments = array();
+                       foreach ($constructorSignature as $constructorArgumentName => $constructorArgumentInformation) {
+                               if (array_key_exists($constructorArgumentName, $possibleConstructorArgumentValues)) {
+                                       $constructorArguments[] = $possibleConstructorArgumentValues[$constructorArgumentName];
+                                       unset($possibleConstructorArgumentValues[$constructorArgumentName]);
+                               } elseif ($constructorArgumentInformation['optional'] === TRUE) {
+                                       $constructorArguments[] = $constructorArgumentInformation['defaultValue'];
+                               } else {
+                                       throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException('Missing constructor argument "' . $constructorArgumentName . '" for object of type "' . $objectType . '".', 1268734872);
+                               }
+                       }
+                       return call_user_func_array(array($this->objectManager, 'get'), array_merge(array($objectType), $constructorArguments));
+               } else {
+                       return $this->objectManager->get($objectType);
+               }
+       }
+
+       /**
+        * This is a replacement for the functionality provided by is_a() with 3 parameters which is only available from
+        * PHP 5.3.9. It can be removed if the TYPO3 CMS PHP version requirement is raised to 5.3.9 or above.
+        *
+        * @param string $targetType
+        * @param string $originalTargetType
+        * @return string
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidDataTypeException
+        */
+       protected function checkInheritanceChainWithoutIsA($targetType, $originalTargetType) {
+               $targetTypeToCompare = $targetType;
+               do {
+                       if ($targetTypeToCompare === $originalTargetType) {
+                               return $targetType;
+                       }
+               } while ($targetTypeToCompare = get_parent_class($targetTypeToCompare));
+
+               throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidDataTypeException('The given type "' . $targetType . '" is not a subtype of "' . $originalTargetType . '".', 1360928582);
+       }
+
+}
+?>
\ No newline at end of file
index 5604ba7..81f530e 100644 (file)
@@ -34,7 +34,7 @@ namespace TYPO3\CMS\Extbase\Property\TypeConverter;
  *
  * @api
  */
-class PersistentObjectConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter implements \TYPO3\CMS\Core\SingletonInterface {
+class PersistentObjectConverter extends ObjectConverter {
 
        /**
         * @var integer
@@ -47,11 +47,6 @@ class PersistentObjectConverter extends \TYPO3\CMS\Extbase\Property\TypeConverte
        const CONFIGURATION_CREATION_ALLOWED = 2;
 
        /**
-        * @var integer
-        */
-       const CONFIGURATION_TARGET_TYPE = 3;
-
-       /**
         * @var array
         */
        protected $sourceTypes = array('string', 'array');
@@ -67,45 +62,12 @@ class PersistentObjectConverter extends \TYPO3\CMS\Extbase\Property\TypeConverte
        protected $priority = 1;
 
        /**
-        * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
-        */
-       protected $objectManager;
-
-       /**
         * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
+        * @inject
         */
        protected $persistenceManager;
 
        /**
-        * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
-        */
-       protected $reflectionService;
-
-       /**
-        * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
-        * @return void
-        */
-       public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager) {
-               $this->objectManager = $objectManager;
-       }
-
-       /**
-        * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
-        * @return void
-        */
-       public function injectPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager) {
-               $this->persistenceManager = $persistenceManager;
-       }
-
-       /**
-        * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
-        * @return void
-        */
-       public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService) {
-               $this->reflectionService = $reflectionService;
-       }
-
-       /**
         * We can only convert if the $targetType is either tagged with entity or value object.
         *
         * @param mixed $source
@@ -129,10 +91,7 @@ class PersistentObjectConverter extends \TYPO3\CMS\Extbase\Property\TypeConverte
                if (isset($source['__identity'])) {
                        unset($source['__identity']);
                }
-               if (isset($source['__type'])) {
-                       unset($source['__type']);
-               }
-               return $source;
+               return parent::getSourceChildPropertiesToBeConverted($source);
        }
 
        /**
@@ -141,8 +100,8 @@ class PersistentObjectConverter extends \TYPO3\CMS\Extbase\Property\TypeConverte
         * @param string $targetType
         * @param string $propertyName
         * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
-        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
         * @return string
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
         */
        public function getTypeOfChildProperty($targetType, $propertyName, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration) {
                $configuredTargetType = $configuration->getConfigurationFor($propertyName)->getConfigurationValue('TYPO3\\CMS\\Extbase\\Property\\TypeConverter\\PersistentObjectConverter', self::CONFIGURATION_TARGET_TYPE);
@@ -166,8 +125,8 @@ class PersistentObjectConverter extends \TYPO3\CMS\Extbase\Property\TypeConverte
         * @param array $convertedChildProperties
         * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
         * @throws \InvalidArgumentException
-        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
         * @return object the target type
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
         */
        public function convertFrom($source, $targetType, array $convertedChildProperties = array(), \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = NULL) {
                if (is_array($source)) {
@@ -211,8 +170,8 @@ class PersistentObjectConverter extends \TYPO3\CMS\Extbase\Property\TypeConverte
         * @param string $targetType
         * @param array &$convertedChildProperties
         * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
-        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
         * @return object
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
         */
        protected function handleArrayData(array $source, $targetType, array &$convertedChildProperties, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = NULL) {
                if (isset($source['__identity'])) {
@@ -240,7 +199,7 @@ class PersistentObjectConverter extends \TYPO3\CMS\Extbase\Property\TypeConverte
         * @return object
         */
        protected function fetchObjectFromPersistence($identity, $targetType) {
-               if (is_numeric($identity)) {
+               if (ctype_digit((string)$identity)) {
                        $object = $this->persistenceManager->getObjectByIdentifier($identity, $targetType);
                } else {
                        throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidSourceException('The identity property "' . $identity . '" is no UID.', 1297931020);
@@ -253,35 +212,6 @@ class PersistentObjectConverter extends \TYPO3\CMS\Extbase\Property\TypeConverte
                return $object;
        }
 
-       /**
-        * Builds a new instance of $objectType with the given $possibleConstructorArgumentValues. If
-        * constructor argument values are missing from the given array the method
-        * looks for a default value in the constructor signature. Furthermore, the constructor arguments are removed from $possibleConstructorArgumentValues
-        *
-        * @param array &$possibleConstructorArgumentValues
-        * @param string $objectType
-        * @return object The created instance
-        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException if a required constructor argument is missing
-        */
-       protected function buildObject(array &$possibleConstructorArgumentValues, $objectType) {
-               try {
-                       $constructorSignature = $this->reflectionService->getMethodParameters($objectType, '__construct');
-               } catch (\ReflectionException $reflectionException) {
-                       $constructorSignature = array();
-               }
-               $constructorArguments = array();
-               foreach ($constructorSignature as $constructorArgumentName => $constructorArgumentInformation) {
-                       if (array_key_exists($constructorArgumentName, $possibleConstructorArgumentValues)) {
-                               $constructorArguments[] = $possibleConstructorArgumentValues[$constructorArgumentName];
-                               unset($possibleConstructorArgumentValues[$constructorArgumentName]);
-                       } elseif ($constructorArgumentInformation['optional'] === TRUE) {
-                               $constructorArguments[] = $constructorArgumentInformation['defaultValue'];
-                       } else {
-                               throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException('Missing constructor argument "' . $constructorArgumentName . '" for object of type "' . $objectType . '".', 1268734872);
-                       }
-               }
-               return call_user_func_array(array($this->objectManager, 'get'), array_merge(array($objectType), $constructorArguments));
-       }
 }
 
 ?>
\ No newline at end of file
index 998eb31..f302e35 100644 (file)
@@ -152,7 +152,7 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface {
        /**
         * @var array
         */
-       protected $methodReflections;
+       protected $methodReflections = array();
 
        /**
         * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
@@ -248,6 +248,27 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface {
        }
 
        /**
+        * Wrapper for method_exists() which tells if the given method exists.
+        *
+        * @param string $className Name of the class containing the method
+        * @param string $methodName Name of the method
+        * @return boolean
+        * @api
+        */
+       public function hasMethod($className, $methodName) {
+               try {
+                       if (!array_key_exists($className, $this->methodReflections) || !array_key_exists($methodName, $this->methodReflections[$className])) {
+                               $this->methodReflections[$className][$methodName] = new \TYPO3\CMS\Extbase\Reflection\MethodReflection($className, $methodName);
+                               $this->dataCacheNeedsUpdate = TRUE;
+                       }
+               } catch (\ReflectionException $e) {
+                       // Method does not exist. Store this information in cache.
+                       $this->methodReflections[$className][$methodName] = NULL;
+               }
+               return isset($this->methodReflections[$className][$methodName]);
+       }
+
+       /**
         * Returns all tags and their values the specified method is tagged with
         *
         * @param string $className Name of the class containing the method
diff --git a/typo3/sysext/extbase/Tests/Fixture/ValueObject.php b/typo3/sysext/extbase/Tests/Fixture/ValueObject.php
new file mode 100644 (file)
index 0000000..453585f
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+namespace TYPO3\CMS\Extbase\Tests\Fixture;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  This class is a backport of the corresponding class of TYPO3 Flow.
+ *  All credits go to the TYPO3 Flow team.
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+/**
+ * An entity
+ */
+class ValueObject extends \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject {
+
+       /**
+        * The value object's name
+        *
+        * @var string
+        */
+       protected $name;
+
+       /**
+        * Constructs this value object
+        *
+        * @param string $name Name of this blog
+        */
+       public function __construct($name) {
+               $this->setName($name);
+       }
+
+       /**
+        * Sets this value object's name
+        *
+        * @param string $name The value object's name
+        * @return void
+        */
+       public function setName($name) {
+               $this->name = $name;
+       }
+
+       /**
+        * Returns the value object's name
+        *
+        * @return string The value object's name
+        */
+       public function getName() {
+               return $this->name;
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/ObjectConverterTest.php b/typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/ObjectConverterTest.php
new file mode 100644 (file)
index 0000000..ec59f57
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+namespace TYPO3\CMS\Extbase\Tests\Unit\Property\TypeConverter;
+
+/*                                                                        *
+ * This script belongs to the Extbase framework.                          *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License as published by the *
+ * Free Software Foundation, either version 3 of the License, or (at your *
+ * option) any later version.                                             *
+ *                                                                        *
+ * This script is distributed in the hope that it will be useful, but     *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-    *
+ * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser       *
+ * General Public License for more details.                               *
+ *                                                                        *
+ * You should have received a copy of the GNU Lesser General Public       *
+ * License along with the script.                                         *
+ * If not, see http://www.gnu.org/licenses/lgpl.html                      *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * Testcase for the ObjectConverter
+ *
+ * @covers \TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter<extended>
+ */
+class ObjectConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter
+        */
+       protected $converter;
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
+        */
+       protected $mockReflectionService;
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
+        */
+       protected $mockObjectManager;
+
+       public function setUp() {
+               $this->mockReflectionService = $this->getMock('TYPO3\CMS\Extbase\Reflection\ReflectionService');
+               $this->mockObjectManager = $this->getMock('TYPO3\CMS\Extbase\Object\ObjectManagerInterface');
+
+               $this->converter = new \TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter();
+               $this->inject($this->converter, 'reflectionService', $this->mockReflectionService);
+               $this->inject($this->converter, 'objectManager', $this->mockObjectManager);
+       }
+
+       /**
+        * @test
+        */
+       public function checkMetadata() {
+               $this->assertEquals(array('array'), $this->converter->getSupportedSourceTypes(), 'Source types do not match');
+               $this->assertEquals('object', $this->converter->getSupportedTargetType(), 'Target type does not match');
+               $this->assertEquals(0, $this->converter->getPriority(), 'Priority does not match');
+       }
+
+       public function dataProviderForCanConvert() {
+               return array(
+                       // Is entity => cannot convert
+                       array('TYPO3\\CMS\\Extbase\\Tests\\Fixture\\Entity', FALSE),
+                       // Is valueobject => cannot convert
+                       array('TYPO3\\CMS\\Extbase\\Tests\\Fixture\\ValueObject', FALSE),
+                       // Is no entity and no value object => can convert
+                       array('stdClass', TRUE)
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider dataProviderForCanConvert
+        */
+       public function canConvertFromReturnsTrueIfClassIsTaggedWithEntityOrValueObject($className, $expected) {
+               $this->assertEquals($expected, $this->converter->canConvertFrom('myInputData', $className));
+       }
+
+       /**
+        * @test
+        */
+       public function getTypeOfChildPropertyShouldUseReflectionServiceToDetermineType() {
+               $this->mockReflectionService->expects($this->any())->method('hasMethod')->with('TheTargetType', 'setThePropertyName')->will($this->returnValue(FALSE));
+               $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with('TheTargetType', '__construct')->will($this->returnValue(array(
+                       'thePropertyName' => array(
+                               'type' => 'TheTypeOfSubObject',
+                               'elementType' => NULL
+                       )
+               )));
+               $configuration = new \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration();
+               $configuration->setTypeConverterOptions('TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter', array());
+               $this->assertEquals('TheTypeOfSubObject', $this->converter->getTypeOfChildProperty('TheTargetType', 'thePropertyName', $configuration));
+       }
+
+}
+?>
\ No newline at end of file
index 6d7cccd..9ad9fcc 100644 (file)
@@ -21,9 +21,6 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Property\TypeConverter;
  * The TYPO3 project - inspiring people to share!                         *
  *                                                                        */
 
-require_once __DIR__ . '/../../../Fixture/ClassWithSetters.php';
-require_once __DIR__ . '/../../../Fixture/ClassWithSettersAndConstructor.php';
-
 /**
  * Testcase for the PersistentObjectConverter
  *
@@ -324,9 +321,18 @@ class PersistentObjectConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTe
                $expectedObject = new \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor('param1');
                $expectedObject->setProperty2('bar');
 
-               $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')->will($this->returnValue(array(
-                       'property1' => array('optional' => FALSE)
-               )));
+               $this->mockReflectionService
+                               ->expects($this->any())
+                               ->method('getMethodParameters')
+                               ->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')
+                               ->will($this->returnValue(array(
+                                       'property1' => array('optional' => FALSE)
+                               )));
+               $this->mockReflectionService
+                               ->expects($this->any())
+                               ->method('hasMethod')
+                               ->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')
+                               ->will($this->returnValue(TRUE));
                $this->mockObjectManager->expects($this->any())->method('getClassNameByObjectName')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor')->will($this->returnValue('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor'));
                $configuration = $this->buildConfiguration(array(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
                $result = $this->converter->convertFrom($source, 'TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', $convertedChildProperties, $configuration);
@@ -346,6 +352,11 @@ class PersistentObjectConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTe
                $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')->will($this->returnValue(array(
                        'property1' => array('optional' => TRUE, 'defaultValue' => 'thisIsTheDefaultValue')
                )));
+               $this->mockReflectionService
+                               ->expects($this->any())
+                               ->method('hasMethod')
+                               ->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')
+                               ->will($this->returnValue(TRUE));
                $this->mockObjectManager->expects($this->any())->method('getClassNameByObjectName')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor')->will($this->returnValue('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor'));
                $configuration = $this->buildConfiguration(array(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
                $result = $this->converter->convertFrom($source, 'TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', array(), $configuration);
@@ -368,6 +379,11 @@ class PersistentObjectConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTe
                $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')->will($this->returnValue(array(
                        'property1' => array('optional' => FALSE)
                )));
+               $this->mockReflectionService
+                               ->expects($this->any())
+                               ->method('hasMethod')
+                               ->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')
+                               ->will($this->returnValue(TRUE));
                $this->mockObjectManager->expects($this->any())->method('getClassNameByObjectName')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor')->will($this->returnValue('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor'));
                $configuration = $this->buildConfiguration(array(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
                $result = $this->converter->convertFrom($source, 'TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', $convertedChildProperties, $configuration);
index 9af59f0..d3cd6d1 100644 (file)
@@ -38,9 +38,18 @@ class ReflectionServiceTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
        /**
         * @test
         */
+       public function hasMethod() {
+               $service = new \TYPO3\CMS\Extbase\Reflection\ReflectionService();
+               $this->assertTrue($service->hasMethod(get_class($this), 'fixtureMethodForMethodTagsValues'));
+               $this->assertFalse($service->hasMethod(get_class($this), 'notExistentMethod'));
+       }
+
+       /**
+        * @test
+        */
        public function getMethodTagsValues() {
                $service = new \TYPO3\CMS\Extbase\Reflection\ReflectionService();
-               $tagsValues = $service->getMethodTagsValues('TYPO3\\CMS\\Extbase\\Tests\\Unit\\Reflection\\ReflectionServiceTest', 'fixtureMethodForMethodTagsValues');
+               $tagsValues = $service->getMethodTagsValues(get_class($this), 'fixtureMethodForMethodTagsValues');
                $this->assertEquals(array(
                        'param' => array('array $foo The foo parameter'),
                        'return' => array('void')
@@ -52,7 +61,7 @@ class ReflectionServiceTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
         */
        public function getMethodParameters() {
                $service = new \TYPO3\CMS\Extbase\Reflection\ReflectionService();
-               $parameters = $service->getMethodParameters('TYPO3\\CMS\\Extbase\\Tests\\Unit\\Reflection\\ReflectionServiceTest', 'fixtureMethodForMethodTagsValues');
+               $parameters = $service->getMethodParameters(get_class($this), 'fixtureMethodForMethodTagsValues');
                $this->assertEquals(array(
                        'foo' => array(
                                'position' => 0,
index 479aff9..f353447 100644 (file)
@@ -40,6 +40,7 @@ unset($extbaseObjectContainer);
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter('TYPO3\\CMS\\Extbase\\Property\\TypeConverter\\IntegerConverter');
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter('TYPO3\\CMS\\Extbase\\Property\\TypeConverter\\ObjectStorageConverter');
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter('TYPO3\\CMS\\Extbase\\Property\\TypeConverter\\PersistentObjectConverter');
+\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter('TYPO3\\CMS\\Extbase\\Property\\TypeConverter\\ObjectConverter');
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter('TYPO3\\CMS\\Extbase\\Property\\TypeConverter\\StringConverter');
 // Experimental FAL<->extbase converters
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter('TYPO3\\CMS\\Extbase\\Property\\TypeConverter\\FileConverter');