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,31 @@
<?php
namespace TYPO3\CMS\Extbase\Validation;
/*
* 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 object holds a validation error.
*/
class Error extends \TYPO3\CMS\Extbase\Error\Error
{
/**
* @var string
*/
protected $message = 'Unknown validation error';
/**
* @var string
*/
protected $code = 1201447005;
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Validation;
/*
* 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 generic validation exception
*/
class Exception extends \TYPO3\CMS\Extbase\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\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 TypeHint" Exception
*/
class InvalidTypeHintException extends \TYPO3\CMS\Extbase\Validation\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\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 "InvalidValidationConfiguration" Exception
*/
class InvalidValidationConfigurationException extends \TYPO3\CMS\Extbase\Validation\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\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 "InvalidValidationOptions" Exception
*/
class InvalidValidationOptionsException extends \TYPO3\CMS\Extbase\Validation\Exception
{
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\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 "NoSuchValidator" Exception
*/
class NoSuchValidatorException extends \TYPO3\CMS\Extbase\Validation\Exception
{
}
@@ -0,0 +1,148 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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 abstract composite validator consisting of other validators
*/
abstract class AbstractCompositeValidator implements ObjectValidatorInterface, \Countable
{
/**
* This contains the supported options, their default values and descriptions.
*
* @var array
*/
protected $supportedOptions = [];
/**
* @var array
*/
protected $options = [];
/**
* @var \SplObjectStorage
*/
protected $validators;
/**
* @var \SplObjectStorage
*/
protected $validatedInstancesContainer;
/**
* Constructs the composite validator and sets validation options
*
* @param array $options Options for the validator
* @throws \TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationOptionsException
*/
public function __construct(array $options = [])
{
// check for options given but not supported
if (($unsupportedOptions = array_diff_key($options, $this->supportedOptions)) !== []) {
throw new \TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationOptionsException('Unsupported validation option(s) found: ' . implode(', ', array_keys($unsupportedOptions)), 1339079804);
}
// check for required options being set
array_walk(
$this->supportedOptions,
function ($supportedOptionData, $supportedOptionName, $options) {
if (isset($supportedOptionData[3]) && !array_key_exists($supportedOptionName, $options)) {
throw new \TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationOptionsException('Required validation option not set: ' . $supportedOptionName, 1339163922);
}
},
$options
);
// merge with default values
$this->options = array_merge(
array_map(
function ($value) {
return $value[0];
},
$this->supportedOptions
),
$options
);
$this->validators = new \SplObjectStorage();
}
/**
* Adds a new validator to the conjunction.
*
* @param \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $validator The validator that should be added
*/
public function addValidator(\TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $validator)
{
if ($validator instanceof ObjectValidatorInterface) {
// @todo: provide bugfix as soon as it is fixed in TYPO3.Flow (http://forge.typo3.org/issues/48093)
$validator->setValidatedInstancesContainer = $this->validatedInstancesContainer;
}
$this->validators->attach($validator);
}
/**
* Removes the specified validator.
*
* @param \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $validator The validator to remove
* @throws \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException
*/
public function removeValidator(\TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $validator)
{
if (!$this->validators->contains($validator)) {
throw new \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException('Cannot remove validator because its not in the conjunction.', 1207020177);
}
$this->validators->detach($validator);
}
/**
* Returns the number of validators contained in this conjunction.
*
* @return int The number of validators
*/
public function count()
{
return count($this->validators);
}
/**
* Returns the child validators of this Composite Validator
*
* @return \SplObjectStorage
*/
public function getValidators()
{
return $this->validators;
}
/**
* Returns the options for this validator
*
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* Allows to set a container to keep track of validated instances.
*
* @param \SplObjectStorage $validatedInstancesContainer A container to keep track of validated instances
*/
public function setValidatedInstancesContainer(\SplObjectStorage $validatedInstancesContainer)
{
$this->validatedInstancesContainer = $validatedInstancesContainer;
}
}
@@ -0,0 +1,161 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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\Validation\Exception\InvalidValidationOptionsException;
/**
* Abstract validator
*/
abstract class AbstractValidator implements ValidatorInterface
{
/**
* Specifies whether this validator accepts empty values.
*
* If this is TRUE, the validators isValid() method is not called in case of an empty value
* Note: A value is considered empty if it is NULL or an empty string!
* By default all validators except for NotEmpty and the Composite Validators accept empty values
*
* @var bool
*/
protected $acceptsEmptyValues = true;
/**
* This contains the supported options, their default values, types and descriptions.
*
* @var array
*/
protected $supportedOptions = [];
/**
* @var array
*/
protected $options = [];
/**
* @var \TYPO3\CMS\Extbase\Error\Result
*/
protected $result;
/**
* Constructs the validator and sets validation options
*
* @param array $options Options for the validator
* @throws InvalidValidationOptionsException
*/
public function __construct(array $options = [])
{
// check for options given but not supported
if (($unsupportedOptions = array_diff_key($options, $this->supportedOptions)) !== []) {
throw new InvalidValidationOptionsException('Unsupported validation option(s) found: ' . implode(', ', array_keys($unsupportedOptions)), 1379981890);
}
// check for required options being set
array_walk(
$this->supportedOptions,
function ($supportedOptionData, $supportedOptionName, $options) {
if (isset($supportedOptionData[3]) && $supportedOptionData[3] === true && !array_key_exists($supportedOptionName, $options)) {
throw new InvalidValidationOptionsException('Required validation option not set: ' . $supportedOptionName, 1379981891);
}
},
$options
);
// merge with default values
$this->options = array_merge(
array_map(
function ($value) {
return $value[0];
},
$this->supportedOptions
),
$options
);
}
/**
* Checks if the given value is valid according to the validator, and returns
* the error messages object which occurred.
*
* @param mixed $value The value that should be validated
* @return \TYPO3\CMS\Extbase\Error\Result
*/
public function validate($value)
{
$this->result = new \TYPO3\CMS\Extbase\Error\Result();
if ($this->acceptsEmptyValues === false || $this->isEmpty($value) === false) {
$this->isValid($value);
}
return $this->result;
}
/**
* Check if $value is valid. If it is not valid, needs to add an error
* to result.
*
* @param mixed $value
*/
abstract protected function isValid($value);
/**
* Creates a new validation error object and adds it to $this->results
*
* @param string $message The error message
* @param int $code The error code (a unix timestamp)
* @param array $arguments Arguments to be replaced in message
* @param string $title title of the error
*/
protected function addError($message, $code, array $arguments = [], $title = '')
{
$this->result->addError(new \TYPO3\CMS\Extbase\Validation\Error($message, $code, $arguments, $title));
}
/**
* Returns the options of this validator
*
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* @param mixed $value
* @return bool TRUE if the given $value is NULL or an empty string ('')
*/
final protected function isEmpty($value)
{
return $value === null || $value === '';
}
/**
* Wrap static call to LocalizationUtility to simplify unit testing
*
* @param string $translateKey
* @param string $extensionName
* @param array $arguments
*
* @return string|null
*/
protected function translateErrorMessage($translateKey, $extensionName, $arguments = [])
{
return \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate(
$translateKey,
$extensionName,
$arguments
);
}
}
@@ -0,0 +1,33 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for alphanumeric strings
*/
class AlphanumericValidator extends AbstractValidator
{
/**
* The given $value is valid if it is an alphanumeric string, which is defined as [\pL\d]*.
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
if (!is_string($value) || preg_match('/^[\pL\d]*$/u', $value) !== 1) {
$this->addError($this->translateErrorMessage('validator.alphanumeric.notvalid', 'extbase'), 1221551320);
}
}
}
@@ -0,0 +1,85 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for boolean values
*/
class BooleanValidator extends AbstractValidator
{
/**
* @var array
*/
protected $supportedOptions = [
// The default is set to NULL here, because we need to be backward compatible here, because this
// BooleanValidator is called automatically on boolean action arguments. If we would set it to TRUE,
// every FALSE value for an action argument would break.
// @todo with next patches: deprecate this BooleanValidator and introduce a BooleanValueValidator, like
// in Flow, which won't be called on boolean action arguments.
'is' => [null, 'Boolean value', 'boolean|string|integer']
];
/**
* Check if $value matches the expectation given to the validator.
* If it does not match, the function adds an error to the result.
*
* Also testing for '1' (true), '0' and '' (false) because casting varies between
* tests and actual usage. This makes the validator loose but still keeping functionality.
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
// see comment above, check if expectation is NULL, then nothing to do!
if ($this->options['is'] === null) {
return;
}
switch (strtolower((string)$this->options['is'])) {
case 'true':
case '1':
$expectation = true;
break;
case 'false':
case '':
case '0':
$expectation = false;
break;
default:
$this->addError('The given expectation is not valid.', 1361959227);
return;
}
if ($value !== $expectation) {
if (!is_bool($value)) {
$this->addError($this->translateErrorMessage(
'validator.boolean.nottrue',
'extbase'
), 1361959230);
} else {
if ($expectation) {
$this->addError($this->translateErrorMessage(
'validator.boolean.nottrue',
'extbase'
), 1361959228);
} else {
$this->addError($this->translateErrorMessage(
'validator.boolean.notfalse',
'extbase'
), 1361959229);
}
}
}
}
}
@@ -0,0 +1,100 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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 generic collection validator.
*/
class CollectionValidator extends GenericObjectValidator
{
/**
* @var array
*/
protected $supportedOptions = [
'elementValidator' => [null, 'The validator type to use for the collection elements', 'string'],
'elementType' => [null, 'The type of the elements in the collection', 'string'],
'validationGroups' => [null, 'The validation groups to link to', 'string'],
];
/**
* @var \TYPO3\CMS\Extbase\Validation\ValidatorResolver
*/
protected $validatorResolver;
/**
* @param \TYPO3\CMS\Extbase\Validation\ValidatorResolver $validatorResolver
*/
public function injectValidatorResolver(\TYPO3\CMS\Extbase\Validation\ValidatorResolver $validatorResolver)
{
$this->validatorResolver = $validatorResolver;
}
/**
* Checks if the given value is valid according to the validator, and returns
* the Error Messages object which occurred.
*
* @param mixed $value The value that should be validated
* @return \TYPO3\CMS\Extbase\Error\Result
*/
public function validate($value)
{
$this->result = new \TYPO3\CMS\Extbase\Error\Result();
if ($this->acceptsEmptyValues === false || $this->isEmpty($value) === false) {
if ((is_object($value) && !\TYPO3\CMS\Extbase\Utility\TypeHandlingUtility::isCollectionType(get_class($value))) && !is_array($value)) {
$this->addError('The given subject was not a collection.', 1317204797);
return $this->result;
}
if ($value instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage && !$value->isInitialized()) {
return $this->result;
}
if (is_object($value)) {
if ($this->isValidatedAlready($value)) {
return $this->result;
}
$this->markInstanceAsValidated($value);
}
$this->isValid($value);
}
return $this->result;
}
/**
* Checks for a collection and if needed validates the items in the collection.
* This is done with the specified element validator or a validator based on
* the given element type and validation group.
*
* Either elementValidator or elementType must be given, otherwise validation
* will be skipped.
*
* @param mixed $value A collection to be validated
*/
protected function isValid($value)
{
foreach ($value as $index => $collectionElement) {
if (isset($this->options['elementValidator'])) {
$collectionElementValidator = $this->validatorResolver->createValidator($this->options['elementValidator']);
} elseif (isset($this->options['elementType'])) {
$collectionElementValidator = $this->validatorResolver->getBaseValidatorConjunction($this->options['elementType']);
} else {
return;
}
if ($collectionElementValidator instanceof ObjectValidatorInterface) {
$collectionElementValidator->setValidatedInstancesContainer($this->validatedInstancesContainer);
}
$this->result->forProperty($index)->merge($collectionElementValidator->validate($collectionElement));
}
}
}
@@ -0,0 +1,51 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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\Error\Result;
/**
* Validator to chain many validators in a conjunction (logical and).
*/
class ConjunctionValidator extends AbstractCompositeValidator
{
/**
* Checks if the given value is valid according to the validators of the conjunction.
* Every validator has to be valid, to make the whole conjunction valid.
*
* @param mixed $value The value that should be validated
* @return Result
*/
public function validate($value)
{
$validators = $this->getValidators();
if ($validators->count() > 0) {
/** @var Result $result */
$result = null;
/** @var AbstractValidator $validator */
foreach ($validators as $validator) {
if ($result === null) {
$result = $validator->validate($value);
} else {
$result->merge($validator->validate($value));
}
}
} else {
$result = new Result;
}
return $result;
}
}
@@ -0,0 +1,46 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for DateTime/DateTimeImmutable objects.
*/
class DateTimeValidator extends AbstractValidator
{
/**
* Checks if the given value is a valid DateTime object. If this is not
* the case, the function adds an error.
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
$this->result->clear();
if ($value instanceof \DateTimeInterface) {
return;
}
$this->addError(
$this->translateErrorMessage(
'validator.datetime.notvalid',
'extbase',
[
gettype($value)
]
),
1238087674,
[gettype($value)]
);
}
}
@@ -0,0 +1,60 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator to chain many validators in a disjunction (logical or).
*/
class DisjunctionValidator extends AbstractCompositeValidator
{
/**
* Checks if the given value is valid according to the validators of the
* disjunction.
*
* So only one validator has to be valid, to make the whole disjunction valid.
* Errors are only returned if all validators failed.
*
* @param mixed $value The value that should be validated
* @return \TYPO3\CMS\Extbase\Error\Result
*/
public function validate($value)
{
$validators = $this->getValidators();
if ($validators->count() > 0) {
$result = null;
foreach ($validators as $validator) {
$validatorResult = $validator->validate($value);
if ($validatorResult->hasErrors()) {
if ($result === null) {
$result = $validatorResult;
} else {
$result->merge($validatorResult);
}
} else {
if ($result === null) {
$result = $validatorResult;
} else {
$result->clear();
}
break;
}
}
} else {
$result = new \TYPO3\CMS\Extbase\Error\Result();
}
return $result;
}
}
@@ -0,0 +1,50 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for email addresses
*/
class EmailAddressValidator extends AbstractValidator
{
/**
* Checks if the given value is a valid email address.
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
if (!is_string($value) || !$this->validEmail($value)) {
$this->addError(
$this->translateErrorMessage(
'validator.emailaddress.notvalid',
'extbase'
),
1221559976
);
}
}
/**
* Checking syntax of input email address
*
* @param string $emailAddress Input string to evaluate
* @return bool Returns TRUE if the $email address (input string) is valid
*/
protected function validEmail($emailAddress)
{
return \TYPO3\CMS\Core\Utility\GeneralUtility::validEmail($emailAddress);
}
}
@@ -0,0 +1,43 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for floats.
*/
class FloatValidator extends AbstractValidator
{
/**
* The given value is valid if it is of type float or a string matching the regular expression [0-9.e+-]
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
if (is_float($value)) {
return;
}
if (!is_string($value) || strpos($value, '.') === false || preg_match('/^[0-9.e+-]+$/', $value) !== 1) {
$this->addError(
$this->translateErrorMessage(
'validator.float.notvalid',
'extbase'
),
1221560288
);
}
}
}
@@ -0,0 +1,193 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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\Reflection\ObjectAccess;
/**
* A generic object validator which allows for specifying property validators
*/
class GenericObjectValidator extends AbstractValidator implements ObjectValidatorInterface
{
/**
* @var \SplObjectStorage[]
*/
protected $propertyValidators = [];
/**
* Checks if the given value is valid according to the validator, and returns
* the Error Messages object which occurred.
*
* @param mixed $value The value that should be validated
* @return \TYPO3\CMS\Extbase\Error\Result
*/
public function validate($value)
{
if (is_object($value) && $this->isValidatedAlready($value)) {
return $this->result;
}
$this->result = new \TYPO3\CMS\Extbase\Error\Result();
if ($this->acceptsEmptyValues === false || $this->isEmpty($value) === false) {
if (!is_object($value)) {
$this->addError('Object expected, %1$s given.', 1241099149, [gettype($value)]);
} elseif ($this->isValidatedAlready($value) === false) {
$this->markInstanceAsValidated($value);
$this->isValid($value);
}
}
return $this->result;
}
/**
* Load the property value to be used for validation.
*
* In case the object is a doctrine proxy, we need to load the real instance first.
*
* @param object $object
* @param string $propertyName
* @return mixed
*/
protected function getPropertyValue($object, $propertyName)
{
// @todo add support for lazy loading proxies, if needed
if (ObjectAccess::isPropertyGettable($object, $propertyName)) {
return ObjectAccess::getProperty($object, $propertyName);
}
return ObjectAccess::getProperty($object, $propertyName, true);
}
/**
* Checks if the specified property of the given object is valid, and adds
* found errors to the $messages object.
*
* @param mixed $value The value to be validated
* @param \Traversable $validators The validators to be called on the value
* @param string $propertyName Name of ther property to check
*/
protected function checkProperty($value, $validators, $propertyName)
{
/** @var \TYPO3\CMS\Extbase\Error\Result $result */
$result = null;
foreach ($validators as $validator) {
if ($validator instanceof ObjectValidatorInterface) {
$validator->setValidatedInstancesContainer($this->validatedInstancesContainer);
}
$currentResult = $validator->validate($value);
if ($currentResult->hasMessages()) {
if ($result == null) {
$result = $currentResult;
} else {
$result->merge($currentResult);
}
}
}
if ($result != null) {
$this->result->forProperty($propertyName)->merge($result);
}
}
/**
* Checks if the given value is valid according to the property validators.
*
* @param mixed $object The value that should be validated
*/
protected function isValid($object)
{
foreach ($this->propertyValidators as $propertyName => $validators) {
$propertyValue = $this->getPropertyValue($object, $propertyName);
$this->checkProperty($propertyValue, $validators, $propertyName);
}
}
/**
* Checks the given object can be validated by the validator implementation
*
* @param mixed $object The object to be checked
* @return bool TRUE if the given value is an object
*/
public function canValidate($object)
{
return is_object($object);
}
/**
* Adds the given validator for validation of the specified property.
*
* @param string $propertyName Name of the property to validate
* @param ValidatorInterface $validator The property validator
*/
public function addPropertyValidator($propertyName, ValidatorInterface $validator)
{
if (!isset($this->propertyValidators[$propertyName])) {
$this->propertyValidators[$propertyName] = new \SplObjectStorage();
}
$this->propertyValidators[$propertyName]->attach($validator);
}
/**
* @param object $object
* @return bool
*/
protected function isValidatedAlready($object)
{
if ($this->validatedInstancesContainer === null) {
$this->validatedInstancesContainer = new \SplObjectStorage();
}
if ($this->validatedInstancesContainer->contains($object)) {
return true;
}
return false;
}
/**
* @param $object
*/
protected function markInstanceAsValidated($object): void
{
$this->validatedInstancesContainer->attach($object);
}
/**
* Returns all property validators - or only validators of the specified property
*
* @param string $propertyName Name of the property to return validators for
* @return array An array of validators
*/
public function getPropertyValidators($propertyName = null)
{
if ($propertyName !== null) {
return $this->propertyValidators[$propertyName] ?? [];
}
return $this->propertyValidators;
}
/**
* @var \SplObjectStorage
*/
protected $validatedInstancesContainer;
/**
* Allows to set a container to keep track of validated instances.
*
* @param \SplObjectStorage $validatedInstancesContainer A container to keep track of validated instances
*/
public function setValidatedInstancesContainer(\SplObjectStorage $validatedInstancesContainer)
{
$this->validatedInstancesContainer = $validatedInstancesContainer;
}
}
@@ -0,0 +1,39 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for integers.
*/
class IntegerValidator extends AbstractValidator
{
/**
* Checks if the given value is a valid integer.
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
if (filter_var($value, FILTER_VALIDATE_INT) === false) {
$this->addError(
$this->translateErrorMessage(
'validator.integer.notvalid',
'extbase'
),
1221560494
);
}
}
}
@@ -0,0 +1,74 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for not empty values.
*/
class NotEmptyValidator extends AbstractValidator
{
/**
* This validator always needs to be executed even if the given value is empty.
* See AbstractValidator::validate()
*
* @var bool
*/
protected $acceptsEmptyValues = false;
/**
* Checks if the given property ($propertyValue) is not empty (NULL, empty string, empty array or empty object).
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
if ($value === null) {
$this->addError(
$this->translateErrorMessage(
'validator.notempty.null',
'extbase'
),
1221560910
);
}
if ($value === '') {
$this->addError(
$this->translateErrorMessage(
'validator.notempty.empty',
'extbase'
),
1221560718
);
}
if (is_array($value) && empty($value)) {
$this->addError(
$this->translateErrorMessage(
'validator.notempty.empty',
'extbase'
),
1347992400
);
}
if (is_object($value) && $value instanceof \Countable && $value->count() === 0) {
$this->addError(
$this->translateErrorMessage(
'validator.notempty.empty',
'extbase'
),
1347992453
);
}
}
}
@@ -0,0 +1,67 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for general numbers
*/
class NumberRangeValidator extends AbstractValidator
{
/**
* @var array
*/
protected $supportedOptions = [
'minimum' => [0, 'The minimum value to accept', 'integer'],
'maximum' => [PHP_INT_MAX, 'The maximum value to accept', 'integer']
];
/**
* The given value is valid if it is a number in the specified range.
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
if (!is_numeric($value)) {
$this->addError(
$this->translateErrorMessage(
'validator.numberrange.notvalid',
'extbase'
),
1221563685
);
return;
}
$minimum = $this->options['minimum'];
$maximum = $this->options['maximum'];
if ($minimum > $maximum) {
$x = $minimum;
$minimum = $maximum;
$maximum = $x;
}
if ($value < $minimum || $value > $maximum) {
$this->addError($this->translateErrorMessage(
'validator.numberrange.range',
'extbase',
[
$minimum,
$maximum
]
), 1221561046, [$minimum, $maximum]);
}
}
}
@@ -0,0 +1,39 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for general numbers.
*/
class NumberValidator extends AbstractValidator
{
/**
* Checks if the given value is a valid number.
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
if (!is_numeric($value)) {
$this->addError(
$this->translateErrorMessage(
'validator.number.notvalid',
'extbase'
),
1221563685
);
}
}
}
@@ -0,0 +1,28 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Contract for a validator
*/
interface ObjectValidatorInterface extends ValidatorInterface
{
/**
* Allows to set a container to keep track of validated instances.
*
* @param \SplObjectStorage $validatedInstancesContainer A container to keep track of validated instances
*/
public function setValidatedInstancesContainer(\SplObjectStorage $validatedInstancesContainer);
}
@@ -0,0 +1,45 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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 validator which accepts any input.
*
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
*/
class RawValidator extends AbstractValidator
{
/**
* @param array $options
*/
public function __construct(array $options = [])
{
trigger_error(
__CLASS__ . ' is deprecated and will be removed in TYPO3 v10.0.',
E_USER_DEPRECATED
);
parent::__construct($options);
}
/**
* This validator is always valid.
*
* @param mixed $value The value that should be validated (not used here)
*/
public function isValid($value)
{
}
}
@@ -0,0 +1,51 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator based on regular expressions.
*/
class RegularExpressionValidator extends AbstractValidator
{
/**
* @var array
*/
protected $supportedOptions = [
'regularExpression' => ['', 'The regular expression to use for validation, used as given', 'string', true]
];
/**
* Checks if the given value matches the specified regular expression.
*
* @param mixed $value The value that should be validated
* @throws \TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationOptionsException
*/
public function isValid($value)
{
$result = preg_match($this->options['regularExpression'], $value);
if ($result === 0) {
$this->addError(
$this->translateErrorMessage(
'validator.regularexpression.nomatch',
'extbase'
),
1221565130
);
}
if ($result === false) {
throw new \TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationOptionsException('regularExpression "' . $this->options['regularExpression'] . '" in RegularExpressionValidator contained an error.', 1298273089);
}
}
}
@@ -0,0 +1,104 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for string length.
*/
class StringLengthValidator extends AbstractValidator
{
/**
* @var array
*/
protected $supportedOptions = [
'minimum' => [0, 'Minimum length for a valid string', 'integer'],
'maximum' => [PHP_INT_MAX, 'Maximum length for a valid string', 'integer']
];
/**
* Checks if the given value is a valid string (or can be cast to a string
* if an object is given) and its length is between minimum and maximum
* specified in the validation options.
*
* @param mixed $value The value that should be validated
* @throws \TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationOptionsException
*/
public function isValid($value)
{
if ($this->options['maximum'] < $this->options['minimum']) {
throw new \TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationOptionsException('The \'maximum\' is shorter than the \'minimum\' in the StringLengthValidator.', 1238107096);
}
if (is_object($value)) {
if (!method_exists($value, '__toString')) {
$this->addError('The given object could not be converted to a string.', 1238110957);
return;
}
} elseif (!is_string($value)) {
$this->addError('The given value was not a valid string.', 1269883975);
return;
}
$stringLength = mb_strlen($value, 'utf-8');
$isValid = true;
if ($stringLength < $this->options['minimum']) {
$isValid = false;
}
if ($stringLength > $this->options['maximum']) {
$isValid = false;
}
if ($isValid === false) {
if ($this->options['minimum'] > 0 && $this->options['maximum'] < PHP_INT_MAX) {
$this->addError(
$this->translateErrorMessage(
'validator.stringlength.between',
'extbase',
[
$this->options['minimum'],
$this->options['maximum']
]
),
1428504122,
[$this->options['minimum'], $this->options['maximum']]
);
} elseif ($this->options['minimum'] > 0) {
$this->addError(
$this->translateErrorMessage(
'validator.stringlength.less',
'extbase',
[
$this->options['minimum']
]
),
1238108068,
[$this->options['minimum']]
);
} else {
$this->addError(
$this->translateErrorMessage(
'validator.stringlength.exceed',
'extbase',
[
$this->options['maximum']
]
),
1238108069,
[$this->options['maximum']]
);
}
}
}
}
@@ -0,0 +1,39 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for strings.
*/
class StringValidator extends AbstractValidator
{
/**
* Checks if the given value is a string.
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
if (!is_string($value)) {
$this->addError(
$this->translateErrorMessage(
'validator.string.notvalid',
'extbase'
),
1238108067
);
}
}
}
@@ -0,0 +1,45 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Validator for "plain" text.
*/
class TextValidator extends AbstractValidator
{
/**
* Checks if the given value is a valid text (contains no XML tags).
*
* Be aware that the value of this check entirely depends on the output context.
* The validated text is not expected to be secure in every circumstance, if you
* want to be sure of that, use a customized regular expression or filter on output.
*
* See http://php.net/filter_var for details.
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
if ($value !== filter_var($value, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES)) {
$this->addError(
$this->translateErrorMessage(
'validator.text.notvalid',
'extbase'
),
1221565786
);
}
}
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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\Utility\GeneralUtility;
/**
* Validator for url.
*/
class UrlValidator extends AbstractValidator
{
/**
* Checks if the given value is a string.
*
* @param mixed $value The value that should be validated
*/
public function isValid($value)
{
if (!is_string($value) || !GeneralUtility::isValidUrl($value)) {
$this->addError(
$this->translateErrorMessage(
'validator.url.notvalid',
'extbase'
),
1238108078
);
}
}
}
@@ -0,0 +1,37 @@
<?php
namespace TYPO3\CMS\Extbase\Validation\Validator;
/*
* 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!
*/
/**
* Contract for a validator
*/
interface ValidatorInterface
{
/**
* Checks if the given value is valid according to the validator, and returns
* the Error Messages object which occurred.
*
* @param mixed $value The value that should be validated
* @return \TYPO3\CMS\Extbase\Error\Result
*/
public function validate($value);
/**
* Returns the options of this validator which can be specified in the constructor
*
* @return array
*/
public function getOptions();
}
@@ -0,0 +1,576 @@
<?php
namespace TYPO3\CMS\Extbase\Validation;
/*
* 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\Log\LogManager;
use TYPO3\CMS\Core\Utility\ClassNamingUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
use TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException;
use TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator;
/**
* Validator resolver to automatically find an appropriate validator for a given subject
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
class ValidatorResolver implements \TYPO3\CMS\Core\SingletonInterface
{
/**
* Match validator names and options
* @deprecated and will be removed in TYPO3 v10.0.
*
* @var string
*/
const PATTERN_MATCH_VALIDATORS = '/
(?:^|,\s*)
(?P<validatorName>[a-z0-9:.\\\\]+)
\s*
(?:\(
(?P<validatorOptions>(?:\s*[a-z0-9]+\s*=\s*(?:
"(?:\\\\"|[^"])*"
|\'(?:\\\\\'|[^\'])*\'
|(?:\s|[^,"\']*)
)(?:\s|,)*)*)
\))?
/ixS';
/**
* Match validator options (to parse actual options)
* @var string
* @deprecated and will be removed in TYPO3 v10.0.
*/
const PATTERN_MATCH_VALIDATOROPTIONS = '/
\s*
(?P<optionName>[a-z0-9]+)
\s*=\s*
(?P<optionValue>
"(?:\\\\"|[^"])*"
|\'(?:\\\\\'|[^\'])*\'
|(?:\s|[^,"\']*)
)
/ixS';
/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
*/
protected $reflectionService;
/**
* @var array
*/
protected $baseValidatorConjunctions = [];
/**
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
*/
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
*/
public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
{
$this->reflectionService = $reflectionService;
}
/**
* Get a validator for a given data type. Returns a validator implementing
* the \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface or NULL if no validator
* could be resolved.
*
* @param string $validatorType Either one of the built-in data types or fully qualified validator class name
* @param array $validatorOptions Options to be passed to the validator
* @return \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface Validator or NULL if none found.
*/
public function createValidator($validatorType, array $validatorOptions = [])
{
try {
/**
* @todo remove throwing Exceptions in resolveValidatorObjectName
*/
$validatorObjectName = $this->resolveValidatorObjectName($validatorType);
$validator = $this->objectManager->get($validatorObjectName, $validatorOptions);
// Move this check into ClassSchema
if (!($validator instanceof \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface)) {
throw new Exception\NoSuchValidatorException('The validator "' . $validatorObjectName . '" does not implement TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface!', 1300694875);
}
return $validator;
} catch (NoSuchValidatorException $e) {
GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__)->debug($e->getMessage());
return null;
}
}
/**
* Resolves and returns the base validator conjunction for the given data type.
*
* If no validator could be resolved (which usually means that no validation is necessary),
* NULL is returned.
*
* @param string $targetClassName The data type to search a validator for. Usually the fully qualified object name
* @return ConjunctionValidator The validator conjunction or NULL
*/
public function getBaseValidatorConjunction($targetClassName)
{
if (!array_key_exists($targetClassName, $this->baseValidatorConjunctions)) {
$this->buildBaseValidatorConjunction($targetClassName, $targetClassName);
}
return $this->baseValidatorConjunctions[$targetClassName];
}
/**
* Detects and registers any validators for arguments:
* - by the data type specified in the param annotations
* - additional validators specified in the validate annotations of a method
*
* @param string $className
* @param string $methodName
* @param array $methodParameters Optional pre-compiled array of method parameters
* @param array $methodValidateAnnotations Optional pre-compiled array of validate annotations (as array)
* @return ConjunctionValidator[] An Array of ValidatorConjunctions for each method parameters.
* @throws \TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationConfigurationException
* @throws \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException
* @throws \TYPO3\CMS\Extbase\Validation\Exception\InvalidTypeHintException
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
*/
public function buildMethodArgumentsValidatorConjunctions($className, $methodName, array $methodParameters = null, array $methodValidateAnnotations = null)
{
trigger_error(
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
E_USER_DEPRECATED
);
/** @var ConjunctionValidator[] $validatorConjunctions */
$validatorConjunctions = [];
if ($methodParameters === null) {
$methodParameters = $this->reflectionService
->getClassSchema($className)
->getMethod($methodName)['params'] ?? [];
}
if (empty($methodParameters)) {
return $validatorConjunctions;
}
foreach ($methodParameters as $parameterName => $methodParameter) {
/** @var ConjunctionValidator $validatorConjunction */
$validatorConjunction = $this->createValidator(ConjunctionValidator::class);
if (!array_key_exists('type', $methodParameter)) {
throw new Exception\InvalidTypeHintException('Missing type information, probably no @param annotation for parameter "$' . $parameterName . '" in ' . $className . '->' . $methodName . '()', 1281962564);
}
if (strpbrk($methodParameter['type'], '\\') === false) {
$typeValidator = $this->createValidator($methodParameter['type']);
} else {
$typeValidator = null;
}
if ($typeValidator !== null) {
$validatorConjunction->addValidator($typeValidator);
}
$validatorConjunctions[$parameterName] = $validatorConjunction;
}
if ($methodValidateAnnotations === null) {
$validateAnnotations = $this->getMethodValidateAnnotations($className, $methodName);
$methodValidateAnnotations = array_map(function ($validateAnnotation) {
return [
'type' => $validateAnnotation['validatorName'],
'options' => $validateAnnotation['validatorOptions'],
'argumentName' => $validateAnnotation['argumentName'],
];
}, $validateAnnotations);
}
foreach ($methodValidateAnnotations as $annotationParameters) {
$newValidator = $this->createValidator($annotationParameters['type'], $annotationParameters['options']);
if ($newValidator === null) {
throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $className . '->' . $methodName . '(): Could not resolve class name for validator "' . $annotationParameters['type'] . '".', 1239853109);
}
if (isset($validatorConjunctions[$annotationParameters['argumentName']])) {
$validatorConjunctions[$annotationParameters['argumentName']]->addValidator($newValidator);
} elseif (strpos($annotationParameters['argumentName'], '.') !== false) {
$objectPath = explode('.', $annotationParameters['argumentName']);
$argumentName = array_shift($objectPath);
$validatorConjunctions[$argumentName]->addValidator($this->buildSubObjectValidator($objectPath, $newValidator));
} else {
throw new Exception\InvalidValidationConfigurationException('Invalid validate annotation in ' . $className . '->' . $methodName . '(): Validator specified for argument name "' . $annotationParameters['argumentName'] . '", but this argument does not exist.', 1253172726);
}
}
return $validatorConjunctions;
}
/**
* Builds a chain of nested object validators by specification of the given
* object path.
*
* @param array $objectPath The object path
* @param \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $propertyValidator The validator which should be added to the property specified by objectPath
* @return \TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
*/
protected function buildSubObjectValidator(array $objectPath, \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $propertyValidator)
{
trigger_error(
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
E_USER_DEPRECATED
);
$rootObjectValidator = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator::class, []);
$parentObjectValidator = $rootObjectValidator;
while (count($objectPath) > 1) {
$subObjectValidator = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator::class, []);
$subPropertyName = array_shift($objectPath);
$parentObjectValidator->addPropertyValidator($subPropertyName, $subObjectValidator);
$parentObjectValidator = $subObjectValidator;
}
$parentObjectValidator->addPropertyValidator(array_shift($objectPath), $propertyValidator);
return $rootObjectValidator;
}
/**
* Builds a base validator conjunction for the given data type.
*
* The base validation rules are those which were declared directly in a class (typically
* a model) through some validate annotations on properties.
*
* If a property holds a class for which a base validator exists, that property will be
* checked as well, regardless of a validate annotation
*
* Additionally, if a custom validator was defined for the class in question, it will be added
* to the end of the conjunction. A custom validator is found if it follows the naming convention
* "Replace '\Model\' by '\Validator\' and append 'Validator'".
*
* Example: $targetClassName is TYPO3\Foo\Domain\Model\Quux, then the validator will be found if it has the
* name TYPO3\Foo\Domain\Validator\QuuxValidator
*
* @param string $indexKey The key to use as index in $this->baseValidatorConjunctions; calculated from target class name and validation groups
* @param string $targetClassName The data type to build the validation conjunction for. Needs to be the fully qualified class name.
* @param array $validationGroups The validation groups to build the validator for
* @throws \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException
* @throws \InvalidArgumentException
*/
protected function buildBaseValidatorConjunction($indexKey, $targetClassName, array $validationGroups = [])
{
$conjunctionValidator = new ConjunctionValidator();
$this->baseValidatorConjunctions[$indexKey] = $conjunctionValidator;
// note: the simpleType check reduces lookups to the class loader
if (!TypeHandlingUtility::isSimpleType($targetClassName) && class_exists($targetClassName)) {
$classSchema = $this->reflectionService->getClassSchema($targetClassName);
// Model based validator
/** @var \TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator $objectValidator */
$objectValidator = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator::class, []);
foreach ($classSchema->getProperties() as $classPropertyName => $classPropertyDefinition) {
/** @var array|array[] $classPropertyDefinition */
$classPropertyTagsValues = $classPropertyDefinition['tags'];
if (!isset($classPropertyTagsValues['var'])) {
throw new \InvalidArgumentException(sprintf('There is no @var annotation for property "%s" in class "%s".', $classPropertyName, $targetClassName), 1363778104);
}
$propertyTargetClassName = $classPropertyDefinition['type'];
// note: the outer simpleType check reduces lookups to the class loader
if (!TypeHandlingUtility::isSimpleType($propertyTargetClassName)) {
if (TypeHandlingUtility::isCollectionType($propertyTargetClassName)) {
$collectionValidator = $this->createValidator(
\TYPO3\CMS\Extbase\Validation\Validator\CollectionValidator::class,
[
'elementType' => $classPropertyDefinition['elementType'],
'validationGroups' => $validationGroups
]
);
$objectValidator->addPropertyValidator($classPropertyName, $collectionValidator);
} elseif (class_exists($propertyTargetClassName) && !TypeHandlingUtility::isCoreType($propertyTargetClassName) && $this->objectManager->isRegistered($propertyTargetClassName) && $this->objectManager->getScope($propertyTargetClassName) === \TYPO3\CMS\Extbase\Object\Container\Container::SCOPE_PROTOTYPE) {
$validatorForProperty = $this->getBaseValidatorConjunction($propertyTargetClassName);
if ($validatorForProperty !== null && $validatorForProperty->count() > 0) {
$objectValidator->addPropertyValidator($classPropertyName, $validatorForProperty);
}
}
}
foreach ($classPropertyDefinition['validators'] as $validatorDefinition) {
// @todo: Respect validationGroups
// @todo: At this point we already have the class name of the validator, thus there is not need
// @todo: calling \TYPO3\CMS\Extbase\Validation\ValidatorResolver::resolveValidatorObjectName inside
// @todo: \TYPO3\CMS\Extbase\Validation\ValidatorResolver::createValidator once again. However, to
// @todo: keep things simple for now, we still use the method createValidator here. In the future,
// @todo: createValidator must only accept FQCN's.
$newValidator = $this->createValidator($validatorDefinition['className'], $validatorDefinition['options']);
if ($newValidator === null) {
throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $targetClassName . '::' . $classPropertyName . ': Could not resolve class name for validator "' . $validatorDefinition['className'] . '".', 1241098027);
}
$objectValidator->addPropertyValidator($classPropertyName, $newValidator);
}
}
if (!empty($objectValidator->getPropertyValidators())) {
$conjunctionValidator->addValidator($objectValidator);
}
}
$this->addCustomValidators($targetClassName, $conjunctionValidator);
}
/**
* This adds custom validators to the passed $conjunctionValidator.
*
* A custom validator is found if it follows the naming convention "Replace '\Model\' by '\Validator\' and
* append 'Validator'". If found, it will be added to the $conjunctionValidator.
*
* In addition canValidate() will be called on all implementations of the ObjectValidatorInterface to find
* all validators that could validate the target. The one with the highest priority will be added as well.
* If multiple validators have the same priority, which one will be added is not deterministic.
*
* @param string $targetClassName
* @param ConjunctionValidator $conjunctionValidator
*/
protected function addCustomValidators($targetClassName, ConjunctionValidator &$conjunctionValidator)
{
// @todo: get rid of ClassNamingUtility usage once we dropped underscored class name support
$possibleValidatorClassName = ClassNamingUtility::translateModelNameToValidatorName($targetClassName);
$customValidator = $this->createValidator($possibleValidatorClassName);
if ($customValidator !== null) {
$conjunctionValidator->addValidator($customValidator);
}
// @todo: find polytype validator for class
}
/**
* Parses the validator options given in @validate annotations.
*
* @param string $validateValue
* @return array
* @internal
* @deprecated Helper, remove in TYPO3 v10.0 together with the deprecated consumer methods
*/
public function parseValidatorAnnotation($validateValue)
{
trigger_error(
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
E_USER_DEPRECATED
);
$matches = [];
if ($validateValue[0] === '$') {
$parts = explode(' ', $validateValue, 2);
$validatorConfiguration = ['argumentName' => ltrim($parts[0], '$'), 'validators' => []];
preg_match_all(self::PATTERN_MATCH_VALIDATORS, $parts[1], $matches, PREG_SET_ORDER);
} else {
$validatorConfiguration = ['validators' => []];
preg_match_all(self::PATTERN_MATCH_VALIDATORS, $validateValue, $matches, PREG_SET_ORDER);
}
foreach ($matches as $match) {
$validatorOptions = [];
if (isset($match['validatorOptions'])) {
$validatorOptions = $this->parseValidatorOptions($match['validatorOptions']);
}
$validatorConfiguration['validators'][] = ['validatorName' => $match['validatorName'], 'validatorOptions' => $validatorOptions];
}
return $validatorConfiguration;
}
/**
* Parses $rawValidatorOptions not containing quoted option values.
* $rawValidatorOptions will be an empty string afterwards (pass by ref!).
*
* @param string $rawValidatorOptions
* @return array An array of optionName/optionValue pairs
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
*/
protected function parseValidatorOptions($rawValidatorOptions)
{
trigger_error(
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
E_USER_DEPRECATED
);
$validatorOptions = [];
$parsedValidatorOptions = [];
preg_match_all(self::PATTERN_MATCH_VALIDATOROPTIONS, $rawValidatorOptions, $validatorOptions, PREG_SET_ORDER);
foreach ($validatorOptions as $validatorOption) {
$parsedValidatorOptions[trim($validatorOption['optionName'])] = trim($validatorOption['optionValue']);
}
array_walk($parsedValidatorOptions, [$this, 'unquoteString']);
return $parsedValidatorOptions;
}
/**
* Removes escapings from a given argument string and trims the outermost
* quotes.
*
* This method is meant as a helper for regular expression results.
*
* @param string &$quotedValue Value to unquote
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
*/
protected function unquoteString(&$quotedValue)
{
trigger_error(
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
E_USER_DEPRECATED
);
switch ($quotedValue[0]) {
case '"':
$quotedValue = str_replace('\\"', '"', trim($quotedValue, '"'));
break;
case '\'':
$quotedValue = str_replace('\\\'', '\'', trim($quotedValue, '\''));
break;
}
$quotedValue = str_replace('\\\\', '\\', $quotedValue);
}
/**
* Returns an object of an appropriate validator for the given class. If no validator is available
* FALSE is returned
*
* @param string $validatorName Either the fully qualified class name of the validator or the short name of a built-in validator
*
* @throws Exception\NoSuchValidatorException
* @return string Name of the validator object
* @internal
*/
public function resolveValidatorObjectName($validatorName)
{
if (strpos($validatorName, ':') !== false) {
// Found shorthand validator, either extbase or foreign extension
// NotEmpty or Acme.MyPck.Ext:MyValidator
list($extensionName, $extensionValidatorName) = explode(':', $validatorName);
if ($validatorName !== $extensionName && $extensionValidatorName !== '') {
// Shorthand custom
if (strpos($extensionName, '.') !== false) {
$extensionNameParts = explode('.', $extensionName);
$extensionName = array_pop($extensionNameParts);
$vendorName = implode('\\', $extensionNameParts);
$possibleClassName = $vendorName . '\\' . $extensionName . '\\Validation\\Validator\\' . $extensionValidatorName;
}
} else {
// Shorthand built in
$possibleClassName = 'TYPO3\\CMS\\Extbase\\Validation\\Validator\\' . $this->getValidatorType($validatorName);
}
} elseif (strpbrk($validatorName, '\\') === false) {
// Shorthand built in
$possibleClassName = 'TYPO3\\CMS\\Extbase\\Validation\\Validator\\' . $this->getValidatorType($validatorName);
} else {
// Full qualified
// Example: \Acme\Ext\Validation\Validator\FooValidator
$possibleClassName = $validatorName;
if (!empty($possibleClassName) && $possibleClassName[0] === '\\') {
$possibleClassName = substr($possibleClassName, 1);
}
}
if (substr($possibleClassName, - strlen('Validator')) !== 'Validator') {
$possibleClassName .= 'Validator';
}
if (class_exists($possibleClassName)) {
$possibleClassNameInterfaces = class_implements($possibleClassName);
if (!in_array(\TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface::class, $possibleClassNameInterfaces)) {
// The guessed validatorname is a valid class name, but does not implement the ValidatorInterface
throw new NoSuchValidatorException('Validator class ' . $validatorName . ' must implement \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface', 1365776838);
}
$resolvedValidatorName = $possibleClassName;
} else {
throw new NoSuchValidatorException('Validator class ' . $validatorName . ' does not exist', 1365799920);
}
return $resolvedValidatorName;
}
/**
* Used to map PHP types to validator types.
*
* @param string $type Data type to unify
* @return string unified data type
*/
protected function getValidatorType($type)
{
switch ($type) {
case 'int':
$type = 'Integer';
break;
case 'bool':
$type = 'Boolean';
break;
case 'double':
$type = 'Float';
break;
case 'numeric':
$type = 'Number';
break;
case 'mixed':
$type = 'Raw';
break;
default:
$type = ucfirst($type);
}
return $type;
}
/**
* Temporary replacement for $this->reflectionService->getMethodAnnotations()
*
* @param string $className
* @param string $methodName
*
* @return array
* @deprecated Helper, remove in TYPO3 v10.0 together with buildMethodArgumentsValidatorConjunctions()
*/
public function getMethodValidateAnnotations($className, $methodName)
{
$validateAnnotations = [];
$methodTagsValues = $this->reflectionService->getClassSchema($className)->getMethod($methodName)['tags'] ?? [];
if (isset($methodTagsValues['validate']) && is_array($methodTagsValues['validate'])) {
foreach ($methodTagsValues['validate'] as $validateValue) {
$parsedAnnotations = $this->parseValidatorAnnotation($validateValue);
foreach ($parsedAnnotations['validators'] as $validator) {
$validateAnnotations[] = [
'argumentName' => $parsedAnnotations['argumentName'],
'validatorName' => $validator['validatorName'],
'validatorOptions' => $validator['validatorOptions']
];
}
}
}
return $validateAnnotations;
}
}