first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Property;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* An generic Property related exception
*/
class Exception extends \TYPO3\CMS\Extbase\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Property\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* A "Duplicate Object" Exception
*/
class DuplicateObjectException extends \TYPO3\CMS\Extbase\Property\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Property\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* A "Duplicate Type Converter" Exception
*/
class DuplicateTypeConverterException extends \TYPO3\CMS\Extbase\Property\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Property\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* An "Invalid Data Type" Exception
*/
class InvalidDataTypeException extends \TYPO3\CMS\Extbase\Property\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Property\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* An "Invalid Property Mapping Configuration" Exception
*/
class InvalidPropertyMappingConfigurationException extends \TYPO3\CMS\Extbase\Property\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Property\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* An "Invalid Source" Exception
*/
class InvalidSourceException extends \TYPO3\CMS\Extbase\Property\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Property\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* An "Invalid Target" Exception
*/
class InvalidTargetException extends \TYPO3\CMS\Extbase\Property\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Property\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* A "TargetNotFound" Exception
*/
class TargetNotFoundException extends \TYPO3\CMS\Extbase\Property\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Property\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* A "Type Converter" Exception
*/
class TypeConverterException extends \TYPO3\CMS\Extbase\Property\Exception
{
}
@@ -0,0 +1,387 @@
<?php
namespace TYPO3\CMS\Extbase\Property;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Extbase\Property\Exception\TargetNotFoundException;
use TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter;
use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
/**
* The Property Mapper transforms simple types (arrays, strings, integers, floats, booleans) to objects or other simple types.
* It is used most prominently to map incoming HTTP arguments to objects.
*/
class PropertyMapper implements \TYPO3\CMS\Core\SingletonInterface
{
/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* @var \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder
*/
protected $configurationBuilder;
/**
* A multi-dimensional array which stores the Type Converters available in the system.
* It has the following structure:
* 1. Dimension: Source Type
* 2. Dimension: Target Type
* 3. Dimension: Priority
* Value: Type Converter instance
*
* @var array
*/
protected $typeConverters = [];
/**
* A list of property mapping messages (errors, warnings) which have occurred on last mapping.
*
* @var \TYPO3\CMS\Extbase\Error\Result
*/
protected $messages;
/**
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder $configurationBuilder
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function injectConfigurationBuilder(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder $configurationBuilder)
{
$this->configurationBuilder = $configurationBuilder;
}
/**
* Lifecycle method, called after all dependencies have been injected.
* Here, the typeConverter array gets initialized.
*
* @throws Exception\DuplicateTypeConverterException
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function initializeObject()
{
foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'] as $typeConverterClassName) {
$typeConverter = $this->objectManager->get($typeConverterClassName);
foreach ($typeConverter->getSupportedSourceTypes() as $supportedSourceType) {
if (isset($this->typeConverters[$supportedSourceType][$typeConverter->getSupportedTargetType()][$typeConverter->getPriority()])) {
throw new Exception\DuplicateTypeConverterException('There exist at least two converters which handle the conversion from "' . $supportedSourceType . '" to "' . $typeConverter->getSupportedTargetType() . '" with priority "' . $typeConverter->getPriority() . '": ' . get_class($this->typeConverters[$supportedSourceType][$typeConverter->getSupportedTargetType()][$typeConverter->getPriority()]) . ' and ' . get_class($typeConverter), 1297951378);
}
$this->typeConverters[$supportedSourceType][$typeConverter->getSupportedTargetType()][$typeConverter->getPriority()] = $typeConverter;
}
}
}
/**
* Map $source to $targetType, and return the result
*
* @param mixed $source the source data to map. MUST be a simple type, NO object allowed!
* @param string $targetType The type of the target; can be either a class name or a simple type.
* @param PropertyMappingConfigurationInterface $configuration Configuration for the property mapping. If NULL, the PropertyMappingConfigurationBuilder will create a default configuration.
* @throws Exception
* @return mixed an instance of $targetType
*/
public function convert($source, $targetType, PropertyMappingConfigurationInterface $configuration = null)
{
if ($configuration === null) {
$configuration = $this->configurationBuilder->build();
}
$currentPropertyPath = [];
$this->messages = new \TYPO3\CMS\Extbase\Error\Result();
try {
$result = $this->doMapping($source, $targetType, $configuration, $currentPropertyPath);
if ($result instanceof \TYPO3\CMS\Extbase\Error\Error) {
return null;
}
return $result;
} catch (TargetNotFoundException $e) {
throw $e;
} catch (\Exception $e) {
throw new Exception('Exception while property mapping at property path "' . implode('.', $currentPropertyPath) . '": ' . $e->getMessage(), 1297759968, $e);
}
}
/**
* Get the messages of the last Property Mapping
*
* @return \TYPO3\CMS\Extbase\Error\Result
*/
public function getMessages()
{
return $this->messages;
}
/**
* Internal function which actually does the property mapping.
*
* @param mixed $source the source data to map. MUST be a simple type, NO object allowed!
* @param string $targetType The type of the target; can be either a class name or a simple type.
* @param PropertyMappingConfigurationInterface $configuration Configuration for the property mapping.
* @param array &$currentPropertyPath The property path currently being mapped; used for knowing the context in case an exception is thrown.
* @throws Exception\TypeConverterException
* @throws Exception\InvalidPropertyMappingConfigurationException
* @return mixed an instance of $targetType
*/
protected function doMapping($source, $targetType, PropertyMappingConfigurationInterface $configuration, &$currentPropertyPath)
{
if (is_object($source)) {
$targetType = $this->parseCompositeType($targetType);
if ($source instanceof $targetType) {
return $source;
}
}
if ($source === null) {
$source = '';
}
$typeConverter = $this->findTypeConverter($source, $targetType, $configuration);
$targetType = $typeConverter->getTargetTypeForSource($source, $targetType, $configuration);
if (!is_object($typeConverter) || !$typeConverter instanceof \TYPO3\CMS\Extbase\Property\TypeConverterInterface) {
throw new Exception\TypeConverterException(
'Type converter for "' . $source . '" -> "' . $targetType . '" not found.',
1476045062
);
}
$convertedChildProperties = [];
foreach ($typeConverter->getSourceChildPropertiesToBeConverted($source) as $sourcePropertyName => $sourcePropertyValue) {
$targetPropertyName = $configuration->getTargetPropertyName($sourcePropertyName);
if ($configuration->shouldSkip($targetPropertyName)) {
continue;
}
if (!$configuration->shouldMap($targetPropertyName)) {
if ($configuration->shouldSkipUnknownProperties()) {
continue;
}
throw new Exception\InvalidPropertyMappingConfigurationException('It is not allowed to map property "' . $targetPropertyName . '". You need to use $propertyMappingConfiguration->allowProperties(\'' . $targetPropertyName . '\') to enable mapping of this property.', 1355155913);
}
$targetPropertyType = $typeConverter->getTypeOfChildProperty($targetType, $targetPropertyName, $configuration);
$subConfiguration = $configuration->getConfigurationFor($targetPropertyName);
$currentPropertyPath[] = $targetPropertyName;
$targetPropertyValue = $this->doMapping($sourcePropertyValue, $targetPropertyType, $subConfiguration, $currentPropertyPath);
array_pop($currentPropertyPath);
if (!($targetPropertyValue instanceof \TYPO3\CMS\Extbase\Error\Error)) {
$convertedChildProperties[$targetPropertyName] = $targetPropertyValue;
}
}
$result = $typeConverter->convertFrom($source, $targetType, $convertedChildProperties, $configuration);
if ($result instanceof \TYPO3\CMS\Extbase\Error\Error) {
$this->messages->forProperty(implode('.', $currentPropertyPath))->addError($result);
}
return $result;
}
/**
* Determine the type converter to be used. If no converter has been found, an exception is raised.
*
* @param mixed $source
* @param string $targetType
* @param PropertyMappingConfigurationInterface $configuration
* @throws Exception\TypeConverterException
* @throws Exception\InvalidTargetException
* @return \TYPO3\CMS\Extbase\Property\TypeConverterInterface Type Converter which should be used to convert between $source and $targetType.
*/
protected function findTypeConverter($source, $targetType, PropertyMappingConfigurationInterface $configuration)
{
if ($configuration->getTypeConverter() !== null) {
return $configuration->getTypeConverter();
}
$sourceType = $this->determineSourceType($source);
if (!is_string($targetType)) {
throw new Exception\InvalidTargetException('The target type was no string, but of type "' . gettype($targetType) . '"', 1297941727);
}
$targetType = $this->parseCompositeType($targetType);
// This is needed to correctly convert old class names to new ones
// This compatibility layer will be removed with 7.0
$targetType = \TYPO3\CMS\Core\Core\ClassLoadingInformation::getClassNameForAlias($targetType);
$targetType = TypeHandlingUtility::normalizeType($targetType);
$converter = null;
if (TypeHandlingUtility::isSimpleType($targetType)) {
if (isset($this->typeConverters[$sourceType][$targetType])) {
$converter = $this->findEligibleConverterWithHighestPriority($this->typeConverters[$sourceType][$targetType], $source, $targetType);
}
} else {
$converter = $this->findFirstEligibleTypeConverterInObjectHierarchy($source, $sourceType, $targetType);
}
if ($converter === null) {
throw new Exception\TypeConverterException(
'No converter found which can be used to convert from "' . $sourceType . '" to "' . $targetType . '".',
1476044883
);
}
return $converter;
}
/**
* Tries to find a suitable type converter for the given source and target type.
*
* @param string $source The actual source value
* @param string $sourceType Type of the source to convert from
* @param string $targetClass Name of the target class to find a type converter for
* @return mixed Either the matching object converter or NULL
* @throws Exception\InvalidTargetException
*/
protected function findFirstEligibleTypeConverterInObjectHierarchy($source, $sourceType, $targetClass)
{
if (!class_exists($targetClass) && !interface_exists($targetClass)) {
throw new Exception\InvalidTargetException('Could not find a suitable type converter for "' . $targetClass . '" because no such class or interface exists.', 1297948764);
}
if (!isset($this->typeConverters[$sourceType])) {
return null;
}
$convertersForSource = $this->typeConverters[$sourceType];
if (isset($convertersForSource[$targetClass])) {
$converter = $this->findEligibleConverterWithHighestPriority($convertersForSource[$targetClass], $source, $targetClass);
if ($converter !== null) {
return $converter;
}
}
foreach (class_parents($targetClass) as $parentClass) {
if (!isset($convertersForSource[$parentClass])) {
continue;
}
$converter = $this->findEligibleConverterWithHighestPriority($convertersForSource[$parentClass], $source, $targetClass);
if ($converter !== null) {
return $converter;
}
}
$converters = $this->getConvertersForInterfaces($convertersForSource, class_implements($targetClass));
$converter = $this->findEligibleConverterWithHighestPriority($converters, $source, $targetClass);
if ($converter !== null) {
return $converter;
}
if (isset($convertersForSource['object'])) {
return $this->findEligibleConverterWithHighestPriority($convertersForSource['object'], $source, $targetClass);
}
return null;
}
/**
* @param mixed $converters
* @param mixed $source
* @param string $targetType
* @return mixed Either the matching object converter or NULL
*/
protected function findEligibleConverterWithHighestPriority($converters, $source, $targetType)
{
if (!is_array($converters)) {
return null;
}
krsort($converters, SORT_NUMERIC);
reset($converters);
/** @var AbstractTypeConverter $converter */
foreach ($converters as $converter) {
if ($converter->canConvertFrom($source, $targetType)) {
return $converter;
}
}
return null;
}
/**
* @param array $convertersForSource
* @param array $interfaceNames
* @return array
* @throws Exception\DuplicateTypeConverterException
*/
protected function getConvertersForInterfaces(array $convertersForSource, array $interfaceNames)
{
$convertersForInterface = [];
foreach ($interfaceNames as $implementedInterface) {
if (isset($convertersForSource[$implementedInterface])) {
foreach ($convertersForSource[$implementedInterface] as $priority => $converter) {
if (isset($convertersForInterface[$priority])) {
throw new Exception\DuplicateTypeConverterException('There exist at least two converters which handle the conversion to an interface with priority "' . $priority . '". ' . get_class($convertersForInterface[$priority]) . ' and ' . get_class($converter), 1297951338);
}
$convertersForInterface[$priority] = $converter;
}
}
}
return $convertersForInterface;
}
/**
* Determine the type of the source data, or throw an exception if source was an unsupported format.
*
* @param mixed $source
* @throws Exception\InvalidSourceException
* @return string the type of $source
*/
protected function determineSourceType($source)
{
if (is_string($source)) {
return 'string';
}
if (is_array($source)) {
return 'array';
}
if (is_float($source)) {
return 'float';
}
if (is_int($source)) {
return 'integer';
}
if (is_bool($source)) {
return 'boolean';
}
throw new Exception\InvalidSourceException('The source is not of type string, array, float, integer or boolean, but of type "' . gettype($source) . '"', 1297773150);
}
/**
* Parse a composite type like \Foo\Collection<\Bar\Entity> into
* \Foo\Collection
*
* @param string $compositeType
* @return string
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function parseCompositeType($compositeType)
{
if (strpos($compositeType, '<') !== false) {
$compositeType = substr($compositeType, 0, strpos($compositeType, '<'));
}
return $compositeType;
}
}
@@ -0,0 +1,380 @@
<?php
namespace TYPO3\CMS\Extbase\Property;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Concrete configuration object for the PropertyMapper.
*/
class PropertyMappingConfiguration implements PropertyMappingConfigurationInterface
{
/**
* Placeholder in property paths for multi-valued types
*/
const PROPERTY_PATH_PLACEHOLDER = '*';
/**
* multi-dimensional array which stores type-converter specific configuration:
* 1. Dimension: Fully qualified class name of the type converter
* 2. Dimension: Configuration Key
* Value: Configuration Value
*
* @var array
*/
protected $configuration;
/**
* Stores the configuration for specific child properties.
*
* @var \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface[]
*/
protected $subConfigurationForProperty = [];
/**
* Keys which should be renamed
*
* @var array
*/
protected $mapping = [];
/**
* @var \TYPO3\CMS\Extbase\Property\TypeConverterInterface
*/
protected $typeConverter;
/**
* List of allowed property names to be converted
*
* @var array
*/
protected $propertiesToBeMapped = [];
/**
* List of property names to be skipped during property mapping
*
* @var array
*/
protected $propertiesToSkip = [];
/**
* List of disallowed property names which will be ignored while property mapping
*
* @var array
*/
protected $propertiesNotToBeMapped = [];
/**
* If TRUE, unknown properties will be skipped during property mapping
*
* @var bool
*/
protected $skipUnknownProperties = false;
/**
* If TRUE, unknown properties will be mapped.
*
* @var bool
*/
protected $mapUnknownProperties = false;
/**
* The behavior is as follows:
*
* - if a property has been explicitly forbidden using allowAllPropertiesExcept(...), it is directly rejected
* - if a property has been allowed using allowProperties(...), it is directly allowed.
* - if allowAllProperties* has been called, we allow unknown properties
* - else, return FALSE.
*
* @param string $propertyName
* @return bool TRUE if the given propertyName should be mapped, FALSE otherwise.
*/
public function shouldMap($propertyName)
{
if (isset($this->propertiesNotToBeMapped[$propertyName])) {
return false;
}
if (isset($this->propertiesToBeMapped[$propertyName])) {
return true;
}
if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
return true;
}
return $this->mapUnknownProperties;
}
/**
* Check if the given $propertyName should be skipped during mapping.
*
* @param string $propertyName
* @return bool
*/
public function shouldSkip($propertyName)
{
return isset($this->propertiesToSkip[$propertyName]);
}
/**
* Allow all properties in property mapping, even unknown ones.
*
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
*/
public function allowAllProperties()
{
$this->mapUnknownProperties = true;
return $this;
}
/**
* Allow a list of specific properties. All arguments of
* allowProperties are used here (varargs).
*
* Example: allowProperties('title', 'content', 'author')
*
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration
*/
public function allowProperties()
{
foreach (func_get_args() as $propertyName) {
$this->propertiesToBeMapped[$propertyName] = $propertyName;
}
return $this;
}
/**
* Skip a list of specific properties. All arguments of
* skipProperties are used here (varargs).
*
* Example: skipProperties('unused', 'dummy')
*
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
*/
public function skipProperties()
{
foreach (func_get_args() as $propertyName) {
$this->propertiesToSkip[$propertyName] = $propertyName;
}
return $this;
}
/**
* Allow all properties during property mapping, but reject a few
* selected ones (blacklist).
*
* Example: allowAllPropertiesExcept('password', 'userGroup')
*
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
*/
public function allowAllPropertiesExcept()
{
$this->mapUnknownProperties = true;
foreach (func_get_args() as $propertyName) {
$this->propertiesNotToBeMapped[$propertyName] = $propertyName;
}
return $this;
}
/**
* When this is enabled, properties that are disallowed will be skipped
* instead of triggering an error during mapping.
*
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
*/
public function skipUnknownProperties()
{
$this->skipUnknownProperties = true;
return $this;
}
/**
* Whether unknown (unconfigured) properties should be skipped during
* mapping, instead if causing an error.
*
* @return bool
*/
public function shouldSkipUnknownProperties()
{
return $this->skipUnknownProperties;
}
/**
* Returns the sub-configuration for the passed $propertyName. Must ALWAYS return a valid configuration object!
*
* @param string $propertyName
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface the property mapping configuration for the given $propertyName.
*/
public function getConfigurationFor($propertyName)
{
if (isset($this->subConfigurationForProperty[$propertyName])) {
return $this->subConfigurationForProperty[$propertyName];
}
if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
return $this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER];
}
return new self();
}
/**
* Maps the given $sourcePropertyName to a target property name.
*
* @param string $sourcePropertyName
* @return string property name of target
*/
public function getTargetPropertyName($sourcePropertyName)
{
if (isset($this->mapping[$sourcePropertyName])) {
return $this->mapping[$sourcePropertyName];
}
return $sourcePropertyName;
}
/**
* @param string $typeConverterClassName
* @param string $key
* @return mixed configuration value for the specific $typeConverterClassName. Can be used by Type Converters to fetch converter-specific configuration.
*/
public function getConfigurationValue($typeConverterClassName, $key)
{
if (!isset($this->configuration[$typeConverterClassName][$key])) {
return null;
}
return $this->configuration[$typeConverterClassName][$key];
}
/**
* Define renaming from Source to Target property.
*
* @param string $sourcePropertyName
* @param string $targetPropertyName
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
*/
public function setMapping($sourcePropertyName, $targetPropertyName)
{
$this->mapping[$sourcePropertyName] = $targetPropertyName;
return $this;
}
/**
* Set all options for the given $typeConverter.
*
* @param string $typeConverter class name of type converter
* @param array $options
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
*/
public function setTypeConverterOptions($typeConverter, array $options)
{
foreach ($this->getTypeConvertersWithParentClasses($typeConverter) as $typeConverter) {
$this->configuration[$typeConverter] = $options;
}
return $this;
}
/**
* Set a single option (denoted by $optionKey) for the given $typeConverter.
*
* @param string $typeConverter class name of type converter
* @param string $optionKey
* @param mixed $optionValue
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
*/
public function setTypeConverterOption($typeConverter, $optionKey, $optionValue)
{
foreach ($this->getTypeConvertersWithParentClasses($typeConverter) as $typeConverter) {
$this->configuration[$typeConverter][$optionKey] = $optionValue;
}
return $this;
}
/**
* Get type converter classes including parents for the given type converter
*
* When setting an option on a subclassed type converter, this option must also be set on
* all its parent type converters.
*
* @param string $typeConverter The type converter class
* @return array Class names of type converters
*/
protected function getTypeConvertersWithParentClasses($typeConverter)
{
$typeConverterClasses = class_parents($typeConverter);
$typeConverterClasses = $typeConverterClasses === false ? [] : $typeConverterClasses;
$typeConverterClasses[] = $typeConverter;
return $typeConverterClasses;
}
/**
* Returns the configuration for the specific property path, ready to be modified. Should be used
* inside a fluent interface like:
* $configuration->forProperty('foo.bar')->setTypeConverterOption(....)
*
* @param string $propertyPath
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
*/
public function forProperty($propertyPath)
{
$splittedPropertyPath = explode('.', $propertyPath);
return $this->traverseProperties($splittedPropertyPath);
}
/**
* Traverse the property configuration. Only used by forProperty().
*
* @param array $splittedPropertyPath
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
*/
public function traverseProperties(array $splittedPropertyPath)
{
if (empty($splittedPropertyPath)) {
return $this;
}
$currentProperty = array_shift($splittedPropertyPath);
if (!isset($this->subConfigurationForProperty[$currentProperty])) {
$type = static::class;
if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
$this->subConfigurationForProperty[$currentProperty] = clone $this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER];
} else {
$this->subConfigurationForProperty[$currentProperty] = new $type;
}
}
return $this->subConfigurationForProperty[$currentProperty]->traverseProperties($splittedPropertyPath);
}
/**
* Return the type converter set for this configuration.
*
* @return \TYPO3\CMS\Extbase\Property\TypeConverterInterface
*/
public function getTypeConverter()
{
return $this->typeConverter;
}
/**
* Set a type converter which should be used for this specific conversion.
*
* @param \TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
*/
public function setTypeConverter(\TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter)
{
$this->typeConverter = $typeConverter;
return $this;
}
}
@@ -0,0 +1,42 @@
<?php
namespace TYPO3\CMS\Extbase\Property;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* This builder creates the default configuration for Property Mapping, if no configuration has been passed to the Property Mapper.
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
class PropertyMappingConfigurationBuilder implements \TYPO3\CMS\Core\SingletonInterface
{
/**
* Builds the default property mapping configuration.
*
* @param string $type the implementation class name of the PropertyMappingConfiguration to instanciate; must be a subclass of \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration
*/
public function build($type = \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration::class)
{
/** @var \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration $configuration */
$configuration = new $type();
$configuration->setTypeConverterOptions(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, [
\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => true,
\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED => true
]);
$configuration->allowAllProperties();
return $configuration;
}
}
@@ -0,0 +1,68 @@
<?php
namespace TYPO3\CMS\Extbase\Property;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Configuration object for the property mapper.
*/
interface PropertyMappingConfigurationInterface
{
/**
* returns TRUE if the given propertyName should be mapped, FALSE otherwise.
*
* @param string $propertyName
* @return bool
*/
public function shouldSkip($propertyName);
/**
* Whether unknown (unconfigured) properties should be skipped during
* mapping, instead if causing an error.
*
* @return bool
*/
public function shouldSkipUnknownProperties();
/**
* Returns the sub-configuration for the passed $propertyName. Must ALWAYS return a valid configuration object!
*
* @param string $propertyName
* @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface the property mapping configuration for the given $propertyName.
*/
public function getConfigurationFor($propertyName);
/**
* Maps the given $sourcePropertyName to a target property name.
* Can be used to rename properties from source to target.
*
* @param string $sourcePropertyName
* @return string property name of target
*/
public function getTargetPropertyName($sourcePropertyName);
/**
* @param string $typeConverterClassName
* @param string $key
* @return mixed configuration value for the specific $typeConverterClassName. Can be used by Type Converters to fetch converter-specific configuration
*/
public function getConfigurationValue($typeConverterClassName, $key);
/**
* This method can be used to explicitly force a TypeConverter to be used for this Configuration.
*
* @return \TYPO3\CMS\Extbase\Property\TypeConverterInterface The type converter to be used for this particular PropertyMappingConfiguration, or NULL if the system-wide configured type converter should be used.
*/
public function getTypeConverter();
}
@@ -0,0 +1,75 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms simple types to \TYPO3\CMS\Extbase\Domain\Model\File.
*
* @internal experimental! This class is experimental and subject to change!
*/
abstract class AbstractFileCollectionConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
{
/**
* @var int
*/
protected $priority = 10;
/**
* @var string
*/
protected $expectedObjectType;
/**
* @var \TYPO3\CMS\Core\Resource\ResourceFactory
*/
protected $fileFactory;
/**
* @param \TYPO3\CMS\Core\Resource\ResourceFactory $fileFactory
*/
public function injectFileFactory(\TYPO3\CMS\Core\Resource\ResourceFactory $fileFactory)
{
$this->fileFactory = $fileFactory;
}
/**
* Actually convert from $source to $targetType, taking into account the fully
* built $convertedChildProperties and $configuration.
*
* @param int $source
* @param string $targetType
* @param array $convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @throws \TYPO3\CMS\Extbase\Property\Exception
* @return \TYPO3\CMS\Extbase\Domain\Model\AbstractFileCollection
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
$object = $this->getObject($source);
if (empty($this->expectedObjectType) || !$object instanceof $this->expectedObjectType) {
throw new \TYPO3\CMS\Extbase\Property\Exception('Expected object of type "' . $this->expectedObjectType . '" but got ' . get_class($object), 1342895976);
}
/** @var \TYPO3\CMS\Extbase\Domain\Model\AbstractFileCollection $subject */
$subject = $this->objectManager->get($targetType);
$subject->setObject($object);
return $subject;
}
/**
* @param int $source
* @return \TYPO3\CMS\Core\Resource\Collection\AbstractFileCollection
*/
abstract protected function getObject($source);
}
@@ -0,0 +1,75 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms simple types to \TYPO3\CMS\Extbase\Domain\Model\File.
*
* @internal experimental! This class is experimental and subject to change!
*/
abstract class AbstractFileFolderConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
{
/**
* @var int
*/
protected $priority = 10;
/**
* @var string
*/
protected $expectedObjectType;
/**
* @var \TYPO3\CMS\Core\Resource\ResourceFactory
*/
protected $fileFactory;
/**
* @param \TYPO3\CMS\Core\Resource\ResourceFactory $fileFactory
*/
public function injectFileFactory(\TYPO3\CMS\Core\Resource\ResourceFactory $fileFactory)
{
$this->fileFactory = $fileFactory;
}
/**
* Actually convert from $source to $targetType, taking into account the fully
* built $convertedChildProperties and $configuration.
*
* @param string|int $source
* @param string $targetType
* @param array $convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @throws \TYPO3\CMS\Extbase\Property\Exception
* @return \TYPO3\CMS\Extbase\Domain\Model\AbstractFileFolder
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
$object = $this->getOriginalResource($source);
if (empty($this->expectedObjectType) || !$object instanceof $this->expectedObjectType) {
throw new \TYPO3\CMS\Extbase\Property\Exception('Expected object of type "' . $this->expectedObjectType . '" but got ' . get_class($object), 1342895975);
}
/** @var \TYPO3\CMS\Extbase\Domain\Model\AbstractFileFolder $subject */
$subject = $this->objectManager->get($targetType);
$subject->setOriginalResource($object);
return $subject;
}
/**
* @param string|int $source
* @return \TYPO3\CMS\Core\Resource\ResourceInterface
*/
abstract protected function getOriginalResource($source);
}
@@ -0,0 +1,140 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Type converter which provides sensible default implementations for most methods. If you extend this class
* you only need to do the following:
* - set $sourceTypes
* - set $targetType
* - set $priority
* - implement convertFrom()
*/
abstract class AbstractTypeConverter implements \TYPO3\CMS\Extbase\Property\TypeConverterInterface, \TYPO3\CMS\Core\SingletonInterface
{
/**
* The source types this converter can convert.
*
* @var array<string>
*/
protected $sourceTypes = [];
/**
* The target type this converter can convert to.
*
* @var string
*/
protected $targetType = '';
/**
* The priority for this converter.
*
* @var int
*/
protected $priority;
/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* Returns the list of source types the TypeConverter can handle.
* Must be PHP simple types, classes or object is not allowed.
*
* @return array<string>
*/
public function getSupportedSourceTypes()
{
return $this->sourceTypes;
}
/**
* Return the target type this TypeConverter converts to.
* Can be a simple type or a class name.
*
* @return string
*/
public function getSupportedTargetType()
{
return $this->targetType;
}
/**
* Returns the $originalTargetType unchanged in this implementation.
*
* @param mixed $source the source data
* @param string $originalTargetType the type we originally want to convert to
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return string
*/
public function getTargetTypeForSource($source, $originalTargetType, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
return $originalTargetType;
}
/**
* Return the priority of this TypeConverter. TypeConverters with a high priority are chosen before low priority.
*
* @return int
*/
public function getPriority()
{
return $this->priority;
}
/**
* This implementation always returns TRUE for this method.
*
* @param mixed $source the source data
* @param string $targetType the type to convert to.
* @return bool TRUE if this TypeConverter can convert from $source to $targetType, FALSE otherwise.
*/
public function canConvertFrom($source, $targetType)
{
return true;
}
/**
* Returns an empty list of sub property names
*
* @param mixed $source
* @return array
*/
public function getSourceChildPropertiesToBeConverted($source)
{
return [];
}
/**
* This method is never called, as getSourceChildPropertiesToBeConverted() returns an empty array.
*
* @param string $targetType
* @param string $propertyName
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
*/
public function getTypeOfChildProperty($targetType, $propertyName, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration)
{
}
}
@@ -0,0 +1,70 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms arrays to arrays.
*/
class ArrayConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
{
/**
* @var array<string>
*/
protected $sourceTypes = ['array', 'string'];
/**
* @var string
*/
protected $targetType = 'array';
/**
* @var int
*/
protected $priority = 10;
/**
* We can only convert empty strings to array or array to array.
*
* @param mixed $source
* @param string $targetType
* @return bool
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function canConvertFrom($source, $targetType)
{
return is_string($source) && $source === '' || is_array($source);
}
/**
* Convert from $source to $targetType, a noop if the source is an array.
* If it is an empty string it will be converted to an empty array.
*
* @param string|array $source
* @param string $targetType
* @param array $convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return array
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
if (is_string($source)) {
if ($source === '') {
$source = [];
}
}
return $source;
}
}
@@ -0,0 +1,50 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms simple types to a boolean, by simply casting it.
*/
class BooleanConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
{
/**
* @var array<string>
*/
protected $sourceTypes = ['boolean', 'string'];
/**
* @var string
*/
protected $targetType = 'boolean';
/**
* @var int
*/
protected $priority = 10;
/**
* Actually convert from $source to $targetType
*
* @param string $source
* @param string $targetType
* @param array $convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return bool
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
return (bool)$source;
}
}
@@ -0,0 +1,70 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Type\Exception\InvalidValueExceptionInterface;
use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
/**
* Converter which transforms simple types to a core type
* implementing \TYPO3\CMS\Core\Type\TypeInterface.
*/
class CoreTypeConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
{
/**
* @var array<string>
*/
protected $sourceTypes = ['string', 'integer', 'float', 'boolean', 'array'];
/**
* @var string
*/
protected $targetType = \TYPO3\CMS\Core\Type\TypeInterface::class;
/**
* @var int
*/
protected $priority = 10;
/**
* @param mixed $source
* @param string $targetType
* @return bool
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function canConvertFrom($source, $targetType)
{
return TypeHandlingUtility::isCoreType($targetType);
}
/**
* Convert an object from $source to an Enumeration.
*
* @param mixed $source
* @param string $targetType
* @param array $convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return object the target type
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
try {
return new $targetType($source);
} catch (InvalidValueExceptionInterface $exception) {
return new \TYPO3\CMS\Extbase\Error\Error($exception->getMessage(), 1381680012);
}
}
}
@@ -0,0 +1,220 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms from different input formats into DateTime objects.
*
* Source can be either a string or an array. The date string is expected to be formatted
* according to DEFAULT_DATE_FORMAT.
*
* But the default date format can be overridden in the initialize*Action() method like this::
*
* $this->arguments['<argumentName>']
* ->getPropertyMappingConfiguration()
* ->forProperty('<propertyName>') // this line can be skipped in order to specify the format for all properties
* ->setTypeConverterOption(\TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter::CONFIGURATION_DATE_FORMAT, '<dateFormat>');
*
* If the source is of type array, it is possible to override the format in the source::
*
* array(
* 'date' => '<dateString>',
* 'dateFormat' => '<dateFormat>'
* );
*
* By using an array as source you can also override time and timezone of the created DateTime object::
*
* array(
* 'date' => '<dateString>',
* 'hour' => '<hour>', // integer
* 'minute' => '<minute>', // integer
* 'seconds' => '<seconds>', // integer
* 'timezone' => '<timezone>', // string, see http://www.php.net/manual/timezones.php
* );
*
* As an alternative to providing the date as string, you might supply day, month and year as array items each::
*
* array(
* 'day' => '<day>', // integer
* 'month' => '<month>', // integer
* 'year' => '<year>', // integer
* );
*/
class DateTimeConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
{
/**
* @var string
*/
const CONFIGURATION_DATE_FORMAT = 'dateFormat';
/**
* The default date format is "YYYY-MM-DDT##:##:##+##:##", for example "2005-08-15T15:52:01+00:00"
* according to the W3C standard @see http://www.w3.org/TR/NOTE-datetime.html
*
* @var string
*/
const DEFAULT_DATE_FORMAT = \DateTime::W3C;
/**
* @var array<string>
*/
protected $sourceTypes = ['string', 'integer', 'array'];
/**
* @var string
*/
protected $targetType = 'DateTime';
/**
* @var int
*/
protected $priority = 10;
/**
* If conversion is possible.
*
* @param string $source
* @param string $targetType
* @return bool
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function canConvertFrom($source, $targetType)
{
if (!is_callable([$targetType, 'createFromFormat'])) {
return false;
}
if (is_array($source)) {
return true;
}
if (is_int($source)) {
return true;
}
return is_string($source);
}
/**
* Converts $source to a \DateTime using the configured dateFormat
*
* @param string|int|array $source the string to be converted to a \DateTime object
* @param string $targetType must be "DateTime"
* @param array $convertedChildProperties not used currently
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return \DateTimeInterface|\TYPO3\CMS\Extbase\Error\Error
* @throws \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
$dateFormat = $this->getDefaultDateFormat($configuration);
if (is_string($source)) {
$dateAsString = $source;
} elseif (is_int($source)) {
$dateAsString = strval($source);
} else {
if (isset($source['date']) && is_string($source['date'])) {
$dateAsString = $source['date'];
} elseif (isset($source['date']) && is_int($source['date'])) {
$dateAsString = strval($source['date']);
} elseif ($this->isDatePartKeysProvided($source)) {
if ($source['day'] < 1 || $source['month'] < 1 || $source['year'] < 1) {
return new \TYPO3\CMS\Extbase\Error\Error('Could not convert the given date parts into a DateTime object because one or more parts were 0.', 1333032779);
}
$dateAsString = sprintf('%d-%d-%d', $source['year'], $source['month'], $source['day']);
} else {
throw new \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException('Could not convert the given source into a DateTime object because it was not an array with a valid date as a string', 1308003914);
}
if (isset($source['dateFormat']) && $source['dateFormat'] !== '') {
$dateFormat = $source['dateFormat'];
}
}
if ($dateAsString === '') {
return null;
}
if (ctype_digit($dateAsString) && $configuration === null && (!is_array($source) || !isset($source['dateFormat']))) {
$dateFormat = 'U';
}
if (is_array($source) && isset($source['timezone']) && (string)$source['timezone'] !== '') {
try {
$timezone = new \DateTimeZone($source['timezone']);
} catch (\Exception $e) {
throw new \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException('The specified timezone "' . $source['timezone'] . '" is invalid.', 1308240974);
}
$date = $targetType::createFromFormat($dateFormat, $dateAsString, $timezone);
} else {
$date = $targetType::createFromFormat($dateFormat, $dateAsString);
}
if ($date === false) {
return new \TYPO3\CMS\Extbase\Validation\Error('The date "%s" was not recognized (for format "%s").', 1307719788, [$dateAsString, $dateFormat]);
}
if (is_array($source)) {
$date = $this->overrideTimeIfSpecified($date, $source);
}
return $date;
}
/**
* Returns whether date information (day, month, year) are present as keys in $source.
*
* @param array $source
* @return bool
*/
protected function isDatePartKeysProvided(array $source)
{
return isset($source['day']) && ctype_digit($source['day'])
&& isset($source['month']) && ctype_digit($source['month'])
&& isset($source['year']) && ctype_digit($source['year']);
}
/**
* Determines the default date format to use for the conversion.
* If no format is specified in the mapping configuration DEFAULT_DATE_FORMAT is used.
*
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return string
* @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
*/
protected function getDefaultDateFormat(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
if ($configuration === null) {
return self::DEFAULT_DATE_FORMAT;
}
$dateFormat = $configuration->getConfigurationValue(DateTimeConverter::class, self::CONFIGURATION_DATE_FORMAT);
if ($dateFormat === null) {
return self::DEFAULT_DATE_FORMAT;
}
if ($dateFormat !== null && !is_string($dateFormat)) {
throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException('CONFIGURATION_DATE_FORMAT must be of type string, "' . (is_object($dateFormat) ? get_class($dateFormat) : gettype($dateFormat)) . '" given', 1307719569);
}
return $dateFormat;
}
/**
* Overrides hour, minute & second of the given date with the values in the $source array
*
* @param \DateTimeInterface $date
* @param array $source
* @return \DateTimeInterface
*/
protected function overrideTimeIfSpecified(\DateTimeInterface $date, array $source): \DateTimeInterface
{
if (!isset($source['hour']) && !isset($source['minute']) && !isset($source['second'])) {
return $date;
}
$hour = isset($source['hour']) ? (int)$source['hour'] : 0;
$minute = isset($source['minute']) ? (int)$source['minute'] : 0;
$second = isset($source['second']) ? (int)$source['second'] : 0;
return $date->setTime($hour, $minute, $second);
}
}
@@ -0,0 +1,47 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms simple types to \TYPO3\CMS\Extbase\Domain\Model\File.
*
* @internal experimental! This class is experimental and subject to change!
*/
class FileConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractFileFolderConverter
{
/**
* @var array<string>
*/
protected $sourceTypes = ['integer', 'string'];
/**
* @var string
*/
protected $targetType = \TYPO3\CMS\Extbase\Domain\Model\File::class;
/**
* @var string
*/
protected $expectedObjectType = \TYPO3\CMS\Core\Resource\File::class;
/**
* @param string|int $source
* @return \TYPO3\CMS\Core\Resource\FileInterface|\TYPO3\CMS\Core\Resource\Folder
*/
protected function getOriginalResource($source)
{
return $this->fileFactory->retrieveFileOrFolderObject($source);
}
}
@@ -0,0 +1,47 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms simple types to \TYPO3\CMS\Extbase\Domain\Model\FileReference.
*
* @internal experimental! This class is experimental and subject to change!
*/
class FileReferenceConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractFileFolderConverter
{
/**
* @var array<string>
*/
protected $sourceTypes = ['integer'];
/**
* @var string
*/
protected $targetType = \TYPO3\CMS\Extbase\Domain\Model\FileReference::class;
/**
* @var string
*/
protected $expectedObjectType = \TYPO3\CMS\Core\Resource\FileReference::class;
/**
* @param int $source
* @return \TYPO3\CMS\Core\Resource\FileReference
*/
protected function getOriginalResource($source)
{
return $this->fileFactory->getFileReferenceObject($source);
}
}
@@ -0,0 +1,77 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms a simple type to a float.
*
* This is basically done by simply casting it.
*/
class FloatConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
{
/**
* @var string
*/
const CONFIGURATION_THOUSANDS_SEPARATOR = 'thousandsSeparator';
/**
* @var string
*/
const CONFIGURATION_DECIMAL_POINT = 'decimalPoint';
/**
* @var array<string>
*/
protected $sourceTypes = ['float', 'integer', 'string'];
/**
* @var string
*/
protected $targetType = 'float';
/**
* @var int
*/
protected $priority = 10;
/**
* Actually convert from $source to $targetType, by doing a typecast.
*
* @param mixed $source
* @param string $targetType
* @param array $convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return float|\TYPO3\CMS\Extbase\Error\Error
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
if ($source === null || (string)$source === '') {
return null;
}
// We won't backport the full flavored locale parsing of floats from Flow here
if (is_string($source) && $configuration !== null) {
$thousandsSeparator = $configuration->getConfigurationValue(self::class, self::CONFIGURATION_THOUSANDS_SEPARATOR);
$decimalPoint = $configuration->getConfigurationValue(self::class, self::CONFIGURATION_DECIMAL_POINT);
$source = str_replace([$thousandsSeparator, $decimalPoint], ['', '.'], $source);
}
if (!is_numeric($source)) {
return new \TYPO3\CMS\Extbase\Error\Error('"%s" cannot be converted to a float value.', 1332934124, [$source]);
}
return (float)$source;
}
}
@@ -0,0 +1,47 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms simple types to \TYPO3\CMS\Extbase\Domain\Model\FileCollection.
*
* @internal experimental! This class is experimental and subject to change!
*/
class FolderBasedFileCollectionConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractFileCollectionConverter
{
/**
* @var array<string>
*/
protected $sourceTypes = ['integer'];
/**
* @var string
*/
protected $targetType = \TYPO3\CMS\Extbase\Domain\Model\FolderBasedFileCollection::class;
/**
* @var string
*/
protected $expectedObjectType = \TYPO3\CMS\Core\Resource\Collection\FolderBasedFileCollection::class;
/**
* @param int $source
* @return \TYPO3\CMS\Core\Resource\Collection\FolderBasedFileCollection
*/
protected function getObject($source)
{
return $this->fileFactory->getCollectionObject($source);
}
}
@@ -0,0 +1,47 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms simple types to \TYPO3\CMS\Extbase\Domain\Model\Folder.
*
* @internal experimental! This class is experimental and subject to change!
*/
class FolderConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractFileFolderConverter
{
/**
* @var array<string>
*/
protected $sourceTypes = ['string'];
/**
* @var string
*/
protected $targetType = \TYPO3\CMS\Extbase\Domain\Model\Folder::class;
/**
* @var string
*/
protected $expectedObjectType = \TYPO3\CMS\Core\Resource\Folder::class;
/**
* @param string $source
* @return \TYPO3\CMS\Core\Resource\Folder
*/
protected function getOriginalResource($source)
{
return $this->fileFactory->getFolderObjectFromCombinedIdentifier($source);
}
}
@@ -0,0 +1,56 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms a simple type to an integer, by simply casting it.
*/
class IntegerConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
{
/**
* @var array<string>
*/
protected $sourceTypes = ['integer', 'string'];
/**
* @var string
*/
protected $targetType = 'integer';
/**
* @var int
*/
protected $priority = 10;
/**
* Actually convert from $source to $targetType, in fact a noop here.
*
* @param int|string $source
* @param string $targetType
* @param array $convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return int|\TYPO3\CMS\Extbase\Error\Error
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
if ($source === null || (string)$source === '') {
return null;
}
if (!is_numeric($source)) {
return new \TYPO3\CMS\Extbase\Error\Error('"%s" is no integer.', 1332933658, [$source]);
}
return (int)$source;
}
}
@@ -0,0 +1,254 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* This converter transforms arrays to simple objects (POPO) by setting properties.
*/
class ObjectConverter extends AbstractTypeConverter
{
/**
* @var int
*/
const CONFIGURATION_TARGET_TYPE = 3;
/**
* @var int
*/
const CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED = 4;
/**
* @var array
*/
protected $sourceTypes = ['array'];
/**
* @var string
*/
protected $targetType = 'object';
/**
* @var int
*/
protected $priority = 10;
/**
* @var \TYPO3\CMS\Extbase\Object\Container\Container
*/
protected $objectContainer;
/**
* @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
*/
protected $reflectionService;
/**
* @param \TYPO3\CMS\Extbase\Object\Container\Container $objectContainer
*/
public function injectObjectContainer(\TYPO3\CMS\Extbase\Object\Container\Container $objectContainer)
{
$this->objectContainer = $objectContainer;
}
/**
* @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
*/
public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
{
$this->reflectionService = $reflectionService;
}
/**
* Only convert non-persistent types
*
* @param mixed $source
* @param string $targetType
* @return bool
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function canConvertFrom($source, $targetType)
{
return !is_subclass_of($targetType, \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject::class);
}
/**
* Convert all properties in the source array
*
* @param mixed $source
* @return array
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
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
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function getTypeOfChildProperty($targetType, $propertyName, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration)
{
$configuredTargetType = $configuration->getConfigurationFor($propertyName)->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter::class, self::CONFIGURATION_TARGET_TYPE);
if ($configuredTargetType !== null) {
return $configuredTargetType;
}
$specificTargetType = $this->objectContainer->getImplementationClassName($targetType);
$classSchema = $this->reflectionService->getClassSchema($specificTargetType);
if ($classSchema->hasMethod(\TYPO3\CMS\Extbase\Reflection\ObjectAccess::buildSetterMethodName($propertyName))) {
$methodParameters = $classSchema->getMethod(\TYPO3\CMS\Extbase\Reflection\ObjectAccess::buildSetterMethodName($propertyName))['params'] ?? [];
$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 "' . $specificTargetType . '".', 1303379158);
}
return $methodParameter['type'];
}
$method = $classSchema->getMethod('__construct');
if (empty($method)) {
$exceptionMessage = sprintf('Type of child property "%s" of class "%s" could not be '
. 'derived from constructor arguments as said class does not have a constructor '
. 'defined.', $propertyName, $specificTargetType);
throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException($exceptionMessage, 1582385098);
}
$methodParameters = $classSchema->getMethod('__construct')['params'] ?? [];
if (!isset($methodParameters[$propertyName])) {
$exceptionMessage = sprintf('Type of child property "%1$s" of class "%2$s" could not be '
. 'derived from constructor arguments as the constructor of said class does not '
. 'have a parameter with property name "%1$s".', $propertyName, $specificTargetType);
throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException($exceptionMessage, 1303379126);
}
$parameterType = $methodParameters[$propertyName]['type'];
if ($parameterType === null) {
$exceptionMessage = sprintf('Type of child property "%1$s" of class "%2$s" could not be '
. 'derived from constructor argument "%1$s". This usually happens if the argument '
. 'misses a type hint.', $propertyName, $specificTargetType);
throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException($exceptionMessage, 1582385619);
}
return $parameterType;
}
/**
* 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
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \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
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
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::class, 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);
}
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);
}
}
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)
{
$specificObjectType = $this->objectContainer->getImplementationClassName($objectType);
$classSchema = $this->reflectionService->getClassSchema($specificObjectType);
if ($classSchema->hasConstructor()) {
$constructorSignature = $classSchema->getMethod('__construct')['params'] ?? [];
$constructorArguments = [];
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([$this->objectManager, 'get'], array_merge([$objectType], $constructorArguments));
}
return $this->objectManager->get($objectType);
}
}
@@ -0,0 +1,85 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms simple types to an ObjectStorage.
*
* @todo Implement functionality for converting collection properties.
*/
class ObjectStorageConverter extends AbstractTypeConverter
{
/**
* @var array<string>
*/
protected $sourceTypes = ['string', 'array'];
/**
* @var string
*/
protected $targetType = \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class;
/**
* @var int
*/
protected $priority = 10;
/**
* Actually convert from $source to $targetType, taking into account the fully
* built $convertedChildProperties and $configuration.
*
* @param mixed $source
* @param string $targetType
* @param array $convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
$objectStorage = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
foreach ($convertedChildProperties as $subProperty) {
$objectStorage->attach($subProperty);
}
return $objectStorage;
}
/**
* Returns the source, if it is an array, otherwise an empty array.
*
* @param mixed $source
* @return array
*/
public function getSourceChildPropertiesToBeConverted($source)
{
if (is_array($source)) {
return $source;
}
return [];
}
/**
* Return the type of a given sub-property inside the $targetType
*
* @param string $targetType
* @param string $propertyName
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return string
*/
public function getTypeOfChildProperty($targetType, $propertyName, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration)
{
$parsedTargetType = \TYPO3\CMS\Extbase\Utility\TypeHandlingUtility::parseType($targetType);
return $parsedTargetType['elementType'];
}
}
@@ -0,0 +1,228 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* This converter transforms arrays or strings to persistent objects. It does the following:
*
* - If the input is string, it is assumed to be a UID. Then, the object is fetched from persistence.
* - If the input is array, we check if it has an identity property.
*
* - If the input has an identity property and NO additional properties, we fetch the object from persistence.
* - If the input has an identity property AND additional properties, we fetch the object from persistence,
* and set the sub-properties. We only do this if the configuration option "CONFIGURATION_MODIFICATION_ALLOWED" is TRUE.
* - If the input has NO identity property, but additional properties, we create a new object and return it.
* However, we only do this if the configuration option "CONFIGURATION_CREATION_ALLOWED" is TRUE.
*/
class PersistentObjectConverter extends ObjectConverter
{
/**
* @var int
*/
const CONFIGURATION_MODIFICATION_ALLOWED = 1;
/**
* @var int
*/
const CONFIGURATION_CREATION_ALLOWED = 2;
/**
* @var array
*/
protected $sourceTypes = ['integer', 'string', 'array'];
/**
* @var string
*/
protected $targetType = 'object';
/**
* @var int
*/
protected $priority = 20;
/**
* @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
*/
protected $persistenceManager;
/**
* @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function injectPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager)
{
$this->persistenceManager = $persistenceManager;
}
/**
* We can only convert if the $targetType is either tagged with entity or value object.
*
* @param mixed $source
* @param string $targetType
* @return bool
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function canConvertFrom($source, $targetType)
{
return is_subclass_of($targetType, \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject::class);
}
/**
* All properties in the source array except __identity are sub-properties.
*
* @param mixed $source
* @return array
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function getSourceChildPropertiesToBeConverted($source)
{
if (is_string($source) || is_int($source)) {
return [];
}
if (isset($source['__identity'])) {
unset($source['__identity']);
}
return parent::getSourceChildPropertiesToBeConverted($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
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function getTypeOfChildProperty($targetType, $propertyName, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration)
{
$configuredTargetType = $configuration->getConfigurationFor($propertyName)->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, self::CONFIGURATION_TARGET_TYPE);
if ($configuredTargetType !== null) {
return $configuredTargetType;
}
$specificTargetType = $this->objectContainer->getImplementationClassName($targetType);
$schema = $this->reflectionService->getClassSchema($specificTargetType);
if (!$schema->hasProperty($propertyName)) {
throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException('Property "' . $propertyName . '" was not found in target object of type "' . $specificTargetType . '".', 1297978366);
}
$propertyInformation = $schema->getProperty($propertyName);
return $propertyInformation['type'] . ($propertyInformation['elementType'] !== null ? '<' . $propertyInformation['elementType'] . '>' : '');
}
/**
* Convert an object from $source to an entity or a value object.
*
* @param mixed $source
* @param string $targetType
* @param array $convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @throws \InvalidArgumentException
* @return object the target type
* @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
if (is_array($source)) {
if (
class_exists($targetType)
&& is_subclass_of($targetType, \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject::class)
) {
// Unset identity for valueobject to use constructor mapping, since the identity is determined from
// constructor arguments
unset($source['__identity']);
}
$object = $this->handleArrayData($source, $targetType, $convertedChildProperties, $configuration);
} elseif (is_string($source) || is_int($source)) {
if (empty($source)) {
return null;
}
$object = $this->fetchObjectFromPersistence($source, $targetType);
} else {
throw new \InvalidArgumentException('Only integers, strings and arrays are accepted.', 1305630314);
}
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, 1297935345);
}
}
return $object;
}
/**
* Handle the case if $source is an array.
*
* @param array $source
* @param string $targetType
* @param array &$convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @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'])) {
$object = $this->fetchObjectFromPersistence($source['__identity'], $targetType);
if (count($source) > 1 && ($configuration === null || $configuration->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, self::CONFIGURATION_MODIFICATION_ALLOWED) !== true)) {
throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException('Modification of persistent objects not allowed. To enable this, you need to set the PropertyMappingConfiguration Value "CONFIGURATION_MODIFICATION_ALLOWED" to TRUE.', 1297932028);
}
} else {
if ($configuration === null || $configuration->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, self::CONFIGURATION_CREATION_ALLOWED) !== true) {
throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException(
'Creation of objects not allowed. To enable this, you need to set the PropertyMappingConfiguration Value "CONFIGURATION_CREATION_ALLOWED" to TRUE',
1476044961
);
}
$object = $this->buildObject($convertedChildProperties, $targetType);
}
return $object;
}
/**
* Fetch an object from persistence layer.
*
* @param mixed $identity
* @param string $targetType
* @throws \TYPO3\CMS\Extbase\Property\Exception\TargetNotFoundException
* @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidSourceException
* @return object
*/
protected function fetchObjectFromPersistence($identity, $targetType)
{
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);
}
if ($object === null) {
throw new \TYPO3\CMS\Extbase\Property\Exception\TargetNotFoundException(sprintf('Object of type %s with identity "%s" not found.', $targetType, print_r($identity, true)), 1297933823);
}
return $object;
}
}
@@ -0,0 +1,47 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms simple types to \TYPO3\CMS\Extbase\Domain\Model\FileCollection.
*
* @internal experimental! This class is experimental and subject to change!
*/
class StaticFileCollectionConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractFileCollectionConverter
{
/**
* @var array<string>
*/
protected $sourceTypes = ['integer'];
/**
* @var string
*/
protected $targetType = \TYPO3\CMS\Extbase\Domain\Model\StaticFileCollection::class;
/**
* @var string
*/
protected $expectedObjectType = \TYPO3\CMS\Core\Resource\Collection\StaticFileCollection::class;
/**
* @param int $source
* @return \TYPO3\CMS\Core\Resource\Collection\StaticFileCollection
*/
protected function getObject($source)
{
return $this->fileFactory->getCollectionObject($source);
}
}
@@ -0,0 +1,51 @@
<?php
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Converter which transforms simple types to a string.
*/
class StringConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
{
/**
* @var array<string>
*/
protected $sourceTypes = ['string', 'integer'];
/**
* @var string
*/
protected $targetType = 'string';
/**
* @var int
*/
protected $priority = 10;
/**
* Actually convert from $source to $targetType, taking into account the fully
* built $convertedChildProperties and $configuration.
*
* @param string $source
* @param string $targetType
* @param array $convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return string
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
{
return (string)$source;
}
}
@@ -0,0 +1,105 @@
<?php
namespace TYPO3\CMS\Extbase\Property;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Interface for type converters, which can convert from a simple type to an object or another simple type.
*
* All Type Converters should have NO INTERNAL STATE, such that they can be used as singletons and multiple times in succession (as this improves performance dramatically).
*/
interface TypeConverterInterface
{
/**
* Returns the list of source types the TypeConverter can handle.
* Must be PHP simple types, classes or object is not allowed.
*
* @return array<string>
*/
public function getSupportedSourceTypes();
/**
* Return the target type this TypeConverter converts to.
* Can be a simple type or a class name.
*
* @return string
*/
public function getSupportedTargetType();
/**
* Returns the type for a given source, depending on e.g. the __type setting or other properties.
*
* @param mixed $source the source data
* @param string $originalTargetType the type we originally want to convert to
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return string
*/
public function getTargetTypeForSource($source, $originalTargetType, PropertyMappingConfigurationInterface $configuration = null);
/**
* Return the priority of this TypeConverter. TypeConverters with a high priority are chosen before low priority.
*
* @return int
*/
public function getPriority();
/**
* Here, the TypeConverter can do some additional runtime checks to see whether
* it can handle the given source data and the given target type.
*
* @param mixed $source the source data
* @param string $targetType the type to convert to.
* @return bool TRUE if this TypeConverter can convert from $source to $targetType, FALSE otherwise.
*/
public function canConvertFrom($source, $targetType);
/**
* Return a list of sub-properties inside the source object.
* The "key" is the sub-property name, and the "value" is the value of the sub-property.
*
* @param mixed $source
* @return array<mixed>
*/
public function getSourceChildPropertiesToBeConverted($source);
/**
* Return the type of a given sub-property inside the $targetType
*
* @param string $targetType
* @param string $propertyName
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return string the type of $propertyName in $targetType
*/
public function getTypeOfChildProperty($targetType, $propertyName, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration);
/**
* Actually convert from $source to $targetType, taking into account the fully
* built $convertedChildProperties and $configuration.
*
* The return value can be one of three types:
* - an arbitrary object, or a simple type (which has been created while mapping).
* This is the normal case.
* - NULL, indicating that this object should *not* be mapped (i.e. a "File Upload" Converter could return NULL if no file has been uploaded, and a silent failure should occur.
* - An instance of \TYPO3\CMS\Extbase\Error\Error -- This will be a user-visible error message later on.
* Furthermore, it should throw an Exception if an unexpected failure (like a security error) occurred or a configuration issue happened.
*
* @param mixed $source
* @param string $targetType
* @param array $convertedChildProperties
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return mixed|\TYPO3\CMS\Extbase\Error\Error the target type, or an error object if a user-error occurred
* @throws \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException thrown in case a developer error occurred
*/
public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null);
}