TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||
use TYPO3\CMS\Core\Resource\ResourceInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Domain\Model\File;
|
||||
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
|
||||
use TYPO3\CMS\Extbase\Domain\Model\Folder;
|
||||
use TYPO3\CMS\Extbase\Property\Exception;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
|
||||
/**
|
||||
* Converter which transforms simple types to \TYPO3\CMS\Extbase\Domain\Model\File.
|
||||
*
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
abstract class AbstractFileFolderConverter extends AbstractTypeConverter
|
||||
{
|
||||
protected string $expectedObjectType;
|
||||
|
||||
protected ResourceFactory $fileFactory;
|
||||
|
||||
public function injectFileFactory(ResourceFactory $fileFactory): void
|
||||
{
|
||||
$this->fileFactory = $fileFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually convert from $source to $targetType, taking into account the fully
|
||||
* built $convertedChildProperties and $configuration.
|
||||
*
|
||||
* @param string|int $source
|
||||
* @throws Exception
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): File|FileReference|Folder {
|
||||
$object = $this->getOriginalResource($source);
|
||||
if (empty($this->expectedObjectType) || !$object instanceof $this->expectedObjectType) {
|
||||
throw new Exception('Expected object of type "' . $this->expectedObjectType . '" but got ' . (is_object($object) ? get_class($object) : 'null'), 1342895975);
|
||||
}
|
||||
/** @var File|FileReference|Folder $subject */
|
||||
$subject = GeneralUtility::makeInstance($targetType);
|
||||
$subject->setOriginalResource($object);
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $source
|
||||
*/
|
||||
abstract protected function getOriginalResource($source): ?ResourceInterface;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
use TYPO3\CMS\Extbase\Property\TypeConverterInterface;
|
||||
|
||||
/**
|
||||
* Type converter which provides sensible default implementations for most methods. If you extend this class
|
||||
* you only need to implement convertFrom()
|
||||
*/
|
||||
abstract class AbstractTypeConverter implements TypeConverterInterface, SingletonInterface
|
||||
{
|
||||
/**
|
||||
* @todo The concept of this method is flawed because it enables the override of the target type depending on the
|
||||
* structure of the source. So, technically we no longer convert type A to B but source of type A with
|
||||
* structure X to type B defined by X. This makes a type converter non-deterministic.
|
||||
*
|
||||
* Returns the $originalTargetType unchanged in this implementation.
|
||||
*
|
||||
* @param mixed $source the source data
|
||||
* @param string $originalTargetType the type we originally want to convert to
|
||||
*/
|
||||
public function getTargetTypeForSource($source, string $originalTargetType, ?PropertyMappingConfigurationInterface $configuration = null): string
|
||||
{
|
||||
return $originalTargetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo this method is only used for converter sources that have children (i.e. objects). Introduce another
|
||||
* ChildPropertyAwareTypeConverterInterface and drop this method from the main interface
|
||||
*
|
||||
* Returns an empty list of sub property names
|
||||
*
|
||||
* @param mixed $source
|
||||
*/
|
||||
public function getSourceChildPropertiesToBeConverted($source): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo this method is only used for converter sources that have children (i.e. objects). Introduce another
|
||||
* ChildPropertyAwareTypeConverterInterface and drop this method from the main interface
|
||||
*
|
||||
* This method is never called, as getSourceChildPropertiesToBeConverted() returns an empty array.
|
||||
*
|
||||
* @param string $targetType
|
||||
* @param string $propertyName
|
||||
*/
|
||||
public function getTypeOfChildProperty(string $targetType, string $propertyName, PropertyMappingConfigurationInterface $configuration): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Property\Exception\TypeConverterException;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
|
||||
/**
|
||||
* Converter which transforms strings/arrays to arrays.
|
||||
*/
|
||||
class ArrayConverter extends AbstractTypeConverter
|
||||
{
|
||||
public const CONFIGURATION_DELIMITER = 'delimiter';
|
||||
public const CONFIGURATION_REMOVE_EMPTY_VALUES = 'removeEmptyValues';
|
||||
public const CONFIGURATION_LIMIT = 'limit';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* If the type converter has a configuration, it can convert non-empty strings, too
|
||||
*
|
||||
* @param string|array $source
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): array|string {
|
||||
if (!is_string($source)) {
|
||||
return $source;
|
||||
}
|
||||
if ($source === '') {
|
||||
return [];
|
||||
}
|
||||
if ($configuration === null) {
|
||||
return $source;
|
||||
}
|
||||
$delimiter = $configuration->getConfigurationValue(self::class, self::CONFIGURATION_DELIMITER);
|
||||
$removeEmptyValues = $configuration->getConfigurationValue(self::class, self::CONFIGURATION_REMOVE_EMPTY_VALUES) ?? false;
|
||||
$limit = $configuration->getConfigurationValue(self::class, self::CONFIGURATION_LIMIT) ?? 0;
|
||||
if (!is_string($delimiter)) {
|
||||
throw new TypeConverterException('No delimiter configured for ' . self::class . ' and non-empty value given.', 1582877555);
|
||||
}
|
||||
|
||||
return GeneralUtility::trimExplode($delimiter, $source, $removeEmptyValues, $limit);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
|
||||
/**
|
||||
* Converter which transforms simple types to a boolean, by simply casting it.
|
||||
*/
|
||||
class BooleanConverter extends AbstractTypeConverter
|
||||
{
|
||||
/**
|
||||
* Actually convert from $source to $targetType
|
||||
*
|
||||
* @param mixed $source
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): bool {
|
||||
return (bool)$source;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Core\Type\Exception\InvalidValueExceptionInterface;
|
||||
use TYPO3\CMS\Extbase\Error\Error;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
|
||||
/**
|
||||
* Converter which transforms simple types to a core type
|
||||
* implementing \TYPO3\CMS\Core\Type\TypeInterface.
|
||||
*/
|
||||
class CoreTypeConverter extends AbstractTypeConverter
|
||||
{
|
||||
/**
|
||||
* Convert an object from $source to an Enumeration.
|
||||
*
|
||||
* @param mixed $source
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): object {
|
||||
try {
|
||||
return new $targetType($source);
|
||||
} catch (InvalidValueExceptionInterface $exception) {
|
||||
return new Error($exception->getMessage(), 1381680012);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Core\Country\Country;
|
||||
use TYPO3\CMS\Core\Country\CountryProvider;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
|
||||
/**
|
||||
* Converter which transforms simple types to a country object.
|
||||
*/
|
||||
class CountryConverter extends AbstractTypeConverter
|
||||
{
|
||||
public const CONFIGURATION_FROM = 'alpha2IsoCode';
|
||||
|
||||
protected CountryProvider $countryProvider;
|
||||
|
||||
public function injectCountryProvider(CountryProvider $countryProvider): void
|
||||
{
|
||||
$this->countryProvider = $countryProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually convert from $source to $targetType, taking into account the fully
|
||||
* built $convertedChildProperties and $configuration.
|
||||
*
|
||||
* @param string $source
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): ?Country {
|
||||
|
||||
$by = self::CONFIGURATION_FROM;
|
||||
if ($configuration !== null) {
|
||||
$by = $configuration->getConfigurationValue(CountryConverter::class, self::CONFIGURATION_FROM);
|
||||
}
|
||||
return match ($by) {
|
||||
'alpha3IsoCode' => $this->countryProvider->getByAlpha3IsoCode((string)$source),
|
||||
default => $this->countryProvider->getByAlpha2IsoCode((string)$source),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Extbase\Error\Error;
|
||||
use TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException;
|
||||
use TYPO3\CMS\Extbase\Property\Exception\TypeConverterException;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
|
||||
/**
|
||||
* 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 AbstractTypeConverter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public 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
|
||||
*/
|
||||
public const DEFAULT_DATE_FORMAT = \DateTimeInterface::W3C;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws TypeConverterException
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): \DateTime|Error|null {
|
||||
$dateFormat = $this->getDefaultDateFormat($configuration);
|
||||
if (is_string($source)) {
|
||||
$dateAsString = $source;
|
||||
} elseif (is_int($source)) {
|
||||
$dateAsString = (string)$source;
|
||||
} else {
|
||||
if (isset($source['date']) && is_string($source['date'])) {
|
||||
$dateAsString = $source['date'];
|
||||
} elseif (isset($source['date']) && is_int($source['date'])) {
|
||||
$dateAsString = (string)$source['date'];
|
||||
} elseif ($this->isDatePartKeysProvided($source)) {
|
||||
if ($source['day'] < 1 || $source['month'] < 1 || $source['year'] < 1) {
|
||||
return new 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 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']))) {
|
||||
// todo: type converters are never called without a property mapping configuration
|
||||
$dateFormat = 'U';
|
||||
}
|
||||
if (is_array($source) && isset($source['timezone']) && (string)$source['timezone'] !== '') {
|
||||
try {
|
||||
$timezone = new \DateTimeZone($source['timezone']);
|
||||
} catch (\Exception $e) {
|
||||
throw new 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(
|
||||
$this->translateErrorMessage(
|
||||
'LLL:EXT:extbase/Resources/Private/Language/locallang.xlf:converter.datetime.notrecognized',
|
||||
),
|
||||
1307719788,
|
||||
[$dateAsString, $dateFormat]
|
||||
);
|
||||
}
|
||||
if (is_array($source)) {
|
||||
$date = $this->overrideTimeIfSpecified($date, $source);
|
||||
}
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap static call to LocalizationUtility to simplify unit testing.
|
||||
*/
|
||||
protected function translateErrorMessage(string $translateKey): string
|
||||
{
|
||||
return LocalizationUtility::translate($translateKey) ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether date information (day, month, year) are present as keys in $source.
|
||||
*/
|
||||
protected function isDatePartKeysProvided(array $source): bool
|
||||
{
|
||||
return isset($source['day'], $source['month'], $source['year'])
|
||||
&& ctype_digit($source['day']) && ctype_digit($source['month']) && 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.
|
||||
*
|
||||
* @throws InvalidPropertyMappingConfigurationException
|
||||
*/
|
||||
protected function getDefaultDateFormat(?PropertyMappingConfigurationInterface $configuration = null): string
|
||||
{
|
||||
if ($configuration === null) {
|
||||
// todo: type converters are never called without a property mapping configuration
|
||||
return self::DEFAULT_DATE_FORMAT;
|
||||
}
|
||||
$dateFormat = $configuration->getConfigurationValue(DateTimeConverter::class, self::CONFIGURATION_DATE_FORMAT);
|
||||
if ($dateFormat === null) {
|
||||
return self::DEFAULT_DATE_FORMAT;
|
||||
}
|
||||
if (!is_string($dateFormat)) {
|
||||
throw new InvalidPropertyMappingConfigurationException('CONFIGURATION_DATE_FORMAT must be of type string, "' . get_debug_type($dateFormat) . '" given', 1307719569);
|
||||
}
|
||||
return $dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides hour, minute & second of the given date with the values in the $source array
|
||||
*/
|
||||
protected function overrideTimeIfSpecified(\DateTime $date, array $source): \DateTime
|
||||
{
|
||||
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,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
use UnitEnum;
|
||||
|
||||
/**
|
||||
* Converter which transforms strings/integers/floats to Enum Instance.
|
||||
*/
|
||||
class EnumConverter extends AbstractTypeConverter
|
||||
{
|
||||
/**
|
||||
* Convert an enum from $source to an enum.
|
||||
*
|
||||
* @template T of UnitEnum
|
||||
* @param class-string<T> $targetType
|
||||
* @return T|null
|
||||
* @throws InvalidTargetException
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
mixed $source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): ?\UnitEnum {
|
||||
return $this->getEnumElement($source, $targetType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T of UnitEnum
|
||||
* @param class-string<T> $targetType
|
||||
* @return T|null
|
||||
* @throws InvalidTargetException
|
||||
*/
|
||||
protected function getEnumElement(float|int|string $source, string $targetType): ?\UnitEnum
|
||||
{
|
||||
if (!enum_exists($targetType)) {
|
||||
throw new InvalidTargetException('TargetType "' . $targetType . '" is not an enum.', 1660834545);
|
||||
}
|
||||
foreach ($targetType::cases() as $enum) {
|
||||
if (property_exists($enum, 'value') && $enum->value == $source) {
|
||||
return $enum;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($targetType::cases() as $enum) {
|
||||
if ($enum->name == $source) {
|
||||
return $enum;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\Resource\ResourceInterface;
|
||||
|
||||
/**
|
||||
* Converter which transforms simple types to \TYPO3\CMS\Extbase\Domain\Model\File.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FileConverter extends AbstractFileFolderConverter
|
||||
{
|
||||
protected string $expectedObjectType = File::class;
|
||||
|
||||
/**
|
||||
* @param string|int $source
|
||||
* @throws ResourceDoesNotExistException
|
||||
*/
|
||||
protected function getOriginalResource($source): ?ResourceInterface
|
||||
{
|
||||
return $this->fileFactory->retrieveFileOrFolderObject($source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Core\Resource\FileReference;
|
||||
use TYPO3\CMS\Core\Resource\ResourceInterface;
|
||||
|
||||
/**
|
||||
* Converter which transforms simple types to \TYPO3\CMS\Extbase\Domain\Model\FileReference.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FileReferenceConverter extends AbstractFileFolderConverter
|
||||
{
|
||||
protected string $expectedObjectType = FileReference::class;
|
||||
|
||||
/**
|
||||
* @param string|int $source
|
||||
*/
|
||||
protected function getOriginalResource($source): ?ResourceInterface
|
||||
{
|
||||
return $this->fileFactory->getFileReferenceObject($source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Extbase\Error\Error;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
|
||||
/**
|
||||
* Converter which transforms a simple type to a float.
|
||||
*
|
||||
* This is basically done by simply casting it.
|
||||
*/
|
||||
class FloatConverter extends AbstractTypeConverter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const CONFIGURATION_THOUSANDS_SEPARATOR = 'thousandsSeparator';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const CONFIGURATION_DECIMAL_POINT = 'decimalPoint';
|
||||
|
||||
/**
|
||||
* Actually convert from $source to $targetType, by doing a typecast.
|
||||
*
|
||||
* @param mixed $source
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): float|Error|null {
|
||||
if ($source === null || (string)$source === '') {
|
||||
return null;
|
||||
}
|
||||
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 Error('"%s" cannot be converted to a float value.', 1332934124, [$source]);
|
||||
}
|
||||
return (float)$source;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Core\Resource\Folder;
|
||||
use TYPO3\CMS\Core\Resource\ResourceInterface;
|
||||
|
||||
/**
|
||||
* Converter which transforms simple types to \TYPO3\CMS\Extbase\Domain\Model\Folder.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FolderConverter extends AbstractFileFolderConverter
|
||||
{
|
||||
protected string $expectedObjectType = Folder::class;
|
||||
|
||||
/**
|
||||
* @param string $source
|
||||
*/
|
||||
protected function getOriginalResource($source): ?ResourceInterface
|
||||
{
|
||||
return $this->fileFactory->getFolderObjectFromCombinedIdentifier($source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Extbase\Error\Error;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
|
||||
/**
|
||||
* Converter which transforms a simple type to an integer, by simply casting it.
|
||||
*/
|
||||
class IntegerConverter extends AbstractTypeConverter
|
||||
{
|
||||
/**
|
||||
* Actually convert from $source to $targetType, in fact a noop here.
|
||||
*
|
||||
* @param int|string|null $source
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): int|Error|null {
|
||||
if ($source === null || (string)$source === '') {
|
||||
return null;
|
||||
}
|
||||
if (!is_numeric($source)) {
|
||||
return new Error('"%s" is no integer.', 1332933658, [$source]);
|
||||
}
|
||||
return (int)$source;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Property\Exception\InvalidDataTypeException;
|
||||
use TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException;
|
||||
use TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
use TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchMethodException;
|
||||
use TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchMethodParameterException;
|
||||
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
|
||||
use TYPO3\CMS\Extbase\Reflection\ReflectionService;
|
||||
|
||||
/**
|
||||
* This converter transforms arrays to simple objects (POPO) by setting properties.
|
||||
*/
|
||||
class ObjectConverter extends AbstractTypeConverter
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public const CONFIGURATION_TARGET_TYPE = 3;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public const CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED = 4;
|
||||
|
||||
protected ContainerInterface $container;
|
||||
|
||||
protected ReflectionService $reflectionService;
|
||||
|
||||
public function injectReflectionService(ReflectionService $reflectionService): void
|
||||
{
|
||||
$this->reflectionService = $reflectionService;
|
||||
}
|
||||
|
||||
public function injectContainer(ContainerInterface $container): void
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all properties in the source array
|
||||
*
|
||||
* @param mixed $source
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function getSourceChildPropertiesToBeConverted($source): array
|
||||
{
|
||||
if (isset($source['__type'])) {
|
||||
unset($source['__type']);
|
||||
}
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of a property is determined by the reflection service.
|
||||
*
|
||||
* @throws InvalidTargetException
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function getTypeOfChildProperty(
|
||||
string $targetType,
|
||||
string $propertyName,
|
||||
PropertyMappingConfigurationInterface $configuration
|
||||
): string {
|
||||
$configuredTargetType = $configuration->getConfigurationFor($propertyName)
|
||||
->getConfigurationValue(ObjectConverter::class, self::CONFIGURATION_TARGET_TYPE);
|
||||
if ($configuredTargetType !== null) {
|
||||
return $configuredTargetType;
|
||||
}
|
||||
|
||||
$classSchema = $this->reflectionService->getClassSchema($targetType);
|
||||
|
||||
// @todo: infer property type from property instead of from setter and make setter optional
|
||||
// {@link https://forge.typo3.org/issues/100136}
|
||||
|
||||
$methodName = 'set' . ucfirst($propertyName);
|
||||
if ($classSchema->hasMethod($methodName)) {
|
||||
$methodParameters = $classSchema->getMethod($methodName)->getParameters();
|
||||
$methodParameter = current($methodParameters);
|
||||
if ($methodParameter->getType() === null) {
|
||||
throw new InvalidTargetException('Setter for property "' . $propertyName . '" had no type hint or documentation in target object of type "' . $targetType . '".', 1303379158);
|
||||
}
|
||||
$property = $classSchema->getProperty($propertyName);
|
||||
$primaryCollectionValueType = $property->getPrimaryCollectionValueType();
|
||||
if ($primaryCollectionValueType) {
|
||||
return $methodParameter->getType() . '<' . ($primaryCollectionValueType->getClassName() ?? $primaryCollectionValueType->getBuiltinType()) . '>';
|
||||
}
|
||||
return $methodParameter->getType();
|
||||
}
|
||||
try {
|
||||
$parameterType = $classSchema->getMethod('__construct')->getParameter($propertyName)->getType();
|
||||
} catch (NoSuchMethodException $e) {
|
||||
$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, $targetType);
|
||||
throw new InvalidTargetException($exceptionMessage, 1582385098);
|
||||
} catch (NoSuchMethodParameterException $e) {
|
||||
$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, $targetType);
|
||||
throw new InvalidTargetException($exceptionMessage, 1303379126);
|
||||
}
|
||||
|
||||
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, $targetType);
|
||||
throw new InvalidTargetException($exceptionMessage, 1582385619);
|
||||
}
|
||||
return $parameterType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an object from $source to an object.
|
||||
*
|
||||
* @param mixed $source
|
||||
* @return object|null the target type
|
||||
* @throws InvalidTargetException
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): ?object {
|
||||
$object = $this->buildObject($convertedChildProperties, $targetType);
|
||||
foreach ($convertedChildProperties as $propertyName => $propertyValue) {
|
||||
$result = 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,
|
||||
get_debug_type($propertyValue),
|
||||
$targetType
|
||||
);
|
||||
throw new InvalidTargetException($exceptionMessage, 1304538165);
|
||||
}
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the target type based on the source's (optional) __type key and by evaluating possible
|
||||
* XCLASS overrides of the target type.
|
||||
*
|
||||
* @param mixed $source
|
||||
* @throws InvalidDataTypeException
|
||||
* @throws InvalidPropertyMappingConfigurationException
|
||||
* @throws \InvalidArgumentException
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function getTargetTypeForSource(
|
||||
$source,
|
||||
string $originalTargetType,
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): string {
|
||||
$targetType = $originalTargetType;
|
||||
|
||||
if (is_array($source) && array_key_exists('__type', $source)) {
|
||||
$targetType = $source['__type'];
|
||||
|
||||
if ($configuration === null) {
|
||||
// todo: this is impossible to achieve since this methods is always called via (convert -> doMapping -> getTargetTypeForSource) and convert and doMapping create configuration objects if missing.
|
||||
throw new \InvalidArgumentException('A property mapping configuration must be given, not NULL.', 1326277369);
|
||||
}
|
||||
if ($configuration->getConfigurationValue(ObjectConverter::class, self::CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED) !== true) {
|
||||
throw new 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 InvalidDataTypeException('The given type "' . $targetType . '" is not a subtype of "' . $originalTargetType . '".', 1317048056);
|
||||
}
|
||||
}
|
||||
|
||||
// Respect XCLASSed object target type
|
||||
return GeneralUtility::getClassName($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: They are considered "handled" by __construct and will
|
||||
* not be mapped calling setters later.
|
||||
*
|
||||
* @return object The created instance
|
||||
* @throws InvalidTargetException if a required constructor argument is missing
|
||||
*/
|
||||
protected function buildObject(array &$possibleConstructorArgumentValues, string $objectType): object
|
||||
{
|
||||
// The ObjectConverter typically kicks in, if request arguments are to be mapped to
|
||||
// a domain model. An example is ext:belog:Domain/Model/Demand.
|
||||
// Domain models are data objects and should thus be fetched via makeInstance(), should
|
||||
// not be registered as service, and should thus not be DI aware.
|
||||
// Additionally, all to-be-mapped arguments are hand over as "possible constructor arguments" here,
|
||||
// and extbase is able to use single arguments as constructor arguments to domain models,
|
||||
// if a __construct() with an argument having the same name as a to-be-mapped argument exists.
|
||||
// This is the reason that &$possibleConstructorArgumentValues is hand over as reference here:
|
||||
// If an argument can be hand over as constructor argument, it is considered "already mapped" and
|
||||
// is not manually mapped calling setters later.
|
||||
// To be as backwards compatible as possible, the following logic is applied:
|
||||
// * If the class is registered as service (container->has()=true), and if there are no
|
||||
// $possibleConstructorArgumentValues, instantiate the class via container->get(). Easy
|
||||
// scenario - the target class is DI aware and will get dependencies injected. A different target
|
||||
// class can be specified using service configuration if needed.
|
||||
// * If the class is registered as service, and if there are $possibleConstructorArgumentValues,
|
||||
// the class is instantiated via container->get(). $possibleConstructorArgumentValues are *not* hand
|
||||
// over to the constructor. The target class can then use constructor injection and inject* methods
|
||||
// for DI. A different target class can be specified using service configuration if needed. Mapping
|
||||
// of arguments is done using setters by follow-up code.
|
||||
// * If the class is *not* registered as service, makeInstance() is used for object retrieval.
|
||||
// * If there are no $possibleConstructorArgumentValues, makeInstance() is used right away.
|
||||
// * If there are $possibleConstructorArgumentValues and __construct() does not exist, makeInstance()
|
||||
// is used without constructor arguments. Mapping of argument values via setters is done by follow-up code.
|
||||
// * If there are $possibleConstructorArgumentValues and if __construct() exists, extbase reflection
|
||||
// is used to map single arguments to constructor arguments with the same name and
|
||||
// makeInstance() is used to instantiate the class. Mapping remaining arguments is done by follow-up code.
|
||||
if ($this->container->has($objectType)) {
|
||||
// @todo: consider dropping container->get() to prevent domain models being treated as services in >=v12.
|
||||
return $this->container->get($objectType);
|
||||
}
|
||||
|
||||
if (empty($possibleConstructorArgumentValues) || !method_exists($objectType, '__construct')) {
|
||||
return GeneralUtility::makeInstance($objectType);
|
||||
}
|
||||
|
||||
$classSchema = $this->reflectionService->getClassSchema($objectType);
|
||||
$constructor = $classSchema->getMethod('__construct');
|
||||
$constructorArguments = [];
|
||||
foreach ($constructor->getParameters() as $parameterName => $parameter) {
|
||||
if (array_key_exists($parameterName, $possibleConstructorArgumentValues)) {
|
||||
$constructorArguments[] = $possibleConstructorArgumentValues[$parameterName];
|
||||
unset($possibleConstructorArgumentValues[$parameterName]);
|
||||
} elseif ($parameter->isOptional()) {
|
||||
$constructorArguments[] = $parameter->getDefaultValue();
|
||||
} else {
|
||||
throw new InvalidTargetException('Missing constructor argument "' . $parameterName . '" for object of type "' . $objectType . '".', 1268734872);
|
||||
}
|
||||
}
|
||||
return GeneralUtility::makeInstance(...[$objectType, ...$constructorArguments]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
|
||||
|
||||
/**
|
||||
* Converter which transforms simple types to an ObjectStorage.
|
||||
*/
|
||||
class ObjectStorageConverter extends AbstractTypeConverter
|
||||
{
|
||||
/**
|
||||
* Actually convert from $source to $targetType, taking into account the fully
|
||||
* built $convertedChildProperties and $configuration.
|
||||
*
|
||||
* @param mixed $source
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): ObjectStorage {
|
||||
$objectStorage = new 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
|
||||
*/
|
||||
public function getSourceChildPropertiesToBeConverted($source): array
|
||||
{
|
||||
if (is_array($source)) {
|
||||
return $source;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of a given sub-property inside the $targetType
|
||||
*
|
||||
* @param string $targetType
|
||||
*/
|
||||
public function getTypeOfChildProperty(
|
||||
$targetType,
|
||||
string $propertyName,
|
||||
PropertyMappingConfigurationInterface $configuration
|
||||
): string {
|
||||
$parsedTargetType = TypeHandlingUtility::parseType($targetType);
|
||||
return $parsedTargetType['elementType'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
|
||||
use TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException;
|
||||
use TYPO3\CMS\Extbase\Property\Exception\InvalidSourceException;
|
||||
use TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException;
|
||||
use TYPO3\CMS\Extbase\Property\Exception\TargetNotFoundException;
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
use TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoPropertyTypesException;
|
||||
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public const CONFIGURATION_MODIFICATION_ALLOWED = 1;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public const CONFIGURATION_CREATION_ALLOWED = 2;
|
||||
|
||||
protected PersistenceManagerInterface $persistenceManager;
|
||||
|
||||
/**
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function injectPersistenceManager(PersistenceManagerInterface $persistenceManager): void
|
||||
{
|
||||
$this->persistenceManager = $persistenceManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* All properties in the source array except __identity are sub-properties.
|
||||
*
|
||||
* @param mixed $source
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function getSourceChildPropertiesToBeConverted($source): array
|
||||
{
|
||||
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
|
||||
* @throws InvalidTargetException
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function getTypeOfChildProperty(
|
||||
$targetType,
|
||||
string $propertyName,
|
||||
PropertyMappingConfigurationInterface $configuration
|
||||
): string {
|
||||
$configuredTargetType = $configuration->getConfigurationFor($propertyName)
|
||||
->getConfigurationValue(PersistentObjectConverter::class, self::CONFIGURATION_TARGET_TYPE);
|
||||
if ($configuredTargetType !== null) {
|
||||
return $configuredTargetType;
|
||||
}
|
||||
|
||||
$schema = $this->reflectionService->getClassSchema($targetType);
|
||||
if (!$schema->hasProperty($propertyName)) {
|
||||
throw new InvalidTargetException('Property "' . $propertyName . '" was not found in target object of type "' . $targetType . '".', 1297978366);
|
||||
}
|
||||
$primaryType = $schema->getProperty($propertyName)->getPrimaryType();
|
||||
if (!$primaryType) {
|
||||
throw NoPropertyTypesException::create($targetType, $propertyName);
|
||||
}
|
||||
|
||||
$type = $primaryType->getClassName() ?? $primaryType->getBuiltinType();
|
||||
if ($primaryType->isCollection() && $primaryType->getCollectionValueTypes() !== []) {
|
||||
$primaryCollectionValueType = $primaryType->getCollectionValueTypes()[0];
|
||||
$collectionValueType = $primaryCollectionValueType->getClassName() ?? $primaryCollectionValueType->getBuiltinType();
|
||||
$type .= '<' . $collectionValueType . '>';
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an object from $source to an entity or a value object.
|
||||
*
|
||||
* @param mixed $source
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws InvalidTargetException
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): ?object {
|
||||
if (is_array($source)) {
|
||||
if (
|
||||
class_exists($targetType)
|
||||
&& is_subclass_of($targetType, 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 {
|
||||
// todo: this case is impossible as this converter is never called with a source that is not an integer, a string or an array
|
||||
throw new \InvalidArgumentException('Only integers, strings and arrays are accepted.', 1305630314);
|
||||
}
|
||||
foreach ($convertedChildProperties as $propertyName => $propertyValue) {
|
||||
$result = 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,
|
||||
get_debug_type($propertyValue),
|
||||
$targetType
|
||||
);
|
||||
throw new InvalidTargetException($exceptionMessage, 1297935345);
|
||||
}
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case if $source is an array.
|
||||
*
|
||||
* @throws InvalidPropertyMappingConfigurationException
|
||||
*/
|
||||
protected function handleArrayData(
|
||||
array $source,
|
||||
string $targetType,
|
||||
array &$convertedChildProperties,
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): object {
|
||||
if (isset($source['__identity'])) {
|
||||
$object = $this->fetchObjectFromPersistence($source['__identity'], $targetType);
|
||||
|
||||
if (count($source) > 1 && ($configuration === null || $configuration->getConfigurationValue(PersistentObjectConverter::class, self::CONFIGURATION_MODIFICATION_ALLOWED) !== true)) {
|
||||
throw new 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(PersistentObjectConverter::class, self::CONFIGURATION_CREATION_ALLOWED) !== true) {
|
||||
throw new 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.
|
||||
*
|
||||
* @throws TargetNotFoundException
|
||||
* @throws InvalidSourceException
|
||||
*/
|
||||
protected function fetchObjectFromPersistence(mixed $identity, string $targetType): object
|
||||
{
|
||||
// @todo - Ideally, this underscore notation should not be passed here.
|
||||
// Consumers of this method should rather earlier resolve to the proper uid
|
||||
// via '$value->getUid' like 'renderHiddenIdentityField' does, for example.
|
||||
// @see #105319
|
||||
if (str_contains((string)$identity, '_')) {
|
||||
$localizedUidParts = explode('_', (string)$identity);
|
||||
$uidIdentity = $localizedUidParts[0];
|
||||
} else {
|
||||
$uidIdentity = (string)$identity;
|
||||
}
|
||||
if (ctype_digit($uidIdentity)) {
|
||||
$object = $this->persistenceManager->getObjectByIdentifier($uidIdentity, $targetType);
|
||||
} else {
|
||||
throw new InvalidSourceException('The identity property "' . $identity . '" is no UID.', 1297931020);
|
||||
}
|
||||
|
||||
if ($object === null) {
|
||||
throw new TargetNotFoundException(sprintf('Object of type %s with identity "%s" not found.', $targetType, print_r($identity, true)), 1297933823);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Extbase\Property\TypeConverter;
|
||||
|
||||
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
|
||||
|
||||
/**
|
||||
* Converter which transforms simple types to a string.
|
||||
*/
|
||||
class StringConverter extends AbstractTypeConverter
|
||||
{
|
||||
/**
|
||||
* Actually convert from $source to $targetType, taking into account the fully
|
||||
* built $convertedChildProperties and $configuration.
|
||||
*
|
||||
* @param string $source
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
public function convertFrom(
|
||||
$source,
|
||||
string $targetType,
|
||||
array $convertedChildProperties = [],
|
||||
?PropertyMappingConfigurationInterface $configuration = null
|
||||
): string {
|
||||
return (string)$source;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user