login consent app sql
This commit is contained in:
89
vendor/symfony/form/Extension/Core/CoreExtension.php
vendored
Normal file
89
vendor/symfony/form/Extension/Core/CoreExtension.php
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core;
|
||||
|
||||
use Symfony\Component\Form\AbstractExtension;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TransformationFailureExtension;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* Represents the main form extension, which loads the core functionality.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class CoreExtension extends AbstractExtension
|
||||
{
|
||||
private $propertyAccessor;
|
||||
private $choiceListFactory;
|
||||
private $translator;
|
||||
|
||||
public function __construct(PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null, TranslatorInterface $translator = null)
|
||||
{
|
||||
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
|
||||
$this->choiceListFactory = $choiceListFactory ?? new CachingFactoryDecorator(new PropertyAccessDecorator(new DefaultChoiceListFactory(), $this->propertyAccessor));
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
protected function loadTypes()
|
||||
{
|
||||
return [
|
||||
new Type\FormType($this->propertyAccessor),
|
||||
new Type\BirthdayType(),
|
||||
new Type\CheckboxType(),
|
||||
new Type\ChoiceType($this->choiceListFactory, $this->translator),
|
||||
new Type\CollectionType(),
|
||||
new Type\CountryType(),
|
||||
new Type\DateIntervalType(),
|
||||
new Type\DateType(),
|
||||
new Type\DateTimeType(),
|
||||
new Type\EmailType(),
|
||||
new Type\HiddenType(),
|
||||
new Type\IntegerType(),
|
||||
new Type\LanguageType(),
|
||||
new Type\LocaleType(),
|
||||
new Type\MoneyType(),
|
||||
new Type\NumberType(),
|
||||
new Type\PasswordType(),
|
||||
new Type\PercentType(),
|
||||
new Type\RadioType(),
|
||||
new Type\RangeType(),
|
||||
new Type\RepeatedType(),
|
||||
new Type\SearchType(),
|
||||
new Type\TextareaType(),
|
||||
new Type\TextType(),
|
||||
new Type\TimeType(),
|
||||
new Type\TimezoneType(),
|
||||
new Type\UrlType(),
|
||||
new Type\FileType($this->translator),
|
||||
new Type\ButtonType(),
|
||||
new Type\SubmitType(),
|
||||
new Type\ResetType(),
|
||||
new Type\CurrencyType(),
|
||||
new Type\TelType(),
|
||||
new Type\ColorType($this->translator),
|
||||
new Type\WeekType(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function loadTypeExtensions()
|
||||
{
|
||||
return [
|
||||
new TransformationFailureExtension($this->translator),
|
||||
];
|
||||
}
|
||||
}
|
64
vendor/symfony/form/Extension/Core/DataAccessor/CallbackAccessor.php
vendored
Normal file
64
vendor/symfony/form/Extension/Core/DataAccessor/CallbackAccessor.php
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataAccessor;
|
||||
|
||||
use Symfony\Component\Form\DataAccessorInterface;
|
||||
use Symfony\Component\Form\Exception\AccessException;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
||||
/**
|
||||
* Writes and reads values to/from an object or array using callback functions.
|
||||
*
|
||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||
*/
|
||||
class CallbackAccessor implements DataAccessorInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue($data, FormInterface $form)
|
||||
{
|
||||
if (null === $getter = $form->getConfig()->getOption('getter')) {
|
||||
throw new AccessException('Unable to read from the given form data as no getter is defined.');
|
||||
}
|
||||
|
||||
return ($getter)($data, $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setValue(&$data, $value, FormInterface $form): void
|
||||
{
|
||||
if (null === $setter = $form->getConfig()->getOption('setter')) {
|
||||
throw new AccessException('Unable to write the given value as no setter is defined.');
|
||||
}
|
||||
|
||||
($setter)($data, $form->getData(), $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable($data, FormInterface $form): bool
|
||||
{
|
||||
return null !== $form->getConfig()->getOption('getter');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable($data, FormInterface $form): bool
|
||||
{
|
||||
return null !== $form->getConfig()->getOption('setter');
|
||||
}
|
||||
}
|
90
vendor/symfony/form/Extension/Core/DataAccessor/ChainAccessor.php
vendored
Normal file
90
vendor/symfony/form/Extension/Core/DataAccessor/ChainAccessor.php
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataAccessor;
|
||||
|
||||
use Symfony\Component\Form\DataAccessorInterface;
|
||||
use Symfony\Component\Form\Exception\AccessException;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
||||
/**
|
||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||
*/
|
||||
class ChainAccessor implements DataAccessorInterface
|
||||
{
|
||||
private $accessors;
|
||||
|
||||
/**
|
||||
* @param DataAccessorInterface[]|iterable $accessors
|
||||
*/
|
||||
public function __construct(iterable $accessors)
|
||||
{
|
||||
$this->accessors = $accessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue($data, FormInterface $form)
|
||||
{
|
||||
foreach ($this->accessors as $accessor) {
|
||||
if ($accessor->isReadable($data, $form)) {
|
||||
return $accessor->getValue($data, $form);
|
||||
}
|
||||
}
|
||||
|
||||
throw new AccessException('Unable to read from the given form data as no accessor in the chain is able to read the data.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setValue(&$data, $value, FormInterface $form): void
|
||||
{
|
||||
foreach ($this->accessors as $accessor) {
|
||||
if ($accessor->isWritable($data, $form)) {
|
||||
$accessor->setValue($data, $value, $form);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new AccessException('Unable to write the given value as no accessor in the chain is able to set the data.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable($data, FormInterface $form): bool
|
||||
{
|
||||
foreach ($this->accessors as $accessor) {
|
||||
if ($accessor->isReadable($data, $form)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable($data, FormInterface $form): bool
|
||||
{
|
||||
foreach ($this->accessors as $accessor) {
|
||||
if ($accessor->isWritable($data, $form)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
103
vendor/symfony/form/Extension/Core/DataAccessor/PropertyPathAccessor.php
vendored
Normal file
103
vendor/symfony/form/Extension/Core/DataAccessor/PropertyPathAccessor.php
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataAccessor;
|
||||
|
||||
use Symfony\Component\Form\DataAccessorInterface;
|
||||
use Symfony\Component\Form\Exception\AccessException;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\PropertyAccess\Exception\AccessException as PropertyAccessException;
|
||||
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyPathInterface;
|
||||
|
||||
/**
|
||||
* Writes and reads values to/from an object or array using property path.
|
||||
*
|
||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class PropertyPathAccessor implements DataAccessorInterface
|
||||
{
|
||||
private $propertyAccessor;
|
||||
|
||||
public function __construct(PropertyAccessorInterface $propertyAccessor = null)
|
||||
{
|
||||
$this->propertyAccessor = $propertyAccessor ?? PropertyAccess::createPropertyAccessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue($data, FormInterface $form)
|
||||
{
|
||||
if (null === $propertyPath = $form->getPropertyPath()) {
|
||||
throw new AccessException('Unable to read from the given form data as no property path is defined.');
|
||||
}
|
||||
|
||||
return $this->getPropertyValue($data, $propertyPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setValue(&$data, $propertyValue, FormInterface $form): void
|
||||
{
|
||||
if (null === $propertyPath = $form->getPropertyPath()) {
|
||||
throw new AccessException('Unable to write the given value as no property path is defined.');
|
||||
}
|
||||
|
||||
// If the field is of type DateTimeInterface and the data is the same skip the update to
|
||||
// keep the original object hash
|
||||
if ($propertyValue instanceof \DateTimeInterface && $propertyValue == $this->getPropertyValue($data, $propertyPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the data is identical to the value in $data, we are
|
||||
// dealing with a reference
|
||||
if (!\is_object($data) || !$form->getConfig()->getByReference() || $propertyValue !== $this->getPropertyValue($data, $propertyPath)) {
|
||||
$this->propertyAccessor->setValue($data, $propertyPath, $propertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable($data, FormInterface $form): bool
|
||||
{
|
||||
return null !== $form->getPropertyPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable($data, FormInterface $form): bool
|
||||
{
|
||||
return null !== $form->getPropertyPath();
|
||||
}
|
||||
|
||||
private function getPropertyValue($data, PropertyPathInterface $propertyPath)
|
||||
{
|
||||
try {
|
||||
return $this->propertyAccessor->getValue($data, $propertyPath);
|
||||
} catch (PropertyAccessException $e) {
|
||||
if (!$e instanceof UninitializedPropertyException
|
||||
// For versions without UninitializedPropertyException check the exception message
|
||||
&& (class_exists(UninitializedPropertyException::class) || false === strpos($e->getMessage(), 'You should initialize it'))
|
||||
) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
75
vendor/symfony/form/Extension/Core/DataMapper/CheckboxListMapper.php
vendored
Normal file
75
vendor/symfony/form/Extension/Core/DataMapper/CheckboxListMapper.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataMapper;
|
||||
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* Maps choices to/from checkbox forms.
|
||||
*
|
||||
* A {@link ChoiceListInterface} implementation is used to find the
|
||||
* corresponding string values for the choices. Each checkbox form whose "value"
|
||||
* option corresponds to any of the selected values is marked as selected.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class CheckboxListMapper implements DataMapperInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mapDataToForms($choices, iterable $checkboxes)
|
||||
{
|
||||
if (\is_array($checkboxes)) {
|
||||
trigger_deprecation('symfony/form', '5.3', 'Passing an array as the second argument of the "%s()" method is deprecated, pass "\Traversable" instead.', __METHOD__);
|
||||
}
|
||||
|
||||
if (null === $choices) {
|
||||
$choices = [];
|
||||
}
|
||||
|
||||
if (!\is_array($choices)) {
|
||||
throw new UnexpectedTypeException($choices, 'array');
|
||||
}
|
||||
|
||||
foreach ($checkboxes as $checkbox) {
|
||||
$value = $checkbox->getConfig()->getOption('value');
|
||||
$checkbox->setData(\in_array($value, $choices, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mapFormsToData(iterable $checkboxes, &$choices)
|
||||
{
|
||||
if (\is_array($checkboxes)) {
|
||||
trigger_deprecation('symfony/form', '5.3', 'Passing an array as the first argument of the "%s()" method is deprecated, pass "\Traversable" instead.', __METHOD__);
|
||||
}
|
||||
|
||||
if (!\is_array($choices)) {
|
||||
throw new UnexpectedTypeException($choices, 'array');
|
||||
}
|
||||
|
||||
$values = [];
|
||||
|
||||
foreach ($checkboxes as $checkbox) {
|
||||
if ($checkbox->getData()) {
|
||||
// construct an array of choice values
|
||||
$values[] = $checkbox->getConfig()->getOption('value');
|
||||
}
|
||||
}
|
||||
|
||||
$choices = $values;
|
||||
}
|
||||
}
|
91
vendor/symfony/form/Extension/Core/DataMapper/DataMapper.php
vendored
Normal file
91
vendor/symfony/form/Extension/Core/DataMapper/DataMapper.php
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataMapper;
|
||||
|
||||
use Symfony\Component\Form\DataAccessorInterface;
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Form\Extension\Core\DataAccessor\CallbackAccessor;
|
||||
use Symfony\Component\Form\Extension\Core\DataAccessor\ChainAccessor;
|
||||
use Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor;
|
||||
|
||||
/**
|
||||
* Maps arrays/objects to/from forms using data accessors.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class DataMapper implements DataMapperInterface
|
||||
{
|
||||
private $dataAccessor;
|
||||
|
||||
public function __construct(DataAccessorInterface $dataAccessor = null)
|
||||
{
|
||||
$this->dataAccessor = $dataAccessor ?? new ChainAccessor([
|
||||
new CallbackAccessor(),
|
||||
new PropertyPathAccessor(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mapDataToForms($data, iterable $forms): void
|
||||
{
|
||||
if (\is_array($forms)) {
|
||||
trigger_deprecation('symfony/form', '5.3', 'Passing an array as the second argument of the "%s()" method is deprecated, pass "\Traversable" instead.', __METHOD__);
|
||||
}
|
||||
|
||||
$empty = null === $data || [] === $data;
|
||||
|
||||
if (!$empty && !\is_array($data) && !\is_object($data)) {
|
||||
throw new UnexpectedTypeException($data, 'object, array or empty');
|
||||
}
|
||||
|
||||
foreach ($forms as $form) {
|
||||
$config = $form->getConfig();
|
||||
|
||||
if (!$empty && $config->getMapped() && $this->dataAccessor->isReadable($data, $form)) {
|
||||
$form->setData($this->dataAccessor->getValue($data, $form));
|
||||
} else {
|
||||
$form->setData($config->getData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mapFormsToData(iterable $forms, &$data): void
|
||||
{
|
||||
if (\is_array($forms)) {
|
||||
trigger_deprecation('symfony/form', '5.3', 'Passing an array as the first argument of the "%s()" method is deprecated, pass "\Traversable" instead.', __METHOD__);
|
||||
}
|
||||
|
||||
if (null === $data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!\is_array($data) && !\is_object($data)) {
|
||||
throw new UnexpectedTypeException($data, 'object, array or empty');
|
||||
}
|
||||
|
||||
foreach ($forms as $form) {
|
||||
$config = $form->getConfig();
|
||||
|
||||
// Write-back is disabled if the form is not synchronized (transformation failed),
|
||||
// if the form was not submitted and if the form is disabled (modification not allowed)
|
||||
if ($config->getMapped() && $form->isSubmitted() && $form->isSynchronized() && !$form->isDisabled() && $this->dataAccessor->isWritable($data, $form)) {
|
||||
$this->dataAccessor->setValue($data, $form->getData(), $form);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
113
vendor/symfony/form/Extension/Core/DataMapper/PropertyPathMapper.php
vendored
Normal file
113
vendor/symfony/form/Extension/Core/DataMapper/PropertyPathMapper.php
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataMapper;
|
||||
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\PropertyAccess\Exception\AccessException;
|
||||
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
trigger_deprecation('symfony/form', '5.2', 'The "%s" class is deprecated. Use "%s" instead.', PropertyPathMapper::class, DataMapper::class);
|
||||
|
||||
/**
|
||||
* Maps arrays/objects to/from forms using property paths.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @deprecated since symfony/form 5.2. Use {@see DataMapper} instead.
|
||||
*/
|
||||
class PropertyPathMapper implements DataMapperInterface
|
||||
{
|
||||
private $propertyAccessor;
|
||||
|
||||
public function __construct(PropertyAccessorInterface $propertyAccessor = null)
|
||||
{
|
||||
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mapDataToForms($data, iterable $forms)
|
||||
{
|
||||
$empty = null === $data || [] === $data;
|
||||
|
||||
if (!$empty && !\is_array($data) && !\is_object($data)) {
|
||||
throw new UnexpectedTypeException($data, 'object, array or empty');
|
||||
}
|
||||
|
||||
foreach ($forms as $form) {
|
||||
$propertyPath = $form->getPropertyPath();
|
||||
$config = $form->getConfig();
|
||||
|
||||
if (!$empty && null !== $propertyPath && $config->getMapped()) {
|
||||
$form->setData($this->getPropertyValue($data, $propertyPath));
|
||||
} else {
|
||||
$form->setData($config->getData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mapFormsToData(iterable $forms, &$data)
|
||||
{
|
||||
if (null === $data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!\is_array($data) && !\is_object($data)) {
|
||||
throw new UnexpectedTypeException($data, 'object, array or empty');
|
||||
}
|
||||
|
||||
foreach ($forms as $form) {
|
||||
$propertyPath = $form->getPropertyPath();
|
||||
$config = $form->getConfig();
|
||||
|
||||
// Write-back is disabled if the form is not synchronized (transformation failed),
|
||||
// if the form was not submitted and if the form is disabled (modification not allowed)
|
||||
if (null !== $propertyPath && $config->getMapped() && $form->isSubmitted() && $form->isSynchronized() && !$form->isDisabled()) {
|
||||
$propertyValue = $form->getData();
|
||||
// If the field is of type DateTimeInterface and the data is the same skip the update to
|
||||
// keep the original object hash
|
||||
if ($propertyValue instanceof \DateTimeInterface && $propertyValue == $this->getPropertyValue($data, $propertyPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the data is identical to the value in $data, we are
|
||||
// dealing with a reference
|
||||
if (!\is_object($data) || !$config->getByReference() || $propertyValue !== $this->getPropertyValue($data, $propertyPath)) {
|
||||
$this->propertyAccessor->setValue($data, $propertyPath, $propertyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getPropertyValue($data, $propertyPath)
|
||||
{
|
||||
try {
|
||||
return $this->propertyAccessor->getValue($data, $propertyPath);
|
||||
} catch (AccessException $e) {
|
||||
if (!$e instanceof UninitializedPropertyException
|
||||
// For versions without UninitializedPropertyException check the exception message
|
||||
&& (class_exists(UninitializedPropertyException::class) || !str_contains($e->getMessage(), 'You should initialize it'))
|
||||
) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
74
vendor/symfony/form/Extension/Core/DataMapper/RadioListMapper.php
vendored
Normal file
74
vendor/symfony/form/Extension/Core/DataMapper/RadioListMapper.php
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataMapper;
|
||||
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* Maps choices to/from radio forms.
|
||||
*
|
||||
* A {@link ChoiceListInterface} implementation is used to find the
|
||||
* corresponding string values for the choices. The radio form whose "value"
|
||||
* option corresponds to the selected value is marked as selected.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class RadioListMapper implements DataMapperInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mapDataToForms($choice, iterable $radios)
|
||||
{
|
||||
if (\is_array($radios)) {
|
||||
trigger_deprecation('symfony/form', '5.3', 'Passing an array as the second argument of the "%s()" method is deprecated, pass "\Traversable" instead.', __METHOD__);
|
||||
}
|
||||
|
||||
if (!\is_string($choice)) {
|
||||
throw new UnexpectedTypeException($choice, 'string');
|
||||
}
|
||||
|
||||
foreach ($radios as $radio) {
|
||||
$value = $radio->getConfig()->getOption('value');
|
||||
$radio->setData($choice === $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mapFormsToData(iterable $radios, &$choice)
|
||||
{
|
||||
if (\is_array($radios)) {
|
||||
trigger_deprecation('symfony/form', '5.3', 'Passing an array as the first argument of the "%s()" method is deprecated, pass "\Traversable" instead.', __METHOD__);
|
||||
}
|
||||
|
||||
if (null !== $choice && !\is_string($choice)) {
|
||||
throw new UnexpectedTypeException($choice, 'null or string');
|
||||
}
|
||||
|
||||
$choice = null;
|
||||
|
||||
foreach ($radios as $radio) {
|
||||
if ($radio->getData()) {
|
||||
if ('placeholder' === $radio->getName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$choice = $radio->getConfig()->getOption('value');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
vendor/symfony/form/Extension/Core/DataTransformer/ArrayToPartsTransformer.php
vendored
Normal file
84
vendor/symfony/form/Extension/Core/DataTransformer/ArrayToPartsTransformer.php
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ArrayToPartsTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $partMapping;
|
||||
|
||||
public function __construct(array $partMapping)
|
||||
{
|
||||
$this->partMapping = $partMapping;
|
||||
}
|
||||
|
||||
public function transform($array)
|
||||
{
|
||||
if (null === $array) {
|
||||
$array = [];
|
||||
}
|
||||
|
||||
if (!\is_array($array)) {
|
||||
throw new TransformationFailedException('Expected an array.');
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($this->partMapping as $partKey => $originalKeys) {
|
||||
if (empty($array)) {
|
||||
$result[$partKey] = null;
|
||||
} else {
|
||||
$result[$partKey] = array_intersect_key($array, array_flip($originalKeys));
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function reverseTransform($array)
|
||||
{
|
||||
if (!\is_array($array)) {
|
||||
throw new TransformationFailedException('Expected an array.');
|
||||
}
|
||||
|
||||
$result = [];
|
||||
$emptyKeys = [];
|
||||
|
||||
foreach ($this->partMapping as $partKey => $originalKeys) {
|
||||
if (!empty($array[$partKey])) {
|
||||
foreach ($originalKeys as $originalKey) {
|
||||
if (isset($array[$partKey][$originalKey])) {
|
||||
$result[$originalKey] = $array[$partKey][$originalKey];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$emptyKeys[] = $partKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (\count($emptyKeys) > 0) {
|
||||
if (\count($emptyKeys) === \count($this->partMapping)) {
|
||||
// All parts empty
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new TransformationFailedException(sprintf('The keys "%s" should not be empty.', implode('", "', $emptyKeys)));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
55
vendor/symfony/form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php
vendored
Normal file
55
vendor/symfony/form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\InvalidArgumentException;
|
||||
|
||||
abstract class BaseDateTimeTransformer implements DataTransformerInterface
|
||||
{
|
||||
protected static $formats = [
|
||||
\IntlDateFormatter::NONE,
|
||||
\IntlDateFormatter::FULL,
|
||||
\IntlDateFormatter::LONG,
|
||||
\IntlDateFormatter::MEDIUM,
|
||||
\IntlDateFormatter::SHORT,
|
||||
];
|
||||
|
||||
protected $inputTimezone;
|
||||
|
||||
protected $outputTimezone;
|
||||
|
||||
/**
|
||||
* @param string|null $inputTimezone The name of the input timezone
|
||||
* @param string|null $outputTimezone The name of the output timezone
|
||||
*
|
||||
* @throws InvalidArgumentException if a timezone is not valid
|
||||
*/
|
||||
public function __construct(string $inputTimezone = null, string $outputTimezone = null)
|
||||
{
|
||||
$this->inputTimezone = $inputTimezone ?: date_default_timezone_get();
|
||||
$this->outputTimezone = $outputTimezone ?: date_default_timezone_get();
|
||||
|
||||
// Check if input and output timezones are valid
|
||||
try {
|
||||
new \DateTimeZone($this->inputTimezone);
|
||||
} catch (\Exception $e) {
|
||||
throw new InvalidArgumentException(sprintf('Input timezone is invalid: "%s".', $this->inputTimezone), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
try {
|
||||
new \DateTimeZone($this->outputTimezone);
|
||||
} catch (\Exception $e) {
|
||||
throw new InvalidArgumentException(sprintf('Output timezone is invalid: "%s".', $this->outputTimezone), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
}
|
85
vendor/symfony/form/Extension/Core/DataTransformer/BooleanToStringTransformer.php
vendored
Normal file
85
vendor/symfony/form/Extension/Core/DataTransformer/BooleanToStringTransformer.php
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between a Boolean and a string.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||
*/
|
||||
class BooleanToStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $trueValue;
|
||||
|
||||
private $falseValues;
|
||||
|
||||
/**
|
||||
* @param string $trueValue The value emitted upon transform if the input is true
|
||||
*/
|
||||
public function __construct(string $trueValue, array $falseValues = [null])
|
||||
{
|
||||
$this->trueValue = $trueValue;
|
||||
$this->falseValues = $falseValues;
|
||||
if (\in_array($this->trueValue, $this->falseValues, true)) {
|
||||
throw new InvalidArgumentException('The specified "true" value is contained in the false-values.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a Boolean into a string.
|
||||
*
|
||||
* @param bool $value Boolean value
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not a Boolean
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\is_bool($value)) {
|
||||
throw new TransformationFailedException('Expected a Boolean.');
|
||||
}
|
||||
|
||||
return $value ? $this->trueValue : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a string into a Boolean.
|
||||
*
|
||||
* @param string $value String value
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not a string
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (\in_array($value, $this->falseValues, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!\is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a string.');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
53
vendor/symfony/form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php
vendored
Normal file
53
vendor/symfony/form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ChoiceToValueTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $choiceList;
|
||||
|
||||
public function __construct(ChoiceListInterface $choiceList)
|
||||
{
|
||||
$this->choiceList = $choiceList;
|
||||
}
|
||||
|
||||
public function transform($choice)
|
||||
{
|
||||
return (string) current($this->choiceList->getValuesForChoices([$choice]));
|
||||
}
|
||||
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null !== $value && !\is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a string or null.');
|
||||
}
|
||||
|
||||
$choices = $this->choiceList->getChoicesForValues([(string) $value]);
|
||||
|
||||
if (1 !== \count($choices)) {
|
||||
if (null === $value || '' === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new TransformationFailedException(sprintf('The choice "%s" does not exist or is not unique.', $value));
|
||||
}
|
||||
|
||||
return current($choices);
|
||||
}
|
||||
}
|
73
vendor/symfony/form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php
vendored
Normal file
73
vendor/symfony/form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ChoicesToValuesTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $choiceList;
|
||||
|
||||
public function __construct(ChoiceListInterface $choiceList)
|
||||
{
|
||||
$this->choiceList = $choiceList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not an array
|
||||
*/
|
||||
public function transform($array)
|
||||
{
|
||||
if (null === $array) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!\is_array($array)) {
|
||||
throw new TransformationFailedException('Expected an array.');
|
||||
}
|
||||
|
||||
return $this->choiceList->getValuesForChoices($array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not an array
|
||||
* or if no matching choice could be
|
||||
* found for some given value
|
||||
*/
|
||||
public function reverseTransform($array)
|
||||
{
|
||||
if (null === $array) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!\is_array($array)) {
|
||||
throw new TransformationFailedException('Expected an array.');
|
||||
}
|
||||
|
||||
$choices = $this->choiceList->getChoicesForValues($array);
|
||||
|
||||
if (\count($choices) !== \count($array)) {
|
||||
throw new TransformationFailedException('Could not find all matching choices for the given values.');
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
}
|
90
vendor/symfony/form/Extension/Core/DataTransformer/DataTransformerChain.php
vendored
Normal file
90
vendor/symfony/form/Extension/Core/DataTransformer/DataTransformerChain.php
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Passes a value through multiple value transformers.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class DataTransformerChain implements DataTransformerInterface
|
||||
{
|
||||
protected $transformers;
|
||||
|
||||
/**
|
||||
* Uses the given value transformers to transform values.
|
||||
*
|
||||
* @param DataTransformerInterface[] $transformers
|
||||
*/
|
||||
public function __construct(array $transformers)
|
||||
{
|
||||
$this->transformers = $transformers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes the value through the transform() method of all nested transformers.
|
||||
*
|
||||
* The transformers receive the value in the same order as they were passed
|
||||
* to the constructor. Each transformer receives the result of the previous
|
||||
* transformer as input. The output of the last transformer is returned
|
||||
* by this method.
|
||||
*
|
||||
* @param mixed $value The original value
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws TransformationFailedException
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
foreach ($this->transformers as $transformer) {
|
||||
$value = $transformer->transform($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes the value through the reverseTransform() method of all nested
|
||||
* transformers.
|
||||
*
|
||||
* The transformers receive the value in the reverse order as they were passed
|
||||
* to the constructor. Each transformer receives the result of the previous
|
||||
* transformer as input. The output of the last transformer is returned
|
||||
* by this method.
|
||||
*
|
||||
* @param mixed $value The transformed value
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws TransformationFailedException
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
for ($i = \count($this->transformers) - 1; $i >= 0; --$i) {
|
||||
$value = $this->transformers[$i]->reverseTransform($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataTransformerInterface[]
|
||||
*/
|
||||
public function getTransformers()
|
||||
{
|
||||
return $this->transformers;
|
||||
}
|
||||
}
|
171
vendor/symfony/form/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php
vendored
Normal file
171
vendor/symfony/form/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* Transforms between a normalized date interval and an interval string/array.
|
||||
*
|
||||
* @author Steffen Roßkamp <steffen.rosskamp@gimmickmedia.de>
|
||||
*/
|
||||
class DateIntervalToArrayTransformer implements DataTransformerInterface
|
||||
{
|
||||
public const YEARS = 'years';
|
||||
public const MONTHS = 'months';
|
||||
public const DAYS = 'days';
|
||||
public const HOURS = 'hours';
|
||||
public const MINUTES = 'minutes';
|
||||
public const SECONDS = 'seconds';
|
||||
public const INVERT = 'invert';
|
||||
|
||||
private const AVAILABLE_FIELDS = [
|
||||
self::YEARS => 'y',
|
||||
self::MONTHS => 'm',
|
||||
self::DAYS => 'd',
|
||||
self::HOURS => 'h',
|
||||
self::MINUTES => 'i',
|
||||
self::SECONDS => 's',
|
||||
self::INVERT => 'r',
|
||||
];
|
||||
private $fields;
|
||||
private $pad;
|
||||
|
||||
/**
|
||||
* @param string[]|null $fields The date fields
|
||||
* @param bool $pad Whether to use padding
|
||||
*/
|
||||
public function __construct(array $fields = null, bool $pad = false)
|
||||
{
|
||||
$this->fields = $fields ?? ['years', 'months', 'days', 'hours', 'minutes', 'seconds', 'invert'];
|
||||
$this->pad = $pad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a normalized date interval into an interval array.
|
||||
*
|
||||
* @param \DateInterval $dateInterval Normalized date interval
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws UnexpectedTypeException if the given value is not a \DateInterval instance
|
||||
*/
|
||||
public function transform($dateInterval)
|
||||
{
|
||||
if (null === $dateInterval) {
|
||||
return array_intersect_key(
|
||||
[
|
||||
'years' => '',
|
||||
'months' => '',
|
||||
'weeks' => '',
|
||||
'days' => '',
|
||||
'hours' => '',
|
||||
'minutes' => '',
|
||||
'seconds' => '',
|
||||
'invert' => false,
|
||||
],
|
||||
array_flip($this->fields)
|
||||
);
|
||||
}
|
||||
if (!$dateInterval instanceof \DateInterval) {
|
||||
throw new UnexpectedTypeException($dateInterval, \DateInterval::class);
|
||||
}
|
||||
$result = [];
|
||||
foreach (self::AVAILABLE_FIELDS as $field => $char) {
|
||||
$result[$field] = $dateInterval->format('%'.($this->pad ? strtoupper($char) : $char));
|
||||
}
|
||||
if (\in_array('weeks', $this->fields, true)) {
|
||||
$result['weeks'] = '0';
|
||||
if (isset($result['days']) && (int) $result['days'] >= 7) {
|
||||
$result['weeks'] = (string) floor($result['days'] / 7);
|
||||
$result['days'] = (string) ($result['days'] % 7);
|
||||
}
|
||||
}
|
||||
$result['invert'] = '-' === $result['invert'];
|
||||
$result = array_intersect_key($result, array_flip($this->fields));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an interval array into a normalized date interval.
|
||||
*
|
||||
* @param array $value Interval array
|
||||
*
|
||||
* @return \DateInterval|null
|
||||
*
|
||||
* @throws UnexpectedTypeException if the given value is not an array
|
||||
* @throws TransformationFailedException if the value could not be transformed
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
if (!\is_array($value)) {
|
||||
throw new UnexpectedTypeException($value, 'array');
|
||||
}
|
||||
if ('' === implode('', $value)) {
|
||||
return null;
|
||||
}
|
||||
$emptyFields = [];
|
||||
foreach ($this->fields as $field) {
|
||||
if (!isset($value[$field])) {
|
||||
$emptyFields[] = $field;
|
||||
}
|
||||
}
|
||||
if (\count($emptyFields) > 0) {
|
||||
throw new TransformationFailedException(sprintf('The fields "%s" should not be empty.', implode('", "', $emptyFields)));
|
||||
}
|
||||
if (isset($value['invert']) && !\is_bool($value['invert'])) {
|
||||
throw new TransformationFailedException('The value of "invert" must be boolean.');
|
||||
}
|
||||
foreach (self::AVAILABLE_FIELDS as $field => $char) {
|
||||
if ('invert' !== $field && isset($value[$field]) && !ctype_digit((string) $value[$field])) {
|
||||
throw new TransformationFailedException(sprintf('This amount of "%s" is invalid.', $field));
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (!empty($value['weeks'])) {
|
||||
$interval = sprintf(
|
||||
'P%sY%sM%sWT%sH%sM%sS',
|
||||
empty($value['years']) ? '0' : $value['years'],
|
||||
empty($value['months']) ? '0' : $value['months'],
|
||||
empty($value['weeks']) ? '0' : $value['weeks'],
|
||||
empty($value['hours']) ? '0' : $value['hours'],
|
||||
empty($value['minutes']) ? '0' : $value['minutes'],
|
||||
empty($value['seconds']) ? '0' : $value['seconds']
|
||||
);
|
||||
} else {
|
||||
$interval = sprintf(
|
||||
'P%sY%sM%sDT%sH%sM%sS',
|
||||
empty($value['years']) ? '0' : $value['years'],
|
||||
empty($value['months']) ? '0' : $value['months'],
|
||||
empty($value['days']) ? '0' : $value['days'],
|
||||
empty($value['hours']) ? '0' : $value['hours'],
|
||||
empty($value['minutes']) ? '0' : $value['minutes'],
|
||||
empty($value['seconds']) ? '0' : $value['seconds']
|
||||
);
|
||||
}
|
||||
$dateInterval = new \DateInterval($interval);
|
||||
if (isset($value['invert'])) {
|
||||
$dateInterval->invert = $value['invert'] ? 1 : 0;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $dateInterval;
|
||||
}
|
||||
}
|
101
vendor/symfony/form/Extension/Core/DataTransformer/DateIntervalToStringTransformer.php
vendored
Normal file
101
vendor/symfony/form/Extension/Core/DataTransformer/DateIntervalToStringTransformer.php
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* Transforms between a date string and a DateInterval object.
|
||||
*
|
||||
* @author Steffen Roßkamp <steffen.rosskamp@gimmickmedia.de>
|
||||
*/
|
||||
class DateIntervalToStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $format;
|
||||
|
||||
/**
|
||||
* Transforms a \DateInterval instance to a string.
|
||||
*
|
||||
* @see \DateInterval::format() for supported formats
|
||||
*
|
||||
* @param string $format The date format
|
||||
*/
|
||||
public function __construct(string $format = 'P%yY%mM%dDT%hH%iM%sS')
|
||||
{
|
||||
$this->format = $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a DateInterval object into a date string with the configured format.
|
||||
*
|
||||
* @param \DateInterval|null $value A DateInterval object
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws UnexpectedTypeException if the given value is not a \DateInterval instance
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
if (!$value instanceof \DateInterval) {
|
||||
throw new UnexpectedTypeException($value, \DateInterval::class);
|
||||
}
|
||||
|
||||
return $value->format($this->format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a date string in the configured format into a DateInterval object.
|
||||
*
|
||||
* @param string $value An ISO 8601 or date string like date interval presentation
|
||||
*
|
||||
* @return \DateInterval|null
|
||||
*
|
||||
* @throws UnexpectedTypeException if the given value is not a string
|
||||
* @throws TransformationFailedException if the date interval could not be parsed
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
if (!\is_string($value)) {
|
||||
throw new UnexpectedTypeException($value, 'string');
|
||||
}
|
||||
if ('' === $value) {
|
||||
return null;
|
||||
}
|
||||
if (!$this->isISO8601($value)) {
|
||||
throw new TransformationFailedException('Non ISO 8601 date strings are not supported yet.');
|
||||
}
|
||||
$valuePattern = '/^'.preg_replace('/%([yYmMdDhHiIsSwW])(\w)/', '(?P<$1>\d+)$2', $this->format).'$/';
|
||||
if (!preg_match($valuePattern, $value)) {
|
||||
throw new TransformationFailedException(sprintf('Value "%s" contains intervals not accepted by format "%s".', $value, $this->format));
|
||||
}
|
||||
try {
|
||||
$dateInterval = new \DateInterval($value);
|
||||
} catch (\Exception $e) {
|
||||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $dateInterval;
|
||||
}
|
||||
|
||||
private function isISO8601(string $string): bool
|
||||
{
|
||||
return preg_match('/^P(?=\w*(?:\d|%\w))(?:\d+Y|%[yY]Y)?(?:\d+M|%[mM]M)?(?:(?:\d+D|%[dD]D)|(?:\d+W|%[wW]W))?(?:T(?:\d+H|[hH]H)?(?:\d+M|[iI]M)?(?:\d+S|[sS]S)?)?$/', $string);
|
||||
}
|
||||
}
|
67
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php
vendored
Normal file
67
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between a DateTimeImmutable object and a DateTime object.
|
||||
*
|
||||
* @author Valentin Udaltsov <udaltsov.valentin@gmail.com>
|
||||
*/
|
||||
final class DateTimeImmutableToDateTimeTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* Transforms a DateTimeImmutable into a DateTime object.
|
||||
*
|
||||
* @param \DateTimeImmutable|null $value A DateTimeImmutable object
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a \DateTimeImmutable
|
||||
*/
|
||||
public function transform($value): ?\DateTime
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$value instanceof \DateTimeImmutable) {
|
||||
throw new TransformationFailedException('Expected a \DateTimeImmutable.');
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID >= 70300) {
|
||||
return \DateTime::createFromImmutable($value);
|
||||
}
|
||||
|
||||
return \DateTime::createFromFormat('U.u', $value->format('U.u'))->setTimezone($value->getTimezone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a DateTime object into a DateTimeImmutable object.
|
||||
*
|
||||
* @param \DateTime|null $value A DateTime object
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a \DateTime
|
||||
*/
|
||||
public function reverseTransform($value): ?\DateTimeImmutable
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$value instanceof \DateTime) {
|
||||
throw new TransformationFailedException('Expected a \DateTime.');
|
||||
}
|
||||
|
||||
return \DateTimeImmutable::createFromMutable($value);
|
||||
}
|
||||
}
|
184
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php
vendored
Normal file
184
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between a normalized time and a localized time string/array.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||
*/
|
||||
class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
||||
{
|
||||
private $pad;
|
||||
private $fields;
|
||||
private $referenceDate;
|
||||
|
||||
/**
|
||||
* @param string|null $inputTimezone The input timezone
|
||||
* @param string|null $outputTimezone The output timezone
|
||||
* @param string[]|null $fields The date fields
|
||||
* @param bool $pad Whether to use padding
|
||||
*/
|
||||
public function __construct(string $inputTimezone = null, string $outputTimezone = null, array $fields = null, bool $pad = false, \DateTimeInterface $referenceDate = null)
|
||||
{
|
||||
parent::__construct($inputTimezone, $outputTimezone);
|
||||
|
||||
$this->fields = $fields ?? ['year', 'month', 'day', 'hour', 'minute', 'second'];
|
||||
$this->pad = $pad;
|
||||
$this->referenceDate = $referenceDate ?? new \DateTimeImmutable('1970-01-01 00:00:00');
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a normalized date into a localized date.
|
||||
*
|
||||
* @param \DateTimeInterface $dateTime A DateTimeInterface object
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a \DateTimeInterface
|
||||
*/
|
||||
public function transform($dateTime)
|
||||
{
|
||||
if (null === $dateTime) {
|
||||
return array_intersect_key([
|
||||
'year' => '',
|
||||
'month' => '',
|
||||
'day' => '',
|
||||
'hour' => '',
|
||||
'minute' => '',
|
||||
'second' => '',
|
||||
], array_flip($this->fields));
|
||||
}
|
||||
|
||||
if (!$dateTime instanceof \DateTimeInterface) {
|
||||
throw new TransformationFailedException('Expected a \DateTimeInterface.');
|
||||
}
|
||||
|
||||
if ($this->inputTimezone !== $this->outputTimezone) {
|
||||
if (!$dateTime instanceof \DateTimeImmutable) {
|
||||
$dateTime = clone $dateTime;
|
||||
}
|
||||
|
||||
$dateTime = $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
|
||||
}
|
||||
|
||||
$result = array_intersect_key([
|
||||
'year' => $dateTime->format('Y'),
|
||||
'month' => $dateTime->format('m'),
|
||||
'day' => $dateTime->format('d'),
|
||||
'hour' => $dateTime->format('H'),
|
||||
'minute' => $dateTime->format('i'),
|
||||
'second' => $dateTime->format('s'),
|
||||
], array_flip($this->fields));
|
||||
|
||||
if (!$this->pad) {
|
||||
foreach ($result as &$entry) {
|
||||
// remove leading zeros
|
||||
$entry = (string) (int) $entry;
|
||||
}
|
||||
// unset reference to keep scope clear
|
||||
unset($entry);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a localized date into a normalized date.
|
||||
*
|
||||
* @param array $value Localized date
|
||||
*
|
||||
* @return \DateTime|null
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not an array,
|
||||
* if the value could not be transformed
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\is_array($value)) {
|
||||
throw new TransformationFailedException('Expected an array.');
|
||||
}
|
||||
|
||||
if ('' === implode('', $value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$emptyFields = [];
|
||||
|
||||
foreach ($this->fields as $field) {
|
||||
if (!isset($value[$field])) {
|
||||
$emptyFields[] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
if (\count($emptyFields) > 0) {
|
||||
throw new TransformationFailedException(sprintf('The fields "%s" should not be empty.', implode('", "', $emptyFields)));
|
||||
}
|
||||
|
||||
if (isset($value['month']) && !ctype_digit((string) $value['month'])) {
|
||||
throw new TransformationFailedException('This month is invalid.');
|
||||
}
|
||||
|
||||
if (isset($value['day']) && !ctype_digit((string) $value['day'])) {
|
||||
throw new TransformationFailedException('This day is invalid.');
|
||||
}
|
||||
|
||||
if (isset($value['year']) && !ctype_digit((string) $value['year'])) {
|
||||
throw new TransformationFailedException('This year is invalid.');
|
||||
}
|
||||
|
||||
if (!empty($value['month']) && !empty($value['day']) && !empty($value['year']) && false === checkdate($value['month'], $value['day'], $value['year'])) {
|
||||
throw new TransformationFailedException('This is an invalid date.');
|
||||
}
|
||||
|
||||
if (isset($value['hour']) && !ctype_digit((string) $value['hour'])) {
|
||||
throw new TransformationFailedException('This hour is invalid.');
|
||||
}
|
||||
|
||||
if (isset($value['minute']) && !ctype_digit((string) $value['minute'])) {
|
||||
throw new TransformationFailedException('This minute is invalid.');
|
||||
}
|
||||
|
||||
if (isset($value['second']) && !ctype_digit((string) $value['second'])) {
|
||||
throw new TransformationFailedException('This second is invalid.');
|
||||
}
|
||||
|
||||
try {
|
||||
$dateTime = new \DateTime(sprintf(
|
||||
'%s-%s-%s %s:%s:%s',
|
||||
empty($value['year']) ? $this->referenceDate->format('Y') : $value['year'],
|
||||
empty($value['month']) ? $this->referenceDate->format('m') : $value['month'],
|
||||
empty($value['day']) ? $this->referenceDate->format('d') : $value['day'],
|
||||
$value['hour'] ?? $this->referenceDate->format('H'),
|
||||
$value['minute'] ?? $this->referenceDate->format('i'),
|
||||
$value['second'] ?? $this->referenceDate->format('s')
|
||||
),
|
||||
new \DateTimeZone($this->outputTimezone)
|
||||
);
|
||||
|
||||
if ($this->inputTimezone !== $this->outputTimezone) {
|
||||
$dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $dateTime;
|
||||
}
|
||||
}
|
106
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToHtml5LocalDateTimeTransformer.php
vendored
Normal file
106
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToHtml5LocalDateTimeTransformer.php
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* @author Franz Wilding <franz.wilding@me.com>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Fred Cox <mcfedr@gmail.com>
|
||||
*/
|
||||
class DateTimeToHtml5LocalDateTimeTransformer extends BaseDateTimeTransformer
|
||||
{
|
||||
public const HTML5_FORMAT = 'Y-m-d\\TH:i:s';
|
||||
|
||||
/**
|
||||
* Transforms a \DateTime into a local date and time string.
|
||||
*
|
||||
* According to the HTML standard, the input string of a datetime-local
|
||||
* input is an RFC3339 date followed by 'T', followed by an RFC3339 time.
|
||||
* https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-local-date-and-time-string
|
||||
*
|
||||
* @param \DateTime|\DateTimeInterface $dateTime A DateTime object
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not an
|
||||
* instance of \DateTime or \DateTimeInterface
|
||||
*/
|
||||
public function transform($dateTime)
|
||||
{
|
||||
if (null === $dateTime) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!$dateTime instanceof \DateTime && !$dateTime instanceof \DateTimeInterface) {
|
||||
throw new TransformationFailedException('Expected a \DateTime or \DateTimeInterface.');
|
||||
}
|
||||
|
||||
if ($this->inputTimezone !== $this->outputTimezone) {
|
||||
if (!$dateTime instanceof \DateTimeImmutable) {
|
||||
$dateTime = clone $dateTime;
|
||||
}
|
||||
|
||||
$dateTime = $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
|
||||
}
|
||||
|
||||
return $dateTime->format(self::HTML5_FORMAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a local date and time string into a \DateTime.
|
||||
*
|
||||
* When transforming back to DateTime the regex is slightly laxer, taking into
|
||||
* account rules for parsing a local date and time string
|
||||
* https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-local-date-and-time-string
|
||||
*
|
||||
* @param string $dateTimeLocal Formatted string
|
||||
*
|
||||
* @return \DateTime|null
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a string,
|
||||
* if the value could not be transformed
|
||||
*/
|
||||
public function reverseTransform($dateTimeLocal)
|
||||
{
|
||||
if (!\is_string($dateTimeLocal)) {
|
||||
throw new TransformationFailedException('Expected a string.');
|
||||
}
|
||||
|
||||
if ('' === $dateTimeLocal) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// to maintain backwards compatibility we do not strictly validate the submitted date
|
||||
// see https://github.com/symfony/symfony/issues/28699
|
||||
if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})[T ]\d{2}:\d{2}(?::\d{2})?/', $dateTimeLocal, $matches)) {
|
||||
throw new TransformationFailedException(sprintf('The date "%s" is not a valid date.', $dateTimeLocal));
|
||||
}
|
||||
|
||||
try {
|
||||
$dateTime = new \DateTime($dateTimeLocal, new \DateTimeZone($this->outputTimezone));
|
||||
} catch (\Exception $e) {
|
||||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
if ($this->inputTimezone !== $dateTime->getTimezone()->getName()) {
|
||||
$dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
|
||||
}
|
||||
|
||||
if (!checkdate($matches[2], $matches[3], $matches[1])) {
|
||||
throw new TransformationFailedException(sprintf('The date "%s-%s-%s" is not a valid date.', $matches[1], $matches[2], $matches[3]));
|
||||
}
|
||||
|
||||
return $dateTime;
|
||||
}
|
||||
}
|
208
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php
vendored
Normal file
208
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* Transforms between a normalized time and a localized time string.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||
*/
|
||||
class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
||||
{
|
||||
private $dateFormat;
|
||||
private $timeFormat;
|
||||
private $pattern;
|
||||
private $calendar;
|
||||
|
||||
/**
|
||||
* @see BaseDateTimeTransformer::formats for available format options
|
||||
*
|
||||
* @param string|null $inputTimezone The name of the input timezone
|
||||
* @param string|null $outputTimezone The name of the output timezone
|
||||
* @param int|null $dateFormat The date format
|
||||
* @param int|null $timeFormat The time format
|
||||
* @param int $calendar One of the \IntlDateFormatter calendar constants
|
||||
* @param string|null $pattern A pattern to pass to \IntlDateFormatter
|
||||
*
|
||||
* @throws UnexpectedTypeException If a format is not supported or if a timezone is not a string
|
||||
*/
|
||||
public function __construct(string $inputTimezone = null, string $outputTimezone = null, int $dateFormat = null, int $timeFormat = null, int $calendar = \IntlDateFormatter::GREGORIAN, string $pattern = null)
|
||||
{
|
||||
parent::__construct($inputTimezone, $outputTimezone);
|
||||
|
||||
if (null === $dateFormat) {
|
||||
$dateFormat = \IntlDateFormatter::MEDIUM;
|
||||
}
|
||||
|
||||
if (null === $timeFormat) {
|
||||
$timeFormat = \IntlDateFormatter::SHORT;
|
||||
}
|
||||
|
||||
if (!\in_array($dateFormat, self::$formats, true)) {
|
||||
throw new UnexpectedTypeException($dateFormat, implode('", "', self::$formats));
|
||||
}
|
||||
|
||||
if (!\in_array($timeFormat, self::$formats, true)) {
|
||||
throw new UnexpectedTypeException($timeFormat, implode('", "', self::$formats));
|
||||
}
|
||||
|
||||
$this->dateFormat = $dateFormat;
|
||||
$this->timeFormat = $timeFormat;
|
||||
$this->calendar = $calendar;
|
||||
$this->pattern = $pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a normalized date into a localized date string/array.
|
||||
*
|
||||
* @param \DateTimeInterface $dateTime A DateTimeInterface object
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not a \DateTimeInterface
|
||||
* or if the date could not be transformed
|
||||
*/
|
||||
public function transform($dateTime)
|
||||
{
|
||||
if (null === $dateTime) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!$dateTime instanceof \DateTimeInterface) {
|
||||
throw new TransformationFailedException('Expected a \DateTimeInterface.');
|
||||
}
|
||||
|
||||
$value = $this->getIntlDateFormatter()->format($dateTime->getTimestamp());
|
||||
|
||||
if (0 != intl_get_error_code()) {
|
||||
throw new TransformationFailedException(intl_get_error_message());
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a localized date string/array into a normalized date.
|
||||
*
|
||||
* @param string|array $value Localized date string/array
|
||||
*
|
||||
* @return \DateTime|null
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not a string,
|
||||
* if the date could not be parsed
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (!\is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a string.');
|
||||
}
|
||||
|
||||
if ('' === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// date-only patterns require parsing to be done in UTC, as midnight might not exist in the local timezone due
|
||||
// to DST changes
|
||||
$dateOnly = $this->isPatternDateOnly();
|
||||
$dateFormatter = $this->getIntlDateFormatter($dateOnly);
|
||||
|
||||
try {
|
||||
$timestamp = @$dateFormatter->parse($value);
|
||||
} catch (\IntlException $e) {
|
||||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
if (0 != intl_get_error_code()) {
|
||||
throw new TransformationFailedException(intl_get_error_message(), intl_get_error_code());
|
||||
} elseif ($timestamp > 253402214400) {
|
||||
// This timestamp represents UTC midnight of 9999-12-31 to prevent 5+ digit years
|
||||
throw new TransformationFailedException('Years beyond 9999 are not supported.');
|
||||
} elseif (false === $timestamp) {
|
||||
// the value couldn't be parsed but the Intl extension didn't report an error code, this
|
||||
// could be the case when the Intl polyfill is used which always returns 0 as the error code
|
||||
throw new TransformationFailedException(sprintf('"%s" could not be parsed as a date.', $value));
|
||||
}
|
||||
|
||||
try {
|
||||
if ($dateOnly) {
|
||||
// we only care about year-month-date, which has been delivered as a timestamp pointing to UTC midnight
|
||||
$dateTime = new \DateTime(gmdate('Y-m-d', $timestamp), new \DateTimeZone($this->outputTimezone));
|
||||
} else {
|
||||
// read timestamp into DateTime object - the formatter delivers a timestamp
|
||||
$dateTime = new \DateTime(sprintf('@%s', $timestamp));
|
||||
}
|
||||
// set timezone separately, as it would be ignored if set via the constructor,
|
||||
// see https://php.net/datetime.construct
|
||||
$dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
|
||||
} catch (\Exception $e) {
|
||||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
if ($this->outputTimezone !== $this->inputTimezone) {
|
||||
$dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
|
||||
}
|
||||
|
||||
return $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a preconfigured IntlDateFormatter instance.
|
||||
*
|
||||
* @param bool $ignoreTimezone Use UTC regardless of the configured timezone
|
||||
*
|
||||
* @return \IntlDateFormatter
|
||||
*
|
||||
* @throws TransformationFailedException in case the date formatter cannot be constructed
|
||||
*/
|
||||
protected function getIntlDateFormatter(bool $ignoreTimezone = false)
|
||||
{
|
||||
$dateFormat = $this->dateFormat;
|
||||
$timeFormat = $this->timeFormat;
|
||||
$timezone = new \DateTimeZone($ignoreTimezone ? 'UTC' : $this->outputTimezone);
|
||||
|
||||
$calendar = $this->calendar;
|
||||
$pattern = $this->pattern;
|
||||
|
||||
$intlDateFormatter = new \IntlDateFormatter(\Locale::getDefault(), $dateFormat, $timeFormat, $timezone, $calendar, $pattern ?? '');
|
||||
|
||||
// new \intlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/66323
|
||||
if (!$intlDateFormatter) {
|
||||
throw new TransformationFailedException(intl_get_error_message(), intl_get_error_code());
|
||||
}
|
||||
|
||||
$intlDateFormatter->setLenient(false);
|
||||
|
||||
return $intlDateFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the pattern contains only a date.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isPatternDateOnly()
|
||||
{
|
||||
if (null === $this->pattern) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// strip escaped text
|
||||
$pattern = preg_replace("#'(.*?)'#", '', $this->pattern);
|
||||
|
||||
// check for the absence of time-related placeholders
|
||||
return 0 === preg_match('#[ahHkKmsSAzZOvVxX]#', $pattern);
|
||||
}
|
||||
}
|
91
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php
vendored
Normal file
91
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class DateTimeToRfc3339Transformer extends BaseDateTimeTransformer
|
||||
{
|
||||
/**
|
||||
* Transforms a normalized date into a localized date.
|
||||
*
|
||||
* @param \DateTimeInterface $dateTime A DateTimeInterface object
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a \DateTimeInterface
|
||||
*/
|
||||
public function transform($dateTime)
|
||||
{
|
||||
if (null === $dateTime) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!$dateTime instanceof \DateTimeInterface) {
|
||||
throw new TransformationFailedException('Expected a \DateTimeInterface.');
|
||||
}
|
||||
|
||||
if ($this->inputTimezone !== $this->outputTimezone) {
|
||||
if (!$dateTime instanceof \DateTimeImmutable) {
|
||||
$dateTime = clone $dateTime;
|
||||
}
|
||||
|
||||
$dateTime = $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
|
||||
}
|
||||
|
||||
return preg_replace('/\+00:00$/', 'Z', $dateTime->format('c'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a formatted string following RFC 3339 into a normalized date.
|
||||
*
|
||||
* @param string $rfc3339 Formatted string
|
||||
*
|
||||
* @return \DateTime|null
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a string,
|
||||
* if the value could not be transformed
|
||||
*/
|
||||
public function reverseTransform($rfc3339)
|
||||
{
|
||||
if (!\is_string($rfc3339)) {
|
||||
throw new TransformationFailedException('Expected a string.');
|
||||
}
|
||||
|
||||
if ('' === $rfc3339) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})T\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|(?:(?:\+|-)\d{2}:\d{2}))$/', $rfc3339, $matches)) {
|
||||
throw new TransformationFailedException(sprintf('The date "%s" is not a valid date.', $rfc3339));
|
||||
}
|
||||
|
||||
try {
|
||||
$dateTime = new \DateTime($rfc3339);
|
||||
} catch (\Exception $e) {
|
||||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
if ($this->inputTimezone !== $dateTime->getTimezone()->getName()) {
|
||||
$dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
|
||||
}
|
||||
|
||||
if (!checkdate($matches[2], $matches[3], $matches[1])) {
|
||||
throw new TransformationFailedException(sprintf('The date "%s-%s-%s" is not a valid date.', $matches[1], $matches[2], $matches[3]));
|
||||
}
|
||||
|
||||
return $dateTime;
|
||||
}
|
||||
}
|
138
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php
vendored
Normal file
138
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between a date string and a DateTime object.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||
*/
|
||||
class DateTimeToStringTransformer extends BaseDateTimeTransformer
|
||||
{
|
||||
/**
|
||||
* Format used for generating strings.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $generateFormat;
|
||||
|
||||
/**
|
||||
* Format used for parsing strings.
|
||||
*
|
||||
* Different than the {@link $generateFormat} because formats for parsing
|
||||
* support additional characters in PHP that are not supported for
|
||||
* generating strings.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $parseFormat;
|
||||
|
||||
/**
|
||||
* Transforms a \DateTime instance to a string.
|
||||
*
|
||||
* @see \DateTime::format() for supported formats
|
||||
*
|
||||
* @param string|null $inputTimezone The name of the input timezone
|
||||
* @param string|null $outputTimezone The name of the output timezone
|
||||
* @param string $format The date format
|
||||
*/
|
||||
public function __construct(string $inputTimezone = null, string $outputTimezone = null, string $format = 'Y-m-d H:i:s')
|
||||
{
|
||||
parent::__construct($inputTimezone, $outputTimezone);
|
||||
|
||||
$this->generateFormat = $this->parseFormat = $format;
|
||||
|
||||
// See https://php.net/datetime.createfromformat
|
||||
// The character "|" in the format makes sure that the parts of a date
|
||||
// that are *not* specified in the format are reset to the corresponding
|
||||
// values from 1970-01-01 00:00:00 instead of the current time.
|
||||
// Without "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 12:32:47",
|
||||
// where the time corresponds to the current server time.
|
||||
// With "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 00:00:00",
|
||||
// which is at least deterministic and thus used here.
|
||||
if (!str_contains($this->parseFormat, '|')) {
|
||||
$this->parseFormat .= '|';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a DateTime object into a date string with the configured format
|
||||
* and timezone.
|
||||
*
|
||||
* @param \DateTimeInterface $dateTime A DateTimeInterface object
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a \DateTimeInterface
|
||||
*/
|
||||
public function transform($dateTime)
|
||||
{
|
||||
if (null === $dateTime) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!$dateTime instanceof \DateTimeInterface) {
|
||||
throw new TransformationFailedException('Expected a \DateTimeInterface.');
|
||||
}
|
||||
|
||||
if (!$dateTime instanceof \DateTimeImmutable) {
|
||||
$dateTime = clone $dateTime;
|
||||
}
|
||||
|
||||
$dateTime = $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
|
||||
|
||||
return $dateTime->format($this->generateFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a date string in the configured timezone into a DateTime object.
|
||||
*
|
||||
* @param string $value A value as produced by PHP's date() function
|
||||
*
|
||||
* @return \DateTime|null
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a string,
|
||||
* or could not be transformed
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (empty($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a string.');
|
||||
}
|
||||
|
||||
$outputTz = new \DateTimeZone($this->outputTimezone);
|
||||
$dateTime = \DateTime::createFromFormat($this->parseFormat, $value, $outputTz);
|
||||
|
||||
$lastErrors = \DateTime::getLastErrors();
|
||||
|
||||
if (0 < $lastErrors['warning_count'] || 0 < $lastErrors['error_count']) {
|
||||
throw new TransformationFailedException(implode(', ', array_merge(array_values($lastErrors['warnings']), array_values($lastErrors['errors']))));
|
||||
}
|
||||
|
||||
try {
|
||||
if ($this->inputTimezone !== $this->outputTimezone) {
|
||||
$dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $dateTime;
|
||||
}
|
||||
}
|
80
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php
vendored
Normal file
80
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between a timestamp and a DateTime object.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||
*/
|
||||
class DateTimeToTimestampTransformer extends BaseDateTimeTransformer
|
||||
{
|
||||
/**
|
||||
* Transforms a DateTime object into a timestamp in the configured timezone.
|
||||
*
|
||||
* @param \DateTimeInterface $dateTime A DateTimeInterface object
|
||||
*
|
||||
* @return int|null
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a \DateTimeInterface
|
||||
*/
|
||||
public function transform($dateTime)
|
||||
{
|
||||
if (null === $dateTime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$dateTime instanceof \DateTimeInterface) {
|
||||
throw new TransformationFailedException('Expected a \DateTimeInterface.');
|
||||
}
|
||||
|
||||
return $dateTime->getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a timestamp in the configured timezone into a DateTime object.
|
||||
*
|
||||
* @param string $value A timestamp
|
||||
*
|
||||
* @return \DateTime|null
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a timestamp
|
||||
* or if the given timestamp is invalid
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!is_numeric($value)) {
|
||||
throw new TransformationFailedException('Expected a numeric.');
|
||||
}
|
||||
|
||||
try {
|
||||
$dateTime = new \DateTime();
|
||||
$dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
|
||||
$dateTime->setTimestamp($value);
|
||||
|
||||
if ($this->inputTimezone !== $this->outputTimezone) {
|
||||
$dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $dateTime;
|
||||
}
|
||||
}
|
82
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeZoneToStringTransformer.php
vendored
Normal file
82
vendor/symfony/form/Extension/Core/DataTransformer/DateTimeZoneToStringTransformer.php
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between a timezone identifier string and a DateTimeZone object.
|
||||
*
|
||||
* @author Roland Franssen <franssen.roland@gmail.com>
|
||||
*/
|
||||
class DateTimeZoneToStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $multiple;
|
||||
|
||||
public function __construct(bool $multiple = false)
|
||||
{
|
||||
$this->multiple = $multiple;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($dateTimeZone)
|
||||
{
|
||||
if (null === $dateTimeZone) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->multiple) {
|
||||
if (!\is_array($dateTimeZone)) {
|
||||
throw new TransformationFailedException('Expected an array of \DateTimeZone objects.');
|
||||
}
|
||||
|
||||
return array_map([new self(), 'transform'], $dateTimeZone);
|
||||
}
|
||||
|
||||
if (!$dateTimeZone instanceof \DateTimeZone) {
|
||||
throw new TransformationFailedException('Expected a \DateTimeZone object.');
|
||||
}
|
||||
|
||||
return $dateTimeZone->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->multiple) {
|
||||
if (!\is_array($value)) {
|
||||
throw new TransformationFailedException('Expected an array of timezone identifier strings.');
|
||||
}
|
||||
|
||||
return array_map([new self(), 'reverseTransform'], $value);
|
||||
}
|
||||
|
||||
if (!\is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a timezone identifier string.');
|
||||
}
|
||||
|
||||
try {
|
||||
return new \DateTimeZone($value);
|
||||
} catch (\Exception $e) {
|
||||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
}
|
59
vendor/symfony/form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php
vendored
Normal file
59
vendor/symfony/form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between an integer and a localized number with grouping
|
||||
* (each thousand) and comma separators.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class IntegerToLocalizedStringTransformer extends NumberToLocalizedStringTransformer
|
||||
{
|
||||
/**
|
||||
* Constructs a transformer.
|
||||
*
|
||||
* @param bool $grouping Whether thousands should be grouped
|
||||
* @param int $roundingMode One of the ROUND_ constants in this class
|
||||
* @param string|null $locale locale used for transforming
|
||||
*/
|
||||
public function __construct(?bool $grouping = false, ?int $roundingMode = \NumberFormatter::ROUND_DOWN, string $locale = null)
|
||||
{
|
||||
parent::__construct(0, $grouping, $roundingMode, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
$decimalSeparator = $this->getNumberFormatter()->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
|
||||
|
||||
if (\is_string($value) && str_contains($value, $decimalSeparator)) {
|
||||
throw new TransformationFailedException(sprintf('The value "%s" is not a valid integer.', $value));
|
||||
}
|
||||
|
||||
$result = parent::reverseTransform($value);
|
||||
|
||||
return null !== $result ? (int) $result : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function castParsedValue($value)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
84
vendor/symfony/form/Extension/Core/DataTransformer/IntlTimeZoneToStringTransformer.php
vendored
Normal file
84
vendor/symfony/form/Extension/Core/DataTransformer/IntlTimeZoneToStringTransformer.php
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between a timezone identifier string and a IntlTimeZone object.
|
||||
*
|
||||
* @author Roland Franssen <franssen.roland@gmail.com>
|
||||
*/
|
||||
class IntlTimeZoneToStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $multiple;
|
||||
|
||||
public function __construct(bool $multiple = false)
|
||||
{
|
||||
$this->multiple = $multiple;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($intlTimeZone)
|
||||
{
|
||||
if (null === $intlTimeZone) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->multiple) {
|
||||
if (!\is_array($intlTimeZone)) {
|
||||
throw new TransformationFailedException('Expected an array of \IntlTimeZone objects.');
|
||||
}
|
||||
|
||||
return array_map([new self(), 'transform'], $intlTimeZone);
|
||||
}
|
||||
|
||||
if (!$intlTimeZone instanceof \IntlTimeZone) {
|
||||
throw new TransformationFailedException('Expected a \IntlTimeZone object.');
|
||||
}
|
||||
|
||||
return $intlTimeZone->getID();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->multiple) {
|
||||
if (!\is_array($value)) {
|
||||
throw new TransformationFailedException('Expected an array of timezone identifier strings.');
|
||||
}
|
||||
|
||||
return array_map([new self(), 'reverseTransform'], $value);
|
||||
}
|
||||
|
||||
if (!\is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a timezone identifier string.');
|
||||
}
|
||||
|
||||
$intlTimeZone = \IntlTimeZone::createTimeZone($value);
|
||||
|
||||
if ('Etc/Unknown' === $intlTimeZone->getID()) {
|
||||
throw new TransformationFailedException(sprintf('Unknown timezone identifier "%s".', $value));
|
||||
}
|
||||
|
||||
return $intlTimeZone;
|
||||
}
|
||||
}
|
74
vendor/symfony/form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php
vendored
Normal file
74
vendor/symfony/form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between a normalized format and a localized money string.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||
*/
|
||||
class MoneyToLocalizedStringTransformer extends NumberToLocalizedStringTransformer
|
||||
{
|
||||
private $divisor;
|
||||
|
||||
public function __construct(?int $scale = 2, ?bool $grouping = true, ?int $roundingMode = \NumberFormatter::ROUND_HALFUP, ?int $divisor = 1, string $locale = null)
|
||||
{
|
||||
parent::__construct($scale ?? 2, $grouping ?? true, $roundingMode, $locale);
|
||||
|
||||
$this->divisor = $divisor ?? 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a normalized format into a localized money string.
|
||||
*
|
||||
* @param int|float|null $value Normalized number
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not numeric or
|
||||
* if the value cannot be transformed
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
if (null !== $value && 1 !== $this->divisor) {
|
||||
if (!is_numeric($value)) {
|
||||
throw new TransformationFailedException('Expected a numeric.');
|
||||
}
|
||||
$value /= $this->divisor;
|
||||
}
|
||||
|
||||
return parent::transform($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a localized money string into a normalized format.
|
||||
*
|
||||
* @param string $value Localized money string
|
||||
*
|
||||
* @return int|float|null
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not a string
|
||||
* or if the value cannot be transformed
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
$value = parent::reverseTransform($value);
|
||||
if (null !== $value && 1 !== $this->divisor) {
|
||||
$value = (float) (string) ($value * $this->divisor);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
265
vendor/symfony/form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
vendored
Normal file
265
vendor/symfony/form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between a number type and a localized number with grouping
|
||||
* (each thousand) and comma separators.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||
*/
|
||||
class NumberToLocalizedStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* @deprecated since Symfony 5.1, use \NumberFormatter::ROUND_CEILING instead.
|
||||
*/
|
||||
public const ROUND_CEILING = \NumberFormatter::ROUND_CEILING;
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 5.1, use \NumberFormatter::ROUND_FLOOR instead.
|
||||
*/
|
||||
public const ROUND_FLOOR = \NumberFormatter::ROUND_FLOOR;
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 5.1, use \NumberFormatter::ROUND_UP instead.
|
||||
*/
|
||||
public const ROUND_UP = \NumberFormatter::ROUND_UP;
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 5.1, use \NumberFormatter::ROUND_DOWN instead.
|
||||
*/
|
||||
public const ROUND_DOWN = \NumberFormatter::ROUND_DOWN;
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 5.1, use \NumberFormatter::ROUND_HALFEVEN instead.
|
||||
*/
|
||||
public const ROUND_HALF_EVEN = \NumberFormatter::ROUND_HALFEVEN;
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 5.1, use \NumberFormatter::ROUND_HALFUP instead.
|
||||
*/
|
||||
public const ROUND_HALF_UP = \NumberFormatter::ROUND_HALFUP;
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 5.1, use \NumberFormatter::ROUND_HALFDOWN instead.
|
||||
*/
|
||||
public const ROUND_HALF_DOWN = \NumberFormatter::ROUND_HALFDOWN;
|
||||
|
||||
protected $grouping;
|
||||
|
||||
protected $roundingMode;
|
||||
|
||||
private $scale;
|
||||
private $locale;
|
||||
|
||||
public function __construct(int $scale = null, ?bool $grouping = false, ?int $roundingMode = \NumberFormatter::ROUND_HALFUP, string $locale = null)
|
||||
{
|
||||
$this->scale = $scale;
|
||||
$this->grouping = $grouping ?? false;
|
||||
$this->roundingMode = $roundingMode ?? \NumberFormatter::ROUND_HALFUP;
|
||||
$this->locale = $locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a number type into localized number.
|
||||
*
|
||||
* @param int|float|null $value Number value
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not numeric
|
||||
* or if the value cannot be transformed
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!is_numeric($value)) {
|
||||
throw new TransformationFailedException('Expected a numeric.');
|
||||
}
|
||||
|
||||
$formatter = $this->getNumberFormatter();
|
||||
$value = $formatter->format($value);
|
||||
|
||||
if (intl_is_failure($formatter->getErrorCode())) {
|
||||
throw new TransformationFailedException($formatter->getErrorMessage());
|
||||
}
|
||||
|
||||
// Convert non-breaking and narrow non-breaking spaces to normal ones
|
||||
$value = str_replace(["\xc2\xa0", "\xe2\x80\xaf"], ' ', $value);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a localized number into an integer or float.
|
||||
*
|
||||
* @param string $value The localized value
|
||||
*
|
||||
* @return int|float|null
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not a string
|
||||
* or if the value cannot be transformed
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null !== $value && !\is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a string.');
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (\in_array($value, ['NaN', 'NAN', 'nan'], true)) {
|
||||
throw new TransformationFailedException('"NaN" is not a valid number.');
|
||||
}
|
||||
|
||||
$position = 0;
|
||||
$formatter = $this->getNumberFormatter();
|
||||
$groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
|
||||
$decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
|
||||
|
||||
if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) {
|
||||
$value = str_replace('.', $decSep, $value);
|
||||
}
|
||||
|
||||
if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) {
|
||||
$value = str_replace(',', $decSep, $value);
|
||||
}
|
||||
|
||||
if (str_contains($value, $decSep)) {
|
||||
$type = \NumberFormatter::TYPE_DOUBLE;
|
||||
} else {
|
||||
$type = \PHP_INT_SIZE === 8
|
||||
? \NumberFormatter::TYPE_INT64
|
||||
: \NumberFormatter::TYPE_INT32;
|
||||
}
|
||||
|
||||
$result = $formatter->parse($value, $type, $position);
|
||||
|
||||
if (intl_is_failure($formatter->getErrorCode())) {
|
||||
throw new TransformationFailedException($formatter->getErrorMessage());
|
||||
}
|
||||
|
||||
if ($result >= \PHP_INT_MAX || $result <= -\PHP_INT_MAX) {
|
||||
throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like.');
|
||||
}
|
||||
|
||||
$result = $this->castParsedValue($result);
|
||||
|
||||
if (false !== $encoding = mb_detect_encoding($value, null, true)) {
|
||||
$length = mb_strlen($value, $encoding);
|
||||
$remainder = mb_substr($value, $position, $length, $encoding);
|
||||
} else {
|
||||
$length = \strlen($value);
|
||||
$remainder = substr($value, $position, $length);
|
||||
}
|
||||
|
||||
// After parsing, position holds the index of the character where the
|
||||
// parsing stopped
|
||||
if ($position < $length) {
|
||||
// Check if there are unrecognized characters at the end of the
|
||||
// number (excluding whitespace characters)
|
||||
$remainder = trim($remainder, " \t\n\r\0\x0b\xc2\xa0");
|
||||
|
||||
if ('' !== $remainder) {
|
||||
throw new TransformationFailedException(sprintf('The number contains unrecognized characters: "%s".', $remainder));
|
||||
}
|
||||
}
|
||||
|
||||
// NumberFormatter::parse() does not round
|
||||
return $this->round($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a preconfigured \NumberFormatter instance.
|
||||
*
|
||||
* @return \NumberFormatter
|
||||
*/
|
||||
protected function getNumberFormatter()
|
||||
{
|
||||
$formatter = new \NumberFormatter($this->locale ?? \Locale::getDefault(), \NumberFormatter::DECIMAL);
|
||||
|
||||
if (null !== $this->scale) {
|
||||
$formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->scale);
|
||||
$formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $this->roundingMode);
|
||||
}
|
||||
|
||||
$formatter->setAttribute(\NumberFormatter::GROUPING_USED, $this->grouping);
|
||||
|
||||
return $formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function castParsedValue($value)
|
||||
{
|
||||
if (\is_int($value) && $value === (int) $float = (float) $value) {
|
||||
return $float;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds a number according to the configured scale and rounding mode.
|
||||
*
|
||||
* @param int|float $number A number
|
||||
*
|
||||
* @return int|float
|
||||
*/
|
||||
private function round($number)
|
||||
{
|
||||
if (null !== $this->scale && null !== $this->roundingMode) {
|
||||
// shift number to maintain the correct scale during rounding
|
||||
$roundingCoef = 10 ** $this->scale;
|
||||
// string representation to avoid rounding errors, similar to bcmul()
|
||||
$number = (string) ($number * $roundingCoef);
|
||||
|
||||
switch ($this->roundingMode) {
|
||||
case \NumberFormatter::ROUND_CEILING:
|
||||
$number = ceil($number);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_FLOOR:
|
||||
$number = floor($number);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_UP:
|
||||
$number = $number > 0 ? ceil($number) : floor($number);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_DOWN:
|
||||
$number = $number > 0 ? floor($number) : ceil($number);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_HALFEVEN:
|
||||
$number = round($number, 0, \PHP_ROUND_HALF_EVEN);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_HALFUP:
|
||||
$number = round($number, 0, \PHP_ROUND_HALF_UP);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_HALFDOWN:
|
||||
$number = round($number, 0, \PHP_ROUND_HALF_DOWN);
|
||||
break;
|
||||
}
|
||||
|
||||
$number = 1 === $roundingCoef ? (int) $number : $number / $roundingCoef;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
}
|
249
vendor/symfony/form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php
vendored
Normal file
249
vendor/symfony/form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php
vendored
Normal file
@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* Transforms between a normalized format (integer or float) and a percentage value.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||
*/
|
||||
class PercentToLocalizedStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
public const FRACTIONAL = 'fractional';
|
||||
public const INTEGER = 'integer';
|
||||
|
||||
protected static $types = [
|
||||
self::FRACTIONAL,
|
||||
self::INTEGER,
|
||||
];
|
||||
|
||||
private $roundingMode;
|
||||
private $type;
|
||||
private $scale;
|
||||
private $html5Format;
|
||||
|
||||
/**
|
||||
* @see self::$types for a list of supported types
|
||||
*
|
||||
* @param int|null $roundingMode A value from \NumberFormatter, such as \NumberFormatter::ROUND_HALFUP
|
||||
* @param bool $html5Format Use an HTML5 specific format, see https://www.w3.org/TR/html51/sec-forms.html#date-time-and-number-formats
|
||||
*
|
||||
* @throws UnexpectedTypeException if the given value of type is unknown
|
||||
*/
|
||||
public function __construct(int $scale = null, string $type = null, int $roundingMode = null, bool $html5Format = false)
|
||||
{
|
||||
if (null === $type) {
|
||||
$type = self::FRACTIONAL;
|
||||
}
|
||||
|
||||
if (null === $roundingMode && (\func_num_args() < 4 || func_get_arg(3))) {
|
||||
trigger_deprecation('symfony/form', '5.1', 'Not passing a rounding mode to "%s()" is deprecated. Starting with Symfony 6.0 it will default to "\NumberFormatter::ROUND_HALFUP".', __METHOD__);
|
||||
}
|
||||
|
||||
if (!\in_array($type, self::$types, true)) {
|
||||
throw new UnexpectedTypeException($type, implode('", "', self::$types));
|
||||
}
|
||||
|
||||
$this->type = $type;
|
||||
$this->scale = $scale ?? 0;
|
||||
$this->roundingMode = $roundingMode;
|
||||
$this->html5Format = $html5Format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms between a normalized format (integer or float) into a percentage value.
|
||||
*
|
||||
* @param int|float $value Normalized value
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not numeric or
|
||||
* if the value could not be transformed
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!is_numeric($value)) {
|
||||
throw new TransformationFailedException('Expected a numeric.');
|
||||
}
|
||||
|
||||
if (self::FRACTIONAL == $this->type) {
|
||||
$value *= 100;
|
||||
}
|
||||
|
||||
$formatter = $this->getNumberFormatter();
|
||||
$value = $formatter->format($value);
|
||||
|
||||
if (intl_is_failure($formatter->getErrorCode())) {
|
||||
throw new TransformationFailedException($formatter->getErrorMessage());
|
||||
}
|
||||
|
||||
// replace the UTF-8 non break spaces
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms between a percentage value into a normalized format (integer or float).
|
||||
*
|
||||
* @param string $value Percentage value
|
||||
*
|
||||
* @return int|float|null
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not a string or
|
||||
* if the value could not be transformed
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (!\is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a string.');
|
||||
}
|
||||
|
||||
if ('' === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$position = 0;
|
||||
$formatter = $this->getNumberFormatter();
|
||||
$groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
|
||||
$decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
|
||||
$grouping = $formatter->getAttribute(\NumberFormatter::GROUPING_USED);
|
||||
|
||||
if ('.' !== $decSep && (!$grouping || '.' !== $groupSep)) {
|
||||
$value = str_replace('.', $decSep, $value);
|
||||
}
|
||||
|
||||
if (',' !== $decSep && (!$grouping || ',' !== $groupSep)) {
|
||||
$value = str_replace(',', $decSep, $value);
|
||||
}
|
||||
|
||||
if (str_contains($value, $decSep)) {
|
||||
$type = \NumberFormatter::TYPE_DOUBLE;
|
||||
} else {
|
||||
$type = \PHP_INT_SIZE === 8 ? \NumberFormatter::TYPE_INT64 : \NumberFormatter::TYPE_INT32;
|
||||
}
|
||||
|
||||
// replace normal spaces so that the formatter can read them
|
||||
$result = $formatter->parse(str_replace(' ', "\xc2\xa0", $value), $type, $position);
|
||||
|
||||
if (intl_is_failure($formatter->getErrorCode())) {
|
||||
throw new TransformationFailedException($formatter->getErrorMessage());
|
||||
}
|
||||
|
||||
if (self::FRACTIONAL == $this->type) {
|
||||
$result /= 100;
|
||||
}
|
||||
|
||||
if (\function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($value, null, true)) {
|
||||
$length = mb_strlen($value, $encoding);
|
||||
$remainder = mb_substr($value, $position, $length, $encoding);
|
||||
} else {
|
||||
$length = \strlen($value);
|
||||
$remainder = substr($value, $position, $length);
|
||||
}
|
||||
|
||||
// After parsing, position holds the index of the character where the
|
||||
// parsing stopped
|
||||
if ($position < $length) {
|
||||
// Check if there are unrecognized characters at the end of the
|
||||
// number (excluding whitespace characters)
|
||||
$remainder = trim($remainder, " \t\n\r\0\x0b\xc2\xa0");
|
||||
|
||||
if ('' !== $remainder) {
|
||||
throw new TransformationFailedException(sprintf('The number contains unrecognized characters: "%s".', $remainder));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->round($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a preconfigured \NumberFormatter instance.
|
||||
*
|
||||
* @return \NumberFormatter
|
||||
*/
|
||||
protected function getNumberFormatter()
|
||||
{
|
||||
// Values used in HTML5 number inputs should be formatted as in "1234.5", ie. 'en' format without grouping,
|
||||
// according to https://www.w3.org/TR/html51/sec-forms.html#date-time-and-number-formats
|
||||
$formatter = new \NumberFormatter($this->html5Format ? 'en' : \Locale::getDefault(), \NumberFormatter::DECIMAL);
|
||||
|
||||
if ($this->html5Format) {
|
||||
$formatter->setAttribute(\NumberFormatter::GROUPING_USED, 0);
|
||||
}
|
||||
|
||||
$formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->scale);
|
||||
|
||||
if (null !== $this->roundingMode) {
|
||||
$formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $this->roundingMode);
|
||||
}
|
||||
|
||||
return $formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds a number according to the configured scale and rounding mode.
|
||||
*
|
||||
* @param int|float $number A number
|
||||
*
|
||||
* @return int|float
|
||||
*/
|
||||
private function round($number)
|
||||
{
|
||||
if (null !== $this->scale && null !== $this->roundingMode) {
|
||||
// shift number to maintain the correct scale during rounding
|
||||
$roundingCoef = 10 ** $this->scale;
|
||||
|
||||
if (self::FRACTIONAL == $this->type) {
|
||||
$roundingCoef *= 100;
|
||||
}
|
||||
|
||||
// string representation to avoid rounding errors, similar to bcmul()
|
||||
$number = (string) ($number * $roundingCoef);
|
||||
|
||||
switch ($this->roundingMode) {
|
||||
case \NumberFormatter::ROUND_CEILING:
|
||||
$number = ceil($number);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_FLOOR:
|
||||
$number = floor($number);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_UP:
|
||||
$number = $number > 0 ? ceil($number) : floor($number);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_DOWN:
|
||||
$number = $number > 0 ? floor($number) : ceil($number);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_HALFEVEN:
|
||||
$number = round($number, 0, \PHP_ROUND_HALF_EVEN);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_HALFUP:
|
||||
$number = round($number, 0, \PHP_ROUND_HALF_UP);
|
||||
break;
|
||||
case \NumberFormatter::ROUND_HALFDOWN:
|
||||
$number = round($number, 0, \PHP_ROUND_HALF_DOWN);
|
||||
break;
|
||||
}
|
||||
|
||||
$number = 1 === $roundingCoef ? (int) $number : $number / $roundingCoef;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
}
|
65
vendor/symfony/form/Extension/Core/DataTransformer/StringToFloatTransformer.php
vendored
Normal file
65
vendor/symfony/form/Extension/Core/DataTransformer/StringToFloatTransformer.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
class StringToFloatTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $scale;
|
||||
|
||||
public function __construct(int $scale = null)
|
||||
{
|
||||
$this->scale = $scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return float|null
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\is_string($value) || !is_numeric($value)) {
|
||||
throw new TransformationFailedException('Expected a numeric string.');
|
||||
}
|
||||
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\is_int($value) && !\is_float($value)) {
|
||||
throw new TransformationFailedException('Expected a numeric.');
|
||||
}
|
||||
|
||||
if ($this->scale > 0) {
|
||||
return number_format((float) $value, $this->scale, '.', '');
|
||||
}
|
||||
|
||||
return (string) $value;
|
||||
}
|
||||
}
|
75
vendor/symfony/form/Extension/Core/DataTransformer/UlidToStringTransformer.php
vendored
Normal file
75
vendor/symfony/form/Extension/Core/DataTransformer/UlidToStringTransformer.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Symfony\Component\Uid\Ulid;
|
||||
|
||||
/**
|
||||
* Transforms between a ULID string and a Ulid object.
|
||||
*
|
||||
* @author Pavel Dyakonov <wapinet@mail.ru>
|
||||
*/
|
||||
class UlidToStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* Transforms a Ulid object into a string.
|
||||
*
|
||||
* @param Ulid $value A Ulid object
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a Ulid object
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$value instanceof Ulid) {
|
||||
throw new TransformationFailedException('Expected a Ulid.');
|
||||
}
|
||||
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a ULID string into a Ulid object.
|
||||
*
|
||||
* @param string $value A ULID string
|
||||
*
|
||||
* @return Ulid|null
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a string,
|
||||
* or could not be transformed
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value || '' === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a string.');
|
||||
}
|
||||
|
||||
try {
|
||||
$ulid = new Ulid($value);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw new TransformationFailedException(sprintf('The value "%s" is not a valid ULID.', $value), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $ulid;
|
||||
}
|
||||
}
|
75
vendor/symfony/form/Extension/Core/DataTransformer/UuidToStringTransformer.php
vendored
Normal file
75
vendor/symfony/form/Extension/Core/DataTransformer/UuidToStringTransformer.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
/**
|
||||
* Transforms between a UUID string and a Uuid object.
|
||||
*
|
||||
* @author Pavel Dyakonov <wapinet@mail.ru>
|
||||
*/
|
||||
class UuidToStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* Transforms a Uuid object into a string.
|
||||
*
|
||||
* @param Uuid $value A Uuid object
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a Uuid object
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$value instanceof Uuid) {
|
||||
throw new TransformationFailedException('Expected a Uuid.');
|
||||
}
|
||||
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a UUID string into a Uuid object.
|
||||
*
|
||||
* @param string $value A UUID string
|
||||
*
|
||||
* @return Uuid|null
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a string,
|
||||
* or could not be transformed
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value || '' === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a string.');
|
||||
}
|
||||
|
||||
try {
|
||||
$uuid = new Uuid($value);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw new TransformationFailedException(sprintf('The value "%s" is not a valid UUID.', $value), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $uuid;
|
||||
}
|
||||
}
|
85
vendor/symfony/form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php
vendored
Normal file
85
vendor/symfony/form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ValueToDuplicatesTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $keys;
|
||||
|
||||
public function __construct(array $keys)
|
||||
{
|
||||
$this->keys = $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates the given value through the array.
|
||||
*
|
||||
* @param mixed $value The value
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->keys as $key) {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the duplicated value from an array.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws TransformationFailedException if the given value is not an array or
|
||||
* if the given array cannot be transformed
|
||||
*/
|
||||
public function reverseTransform($array)
|
||||
{
|
||||
if (!\is_array($array)) {
|
||||
throw new TransformationFailedException('Expected an array.');
|
||||
}
|
||||
|
||||
$result = current($array);
|
||||
$emptyKeys = [];
|
||||
|
||||
foreach ($this->keys as $key) {
|
||||
if (isset($array[$key]) && '' !== $array[$key] && false !== $array[$key] && [] !== $array[$key]) {
|
||||
if ($array[$key] !== $result) {
|
||||
throw new TransformationFailedException('All values in the array should be the same.');
|
||||
}
|
||||
} else {
|
||||
$emptyKeys[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if (\count($emptyKeys) > 0) {
|
||||
if (\count($emptyKeys) == \count($this->keys)) {
|
||||
// All keys empty
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new TransformationFailedException(sprintf('The keys "%s" should not be empty.', implode('", "', $emptyKeys)));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
105
vendor/symfony/form/Extension/Core/DataTransformer/WeekToArrayTransformer.php
vendored
Normal file
105
vendor/symfony/form/Extension/Core/DataTransformer/WeekToArrayTransformer.php
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between an ISO 8601 week date string and an array.
|
||||
*
|
||||
* @author Damien Fayet <damienf1521@gmail.com>
|
||||
*/
|
||||
class WeekToArrayTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* Transforms a string containing an ISO 8601 week date into an array.
|
||||
*
|
||||
* @param string|null $value A week date string
|
||||
*
|
||||
* @return array{year: int|null, week: int|null}
|
||||
*
|
||||
* @throws TransformationFailedException If the given value is not a string,
|
||||
* or if the given value does not follow the right format
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return ['year' => null, 'week' => null];
|
||||
}
|
||||
|
||||
if (!\is_string($value)) {
|
||||
throw new TransformationFailedException(sprintf('Value is expected to be a string but was "%s".', get_debug_type($value)));
|
||||
}
|
||||
|
||||
if (0 === preg_match('/^(?P<year>\d{4})-W(?P<week>\d{2})$/', $value, $matches)) {
|
||||
throw new TransformationFailedException('Given data does not follow the date format "Y-\WW".');
|
||||
}
|
||||
|
||||
return [
|
||||
'year' => (int) $matches['year'],
|
||||
'week' => (int) $matches['week'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an array into a week date string.
|
||||
*
|
||||
* @param array{year: int|null, week: int|null} $value
|
||||
*
|
||||
* @return string|null A week date string following the format Y-\WW
|
||||
*
|
||||
* @throws TransformationFailedException If the given value cannot be merged in a valid week date string,
|
||||
* or if the obtained week date does not exists
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value || [] === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\is_array($value)) {
|
||||
throw new TransformationFailedException(sprintf('Value is expected to be an array, but was "%s".', get_debug_type($value)));
|
||||
}
|
||||
|
||||
if (!\array_key_exists('year', $value)) {
|
||||
throw new TransformationFailedException('Key "year" is missing.');
|
||||
}
|
||||
|
||||
if (!\array_key_exists('week', $value)) {
|
||||
throw new TransformationFailedException('Key "week" is missing.');
|
||||
}
|
||||
|
||||
if ($additionalKeys = array_diff(array_keys($value), ['year', 'week'])) {
|
||||
throw new TransformationFailedException(sprintf('Expected only keys "year" and "week" to be present, but also got ["%s"].', implode('", "', $additionalKeys)));
|
||||
}
|
||||
|
||||
if (null === $value['year'] && null === $value['week']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\is_int($value['year'])) {
|
||||
throw new TransformationFailedException(sprintf('Year is expected to be an integer, but was "%s".', get_debug_type($value['year'])));
|
||||
}
|
||||
|
||||
if (!\is_int($value['week'])) {
|
||||
throw new TransformationFailedException(sprintf('Week is expected to be an integer, but was "%s".', get_debug_type($value['week'])));
|
||||
}
|
||||
|
||||
// The 28th December is always in the last week of the year
|
||||
if (date('W', strtotime('28th December '.$value['year'])) < $value['week']) {
|
||||
throw new TransformationFailedException(sprintf('Week "%d" does not exist for year "%d".', $value['week'], $value['year']));
|
||||
}
|
||||
|
||||
return sprintf('%d-W%02d', $value['year'], $value['week']);
|
||||
}
|
||||
}
|
48
vendor/symfony/form/Extension/Core/EventListener/FixUrlProtocolListener.php
vendored
Normal file
48
vendor/symfony/form/Extension/Core/EventListener/FixUrlProtocolListener.php
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
/**
|
||||
* Adds a protocol to a URL if it doesn't already have one.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FixUrlProtocolListener implements EventSubscriberInterface
|
||||
{
|
||||
private $defaultProtocol;
|
||||
|
||||
/**
|
||||
* @param string|null $defaultProtocol The URL scheme to add when there is none or null to not modify the data
|
||||
*/
|
||||
public function __construct(?string $defaultProtocol = 'http')
|
||||
{
|
||||
$this->defaultProtocol = $defaultProtocol;
|
||||
}
|
||||
|
||||
public function onSubmit(FormEvent $event)
|
||||
{
|
||||
$data = $event->getData();
|
||||
|
||||
if ($this->defaultProtocol && $data && \is_string($data) && !preg_match('~^(?:[/.]|[\w+.-]+://|[^:/?@#]++@)~', $data)) {
|
||||
$event->setData($this->defaultProtocol.'://'.$data);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [FormEvents::SUBMIT => 'onSubmit'];
|
||||
}
|
||||
}
|
113
vendor/symfony/form/Extension/Core/EventListener/MergeCollectionListener.php
vendored
Normal file
113
vendor/symfony/form/Extension/Core/EventListener/MergeCollectionListener.php
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class MergeCollectionListener implements EventSubscriberInterface
|
||||
{
|
||||
private $allowAdd;
|
||||
private $allowDelete;
|
||||
|
||||
/**
|
||||
* @param bool $allowAdd Whether values might be added to the collection
|
||||
* @param bool $allowDelete Whether values might be removed from the collection
|
||||
*/
|
||||
public function __construct(bool $allowAdd = false, bool $allowDelete = false)
|
||||
{
|
||||
$this->allowAdd = $allowAdd;
|
||||
$this->allowDelete = $allowDelete;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
FormEvents::SUBMIT => 'onSubmit',
|
||||
];
|
||||
}
|
||||
|
||||
public function onSubmit(FormEvent $event)
|
||||
{
|
||||
$dataToMergeInto = $event->getForm()->getNormData();
|
||||
$data = $event->getData();
|
||||
|
||||
if (null === $data) {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
if (!\is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) {
|
||||
throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)');
|
||||
}
|
||||
|
||||
if (null !== $dataToMergeInto && !\is_array($dataToMergeInto) && !($dataToMergeInto instanceof \Traversable && $dataToMergeInto instanceof \ArrayAccess)) {
|
||||
throw new UnexpectedTypeException($dataToMergeInto, 'array or (\Traversable and \ArrayAccess)');
|
||||
}
|
||||
|
||||
// If we are not allowed to change anything, return immediately
|
||||
if ($data === $dataToMergeInto || (!$this->allowAdd && !$this->allowDelete)) {
|
||||
$event->setData($dataToMergeInto);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $dataToMergeInto) {
|
||||
// No original data was set. Set it if allowed
|
||||
if ($this->allowAdd) {
|
||||
$dataToMergeInto = $data;
|
||||
}
|
||||
} else {
|
||||
// Calculate delta
|
||||
$itemsToAdd = \is_object($data) ? clone $data : $data;
|
||||
$itemsToDelete = [];
|
||||
|
||||
foreach ($dataToMergeInto as $beforeKey => $beforeItem) {
|
||||
foreach ($data as $afterKey => $afterItem) {
|
||||
if ($afterItem === $beforeItem) {
|
||||
// Item found, next original item
|
||||
unset($itemsToAdd[$afterKey]);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Item not found, remember for deletion
|
||||
$itemsToDelete[] = $beforeKey;
|
||||
}
|
||||
|
||||
// Remove deleted items before adding to free keys that are to be
|
||||
// replaced
|
||||
if ($this->allowDelete) {
|
||||
foreach ($itemsToDelete as $key) {
|
||||
unset($dataToMergeInto[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add remaining items
|
||||
if ($this->allowAdd) {
|
||||
foreach ($itemsToAdd as $key => $item) {
|
||||
if (!isset($dataToMergeInto[$key])) {
|
||||
$dataToMergeInto[$key] = $item;
|
||||
} else {
|
||||
$dataToMergeInto[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$event->setData($dataToMergeInto);
|
||||
}
|
||||
}
|
169
vendor/symfony/form/Extension/Core/EventListener/ResizeFormListener.php
vendored
Normal file
169
vendor/symfony/form/Extension/Core/EventListener/ResizeFormListener.php
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
||||
/**
|
||||
* Resize a collection form element based on the data sent from the client.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ResizeFormListener implements EventSubscriberInterface
|
||||
{
|
||||
protected $type;
|
||||
protected $options;
|
||||
protected $allowAdd;
|
||||
protected $allowDelete;
|
||||
|
||||
private $deleteEmpty;
|
||||
|
||||
/**
|
||||
* @param bool $allowAdd Whether children could be added to the group
|
||||
* @param bool $allowDelete Whether children could be removed from the group
|
||||
* @param bool|callable $deleteEmpty
|
||||
*/
|
||||
public function __construct(string $type, array $options = [], bool $allowAdd = false, bool $allowDelete = false, $deleteEmpty = false)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->allowAdd = $allowAdd;
|
||||
$this->allowDelete = $allowDelete;
|
||||
$this->options = $options;
|
||||
$this->deleteEmpty = $deleteEmpty;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
FormEvents::PRE_SET_DATA => 'preSetData',
|
||||
FormEvents::PRE_SUBMIT => 'preSubmit',
|
||||
// (MergeCollectionListener, MergeDoctrineCollectionListener)
|
||||
FormEvents::SUBMIT => ['onSubmit', 50],
|
||||
];
|
||||
}
|
||||
|
||||
public function preSetData(FormEvent $event)
|
||||
{
|
||||
$form = $event->getForm();
|
||||
$data = $event->getData();
|
||||
|
||||
if (null === $data) {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
if (!\is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) {
|
||||
throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)');
|
||||
}
|
||||
|
||||
// First remove all rows
|
||||
foreach ($form as $name => $child) {
|
||||
$form->remove($name);
|
||||
}
|
||||
|
||||
// Then add all rows again in the correct order
|
||||
foreach ($data as $name => $value) {
|
||||
$form->add($name, $this->type, array_replace([
|
||||
'property_path' => '['.$name.']',
|
||||
], $this->options));
|
||||
}
|
||||
}
|
||||
|
||||
public function preSubmit(FormEvent $event)
|
||||
{
|
||||
$form = $event->getForm();
|
||||
$data = $event->getData();
|
||||
|
||||
if (!\is_array($data)) {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
// Remove all empty rows
|
||||
if ($this->allowDelete) {
|
||||
foreach ($form as $name => $child) {
|
||||
if (!isset($data[$name])) {
|
||||
$form->remove($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add all additional rows
|
||||
if ($this->allowAdd) {
|
||||
foreach ($data as $name => $value) {
|
||||
if (!$form->has($name)) {
|
||||
$form->add($name, $this->type, array_replace([
|
||||
'property_path' => '['.$name.']',
|
||||
], $this->options));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onSubmit(FormEvent $event)
|
||||
{
|
||||
$form = $event->getForm();
|
||||
$data = $event->getData();
|
||||
|
||||
// At this point, $data is an array or an array-like object that already contains the
|
||||
// new entries, which were added by the data mapper. The data mapper ignores existing
|
||||
// entries, so we need to manually unset removed entries in the collection.
|
||||
|
||||
if (null === $data) {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
if (!\is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) {
|
||||
throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)');
|
||||
}
|
||||
|
||||
if ($this->deleteEmpty) {
|
||||
$previousData = $form->getData();
|
||||
/** @var FormInterface $child */
|
||||
foreach ($form as $name => $child) {
|
||||
if (!$child->isValid() || !$child->isSynchronized()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isNew = !isset($previousData[$name]);
|
||||
$isEmpty = \is_callable($this->deleteEmpty) ? ($this->deleteEmpty)($child->getData()) : $child->isEmpty();
|
||||
|
||||
// $isNew can only be true if allowAdd is true, so we don't
|
||||
// need to check allowAdd again
|
||||
if ($isEmpty && ($isNew || $this->allowDelete)) {
|
||||
unset($data[$name]);
|
||||
$form->remove($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The data mapper only adds, but does not remove items, so do this
|
||||
// here
|
||||
if ($this->allowDelete) {
|
||||
$toDelete = [];
|
||||
|
||||
foreach ($data as $name => $child) {
|
||||
if (!$form->has($name)) {
|
||||
$toDelete[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($toDelete as $name) {
|
||||
unset($data[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
$event->setData($data);
|
||||
}
|
||||
}
|
65
vendor/symfony/form/Extension/Core/EventListener/TransformationFailureListener.php
vendored
Normal file
65
vendor/symfony/form/Extension/Core/EventListener/TransformationFailureListener.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @author Christian Flothmann <christian.flothmann@sensiolabs.de>
|
||||
*/
|
||||
class TransformationFailureListener implements EventSubscriberInterface
|
||||
{
|
||||
private $translator;
|
||||
|
||||
public function __construct(TranslatorInterface $translator = null)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
FormEvents::POST_SUBMIT => ['convertTransformationFailureToFormError', -1024],
|
||||
];
|
||||
}
|
||||
|
||||
public function convertTransformationFailureToFormError(FormEvent $event)
|
||||
{
|
||||
$form = $event->getForm();
|
||||
|
||||
if (null === $form->getTransformationFailure() || !$form->isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($form as $child) {
|
||||
if (!$child->isSynchronized()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$clientDataAsString = is_scalar($form->getViewData()) ? (string) $form->getViewData() : get_debug_type($form->getViewData());
|
||||
$messageTemplate = $form->getConfig()->getOption('invalid_message', 'The value {{ value }} is not valid.');
|
||||
$messageParameters = array_replace(['{{ value }}' => $clientDataAsString], $form->getConfig()->getOption('invalid_message_parameters', []));
|
||||
|
||||
if (null !== $this->translator) {
|
||||
$message = $this->translator->trans($messageTemplate, $messageParameters);
|
||||
} else {
|
||||
$message = strtr($messageTemplate, $messageParameters);
|
||||
}
|
||||
|
||||
$form->addError(new FormError($message, $messageTemplate, $messageParameters, null, $form->getTransformationFailure()));
|
||||
}
|
||||
}
|
41
vendor/symfony/form/Extension/Core/EventListener/TrimListener.php
vendored
Normal file
41
vendor/symfony/form/Extension/Core/EventListener/TrimListener.php
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\Util\StringUtil;
|
||||
|
||||
/**
|
||||
* Trims string data.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class TrimListener implements EventSubscriberInterface
|
||||
{
|
||||
public function preSubmit(FormEvent $event)
|
||||
{
|
||||
$data = $event->getData();
|
||||
|
||||
if (!\is_string($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event->setData(StringUtil::trim($data));
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [FormEvents::PRE_SUBMIT => 'preSubmit'];
|
||||
}
|
||||
}
|
150
vendor/symfony/form/Extension/Core/Type/BaseType.php
vendored
Normal file
150
vendor/symfony/form/Extension/Core/Type/BaseType.php
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractRendererEngine;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Encapsulates common logic of {@link FormType} and {@link ButtonType}.
|
||||
*
|
||||
* This type does not appear in the form's type inheritance chain and as such
|
||||
* cannot be extended (via {@link \Symfony\Component\Form\FormExtensionInterface}) nor themed.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
abstract class BaseType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->setDisabled($options['disabled']);
|
||||
$builder->setAutoInitialize($options['auto_initialize']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$name = $form->getName();
|
||||
$blockName = $options['block_name'] ?: $form->getName();
|
||||
$translationDomain = $options['translation_domain'];
|
||||
$labelTranslationParameters = $options['label_translation_parameters'];
|
||||
$attrTranslationParameters = $options['attr_translation_parameters'];
|
||||
$labelFormat = $options['label_format'];
|
||||
|
||||
if ($view->parent) {
|
||||
if ('' !== ($parentFullName = $view->parent->vars['full_name'])) {
|
||||
$id = sprintf('%s_%s', $view->parent->vars['id'], $name);
|
||||
$fullName = sprintf('%s[%s]', $parentFullName, $name);
|
||||
$uniqueBlockPrefix = sprintf('%s_%s', $view->parent->vars['unique_block_prefix'], $blockName);
|
||||
} else {
|
||||
$id = $name;
|
||||
$fullName = $name;
|
||||
$uniqueBlockPrefix = '_'.$blockName;
|
||||
}
|
||||
|
||||
if (null === $translationDomain) {
|
||||
$translationDomain = $view->parent->vars['translation_domain'];
|
||||
}
|
||||
|
||||
$labelTranslationParameters = array_merge($view->parent->vars['label_translation_parameters'], $labelTranslationParameters);
|
||||
$attrTranslationParameters = array_merge($view->parent->vars['attr_translation_parameters'], $attrTranslationParameters);
|
||||
|
||||
if (!$labelFormat) {
|
||||
$labelFormat = $view->parent->vars['label_format'];
|
||||
}
|
||||
} else {
|
||||
$id = $name;
|
||||
$fullName = $name;
|
||||
$uniqueBlockPrefix = '_'.$blockName;
|
||||
|
||||
// Strip leading underscores and digits. These are allowed in
|
||||
// form names, but not in HTML4 ID attributes.
|
||||
// https://www.w3.org/TR/html401/struct/global#adef-id
|
||||
$id = ltrim($id, '_0123456789');
|
||||
}
|
||||
|
||||
$blockPrefixes = [];
|
||||
for ($type = $form->getConfig()->getType(); null !== $type; $type = $type->getParent()) {
|
||||
array_unshift($blockPrefixes, $type->getBlockPrefix());
|
||||
}
|
||||
if (null !== $options['block_prefix']) {
|
||||
$blockPrefixes[] = $options['block_prefix'];
|
||||
}
|
||||
$blockPrefixes[] = $uniqueBlockPrefix;
|
||||
|
||||
$view->vars = array_replace($view->vars, [
|
||||
'form' => $view,
|
||||
'id' => $id,
|
||||
'name' => $name,
|
||||
'full_name' => $fullName,
|
||||
'disabled' => $form->isDisabled(),
|
||||
'label' => $options['label'],
|
||||
'label_format' => $labelFormat,
|
||||
'label_html' => $options['label_html'],
|
||||
'multipart' => false,
|
||||
'attr' => $options['attr'],
|
||||
'block_prefixes' => $blockPrefixes,
|
||||
'unique_block_prefix' => $uniqueBlockPrefix,
|
||||
'row_attr' => $options['row_attr'],
|
||||
'translation_domain' => $translationDomain,
|
||||
'label_translation_parameters' => $labelTranslationParameters,
|
||||
'attr_translation_parameters' => $attrTranslationParameters,
|
||||
'priority' => $options['priority'],
|
||||
// Using the block name here speeds up performance in collection
|
||||
// forms, where each entry has the same full block name.
|
||||
// Including the type is important too, because if rows of a
|
||||
// collection form have different types (dynamically), they should
|
||||
// be rendered differently.
|
||||
// https://github.com/symfony/symfony/issues/5038
|
||||
AbstractRendererEngine::CACHE_KEY_VAR => $uniqueBlockPrefix.'_'.$form->getConfig()->getType()->getBlockPrefix(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'block_name' => null,
|
||||
'block_prefix' => null,
|
||||
'disabled' => false,
|
||||
'label' => null,
|
||||
'label_format' => null,
|
||||
'row_attr' => [],
|
||||
'label_html' => false,
|
||||
'label_translation_parameters' => [],
|
||||
'attr_translation_parameters' => [],
|
||||
'attr' => [],
|
||||
'translation_domain' => null,
|
||||
'auto_initialize' => true,
|
||||
'priority' => 0,
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('block_prefix', ['null', 'string']);
|
||||
$resolver->setAllowedTypes('attr', 'array');
|
||||
$resolver->setAllowedTypes('row_attr', 'array');
|
||||
$resolver->setAllowedTypes('label_html', 'bool');
|
||||
$resolver->setAllowedTypes('priority', 'int');
|
||||
|
||||
$resolver->setInfo('priority', 'The form rendering priority (higher priorities will be rendered first)');
|
||||
}
|
||||
}
|
52
vendor/symfony/form/Extension/Core/Type/BirthdayType.php
vendored
Normal file
52
vendor/symfony/form/Extension/Core/Type/BirthdayType.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class BirthdayType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'years' => range((int) date('Y') - 120, date('Y')),
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a valid birthdate.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('years', 'array');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return DateType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'birthday';
|
||||
}
|
||||
}
|
49
vendor/symfony/form/Extension/Core/Type/ButtonType.php
vendored
Normal file
49
vendor/symfony/form/Extension/Core/Type/ButtonType.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\ButtonTypeInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* A form button.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ButtonType extends BaseType implements ButtonTypeInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'button';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefault('auto_initialize', false);
|
||||
}
|
||||
}
|
84
vendor/symfony/form/Extension/Core/Type/CheckboxType.php
vendored
Normal file
84
vendor/symfony/form/Extension/Core/Type/CheckboxType.php
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\BooleanToStringTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class CheckboxType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
// Unlike in other types, where the data is NULL by default, it
|
||||
// needs to be a Boolean here. setData(null) is not acceptable
|
||||
// for checkboxes and radio buttons (unless a custom model
|
||||
// transformer handles this case).
|
||||
// We cannot solve this case via overriding the "data" option, because
|
||||
// doing so also calls setDataLocked(true).
|
||||
$builder->setData($options['data'] ?? false);
|
||||
$builder->addViewTransformer(new BooleanToStringTransformer($options['value'], $options['false_values']));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars = array_replace($view->vars, [
|
||||
'value' => $options['value'],
|
||||
'checked' => null !== $form->getViewData(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$emptyData = function (FormInterface $form, $viewData) {
|
||||
return $viewData;
|
||||
};
|
||||
|
||||
$resolver->setDefaults([
|
||||
'value' => '1',
|
||||
'empty_data' => $emptyData,
|
||||
'compound' => false,
|
||||
'false_values' => [null],
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'The checkbox has an invalid value.';
|
||||
},
|
||||
'is_empty_callback' => static function ($modelData): bool {
|
||||
return false === $modelData;
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('false_values', 'array');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'checkbox';
|
||||
}
|
||||
}
|
501
vendor/symfony/form/Extension/Core/Type/ChoiceType.php
vendored
Normal file
501
vendor/symfony/form/Extension/Core/Type/ChoiceType.php
vendored
Normal file
@ -0,0 +1,501 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceAttr;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceFieldName;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceFilter;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLabel;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLoader;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceTranslationParameters;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceValue;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\Cache\GroupBy;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\Cache\PreferredChoice;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
|
||||
use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator;
|
||||
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
|
||||
use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView;
|
||||
use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
|
||||
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Symfony\Component\Form\Extension\Core\DataMapper\CheckboxListMapper;
|
||||
use Symfony\Component\Form\Extension\Core\DataMapper\RadioListMapper;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\PropertyAccess\PropertyPath;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class ChoiceType extends AbstractType
|
||||
{
|
||||
private $choiceListFactory;
|
||||
private $translator;
|
||||
|
||||
/**
|
||||
* @param TranslatorInterface $translator
|
||||
*/
|
||||
public function __construct(ChoiceListFactoryInterface $choiceListFactory = null, $translator = null)
|
||||
{
|
||||
$this->choiceListFactory = $choiceListFactory ?? new CachingFactoryDecorator(
|
||||
new PropertyAccessDecorator(
|
||||
new DefaultChoiceListFactory()
|
||||
)
|
||||
);
|
||||
|
||||
if (null !== $translator && !$translator instanceof TranslatorInterface) {
|
||||
throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be han instance of "%s", "%s" given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
|
||||
}
|
||||
$this->translator = $translator;
|
||||
|
||||
// BC, to be removed in 6.0
|
||||
if ($this->choiceListFactory instanceof CachingFactoryDecorator) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ref = new \ReflectionMethod($this->choiceListFactory, 'createListFromChoices');
|
||||
|
||||
if ($ref->getNumberOfParameters() < 3) {
|
||||
trigger_deprecation('symfony/form', '5.1', 'Not defining a third parameter "callable|null $filter" in "%s::%s()" is deprecated.', $ref->class, $ref->name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$unknownValues = [];
|
||||
$choiceList = $this->createChoiceList($options);
|
||||
$builder->setAttribute('choice_list', $choiceList);
|
||||
|
||||
if ($options['expanded']) {
|
||||
$builder->setDataMapper($options['multiple'] ? new CheckboxListMapper() : new RadioListMapper());
|
||||
|
||||
// Initialize all choices before doing the index check below.
|
||||
// This helps in cases where index checks are optimized for non
|
||||
// initialized choice lists. For example, when using an SQL driver,
|
||||
// the index check would read in one SQL query and the initialization
|
||||
// requires another SQL query. When the initialization is done first,
|
||||
// one SQL query is sufficient.
|
||||
|
||||
$choiceListView = $this->createChoiceListView($choiceList, $options);
|
||||
$builder->setAttribute('choice_list_view', $choiceListView);
|
||||
|
||||
// Check if the choices already contain the empty value
|
||||
// Only add the placeholder option if this is not the case
|
||||
if (null !== $options['placeholder'] && 0 === \count($choiceList->getChoicesForValues(['']))) {
|
||||
$placeholderView = new ChoiceView(null, '', $options['placeholder']);
|
||||
|
||||
// "placeholder" is a reserved name
|
||||
$this->addSubForm($builder, 'placeholder', $placeholderView, $options);
|
||||
}
|
||||
|
||||
$this->addSubForms($builder, $choiceListView->preferredChoices, $options);
|
||||
$this->addSubForms($builder, $choiceListView->choices, $options);
|
||||
}
|
||||
|
||||
if ($options['expanded'] || $options['multiple']) {
|
||||
// Make sure that scalar, submitted values are converted to arrays
|
||||
// which can be submitted to the checkboxes/radio buttons
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($choiceList, $options, &$unknownValues) {
|
||||
$form = $event->getForm();
|
||||
$data = $event->getData();
|
||||
|
||||
// Since the type always use mapper an empty array will not be
|
||||
// considered as empty in Form::submit(), we need to evaluate
|
||||
// empty data here so its value is submitted to sub forms
|
||||
if (null === $data) {
|
||||
$emptyData = $form->getConfig()->getEmptyData();
|
||||
$data = $emptyData instanceof \Closure ? $emptyData($form, $data) : $emptyData;
|
||||
}
|
||||
|
||||
// Convert the submitted data to a string, if scalar, before
|
||||
// casting it to an array
|
||||
if (!\is_array($data)) {
|
||||
if ($options['multiple']) {
|
||||
throw new TransformationFailedException('Expected an array.');
|
||||
}
|
||||
|
||||
$data = (array) (string) $data;
|
||||
}
|
||||
|
||||
// A map from submitted values to integers
|
||||
$valueMap = array_flip($data);
|
||||
|
||||
// Make a copy of the value map to determine whether any unknown
|
||||
// values were submitted
|
||||
$unknownValues = $valueMap;
|
||||
|
||||
// Reconstruct the data as mapping from child names to values
|
||||
$knownValues = [];
|
||||
|
||||
if ($options['expanded']) {
|
||||
/** @var FormInterface $child */
|
||||
foreach ($form as $child) {
|
||||
$value = $child->getConfig()->getOption('value');
|
||||
|
||||
// Add the value to $data with the child's name as key
|
||||
if (isset($valueMap[$value])) {
|
||||
$knownValues[$child->getName()] = $value;
|
||||
unset($unknownValues[$value]);
|
||||
continue;
|
||||
} else {
|
||||
$knownValues[$child->getName()] = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($data as $value) {
|
||||
if ($choiceList->getChoicesForValues([$value])) {
|
||||
$knownValues[] = $value;
|
||||
unset($unknownValues[$value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The empty value is always known, independent of whether a
|
||||
// field exists for it or not
|
||||
unset($unknownValues['']);
|
||||
|
||||
// Throw exception if unknown values were submitted (multiple choices will be handled in a different event listener below)
|
||||
if (\count($unknownValues) > 0 && !$options['multiple']) {
|
||||
throw new TransformationFailedException(sprintf('The choices "%s" do not exist in the choice list.', implode('", "', array_keys($unknownValues))));
|
||||
}
|
||||
|
||||
$event->setData($knownValues);
|
||||
});
|
||||
}
|
||||
|
||||
if ($options['multiple']) {
|
||||
$messageTemplate = $options['invalid_message'] ?? 'The value {{ value }} is not valid.';
|
||||
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use (&$unknownValues, $messageTemplate) {
|
||||
// Throw exception if unknown values were submitted
|
||||
if (\count($unknownValues) > 0) {
|
||||
$form = $event->getForm();
|
||||
|
||||
$clientDataAsString = is_scalar($form->getViewData()) ? (string) $form->getViewData() : (\is_array($form->getViewData()) ? implode('", "', array_keys($unknownValues)) : \gettype($form->getViewData()));
|
||||
|
||||
if (null !== $this->translator) {
|
||||
$message = $this->translator->trans($messageTemplate, ['{{ value }}' => $clientDataAsString], 'validators');
|
||||
} else {
|
||||
$message = strtr($messageTemplate, ['{{ value }}' => $clientDataAsString]);
|
||||
}
|
||||
|
||||
$form->addError(new FormError($message, $messageTemplate, ['{{ value }}' => $clientDataAsString], null, new TransformationFailedException(sprintf('The choices "%s" do not exist in the choice list.', $clientDataAsString))));
|
||||
}
|
||||
});
|
||||
|
||||
// <select> tag with "multiple" option or list of checkbox inputs
|
||||
$builder->addViewTransformer(new ChoicesToValuesTransformer($choiceList));
|
||||
} else {
|
||||
// <select> tag without "multiple" option or list of radio inputs
|
||||
$builder->addViewTransformer(new ChoiceToValueTransformer($choiceList));
|
||||
}
|
||||
|
||||
if ($options['multiple'] && $options['by_reference']) {
|
||||
// Make sure the collection created during the client->norm
|
||||
// transformation is merged back into the original collection
|
||||
$builder->addEventSubscriber(new MergeCollectionListener(true, true));
|
||||
}
|
||||
|
||||
// To avoid issues when the submitted choices are arrays (i.e. array to string conversions),
|
||||
// we have to ensure that all elements of the submitted choice data are NULL, strings or ints.
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
|
||||
$data = $event->getData();
|
||||
|
||||
if (!\is_array($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($data as $v) {
|
||||
if (null !== $v && !\is_string($v) && !\is_int($v)) {
|
||||
throw new TransformationFailedException('All choices submitted must be NULL, strings or ints.');
|
||||
}
|
||||
}
|
||||
}, 256);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$choiceTranslationDomain = $options['choice_translation_domain'];
|
||||
if ($view->parent && null === $choiceTranslationDomain) {
|
||||
$choiceTranslationDomain = $view->vars['translation_domain'];
|
||||
}
|
||||
|
||||
/** @var ChoiceListInterface $choiceList */
|
||||
$choiceList = $form->getConfig()->getAttribute('choice_list');
|
||||
|
||||
/** @var ChoiceListView $choiceListView */
|
||||
$choiceListView = $form->getConfig()->hasAttribute('choice_list_view')
|
||||
? $form->getConfig()->getAttribute('choice_list_view')
|
||||
: $this->createChoiceListView($choiceList, $options);
|
||||
|
||||
$view->vars = array_replace($view->vars, [
|
||||
'multiple' => $options['multiple'],
|
||||
'expanded' => $options['expanded'],
|
||||
'preferred_choices' => $choiceListView->preferredChoices,
|
||||
'choices' => $choiceListView->choices,
|
||||
'separator' => '-------------------',
|
||||
'placeholder' => null,
|
||||
'choice_translation_domain' => $choiceTranslationDomain,
|
||||
'choice_translation_parameters' => $options['choice_translation_parameters'],
|
||||
]);
|
||||
|
||||
// The decision, whether a choice is selected, is potentially done
|
||||
// thousand of times during the rendering of a template. Provide a
|
||||
// closure here that is optimized for the value of the form, to
|
||||
// avoid making the type check inside the closure.
|
||||
if ($options['multiple']) {
|
||||
$view->vars['is_selected'] = function ($choice, array $values) {
|
||||
return \in_array($choice, $values, true);
|
||||
};
|
||||
} else {
|
||||
$view->vars['is_selected'] = function ($choice, $value) {
|
||||
return $choice === $value;
|
||||
};
|
||||
}
|
||||
|
||||
// Check if the choices already contain the empty value
|
||||
$view->vars['placeholder_in_choices'] = $choiceListView->hasPlaceholder();
|
||||
|
||||
// Only add the empty value option if this is not the case
|
||||
if (null !== $options['placeholder'] && !$view->vars['placeholder_in_choices']) {
|
||||
$view->vars['placeholder'] = $options['placeholder'];
|
||||
}
|
||||
|
||||
if ($options['multiple'] && !$options['expanded']) {
|
||||
// Add "[]" to the name in case a select tag with multiple options is
|
||||
// displayed. Otherwise only one of the selected options is sent in the
|
||||
// POST request.
|
||||
$view->vars['full_name'] .= '[]';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function finishView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
if ($options['expanded']) {
|
||||
// Radio buttons should have the same name as the parent
|
||||
$childName = $view->vars['full_name'];
|
||||
|
||||
// Checkboxes should append "[]" to allow multiple selection
|
||||
if ($options['multiple']) {
|
||||
$childName .= '[]';
|
||||
}
|
||||
|
||||
foreach ($view as $childView) {
|
||||
$childView->vars['full_name'] = $childName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$emptyData = function (Options $options) {
|
||||
if ($options['expanded'] && !$options['multiple']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($options['multiple']) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
$placeholderDefault = function (Options $options) {
|
||||
return $options['required'] ? null : '';
|
||||
};
|
||||
|
||||
$placeholderNormalizer = function (Options $options, $placeholder) {
|
||||
if ($options['multiple']) {
|
||||
// never use an empty value for this case
|
||||
return null;
|
||||
} elseif ($options['required'] && ($options['expanded'] || isset($options['attr']['size']) && $options['attr']['size'] > 1)) {
|
||||
// placeholder for required radio buttons or a select with size > 1 does not make sense
|
||||
return null;
|
||||
} elseif (false === $placeholder) {
|
||||
// an empty value should be added but the user decided otherwise
|
||||
return null;
|
||||
} elseif ($options['expanded'] && '' === $placeholder) {
|
||||
// never use an empty label for radio buttons
|
||||
return 'None';
|
||||
}
|
||||
|
||||
// empty value has been set explicitly
|
||||
return $placeholder;
|
||||
};
|
||||
|
||||
$compound = function (Options $options) {
|
||||
return $options['expanded'];
|
||||
};
|
||||
|
||||
$choiceTranslationDomainNormalizer = function (Options $options, $choiceTranslationDomain) {
|
||||
if (true === $choiceTranslationDomain) {
|
||||
return $options['translation_domain'];
|
||||
}
|
||||
|
||||
return $choiceTranslationDomain;
|
||||
};
|
||||
|
||||
$resolver->setDefaults([
|
||||
'multiple' => false,
|
||||
'expanded' => false,
|
||||
'choices' => [],
|
||||
'choice_filter' => null,
|
||||
'choice_loader' => null,
|
||||
'choice_label' => null,
|
||||
'choice_name' => null,
|
||||
'choice_value' => null,
|
||||
'choice_attr' => null,
|
||||
'choice_translation_parameters' => [],
|
||||
'preferred_choices' => [],
|
||||
'group_by' => null,
|
||||
'empty_data' => $emptyData,
|
||||
'placeholder' => $placeholderDefault,
|
||||
'error_bubbling' => false,
|
||||
'compound' => $compound,
|
||||
// The view data is always a string or an array of strings,
|
||||
// even if the "data" option is manually set to an object.
|
||||
// See https://github.com/symfony/symfony/pull/5582
|
||||
'data_class' => null,
|
||||
'choice_translation_domain' => true,
|
||||
'trim' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'The selected choice is invalid.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setNormalizer('placeholder', $placeholderNormalizer);
|
||||
$resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer);
|
||||
|
||||
$resolver->setAllowedTypes('choices', ['null', 'array', \Traversable::class]);
|
||||
$resolver->setAllowedTypes('choice_translation_domain', ['null', 'bool', 'string']);
|
||||
$resolver->setAllowedTypes('choice_loader', ['null', ChoiceLoaderInterface::class, ChoiceLoader::class]);
|
||||
$resolver->setAllowedTypes('choice_filter', ['null', 'callable', 'string', PropertyPath::class, ChoiceFilter::class]);
|
||||
$resolver->setAllowedTypes('choice_label', ['null', 'bool', 'callable', 'string', PropertyPath::class, ChoiceLabel::class]);
|
||||
$resolver->setAllowedTypes('choice_name', ['null', 'callable', 'string', PropertyPath::class, ChoiceFieldName::class]);
|
||||
$resolver->setAllowedTypes('choice_value', ['null', 'callable', 'string', PropertyPath::class, ChoiceValue::class]);
|
||||
$resolver->setAllowedTypes('choice_attr', ['null', 'array', 'callable', 'string', PropertyPath::class, ChoiceAttr::class]);
|
||||
$resolver->setAllowedTypes('choice_translation_parameters', ['null', 'array', 'callable', ChoiceTranslationParameters::class]);
|
||||
$resolver->setAllowedTypes('preferred_choices', ['array', \Traversable::class, 'callable', 'string', PropertyPath::class, PreferredChoice::class]);
|
||||
$resolver->setAllowedTypes('group_by', ['null', 'callable', 'string', PropertyPath::class, GroupBy::class]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'choice';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the sub fields for an expanded choice field.
|
||||
*/
|
||||
private function addSubForms(FormBuilderInterface $builder, array $choiceViews, array $options)
|
||||
{
|
||||
foreach ($choiceViews as $name => $choiceView) {
|
||||
// Flatten groups
|
||||
if (\is_array($choiceView)) {
|
||||
$this->addSubForms($builder, $choiceView, $options);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($choiceView instanceof ChoiceGroupView) {
|
||||
$this->addSubForms($builder, $choiceView->choices, $options);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->addSubForm($builder, $name, $choiceView, $options);
|
||||
}
|
||||
}
|
||||
|
||||
private function addSubForm(FormBuilderInterface $builder, string $name, ChoiceView $choiceView, array $options)
|
||||
{
|
||||
$choiceOpts = [
|
||||
'value' => $choiceView->value,
|
||||
'label' => $choiceView->label,
|
||||
'label_html' => $options['label_html'],
|
||||
'attr' => $choiceView->attr,
|
||||
'label_translation_parameters' => $choiceView->labelTranslationParameters,
|
||||
'translation_domain' => $options['choice_translation_domain'],
|
||||
'block_name' => 'entry',
|
||||
];
|
||||
|
||||
if ($options['multiple']) {
|
||||
$choiceType = CheckboxType::class;
|
||||
// The user can check 0 or more checkboxes. If required
|
||||
// is true, they are required to check all of them.
|
||||
$choiceOpts['required'] = false;
|
||||
} else {
|
||||
$choiceType = RadioType::class;
|
||||
}
|
||||
|
||||
$builder->add($name, $choiceType, $choiceOpts);
|
||||
}
|
||||
|
||||
private function createChoiceList(array $options)
|
||||
{
|
||||
if (null !== $options['choice_loader']) {
|
||||
return $this->choiceListFactory->createListFromLoader(
|
||||
$options['choice_loader'],
|
||||
$options['choice_value'],
|
||||
$options['choice_filter']
|
||||
);
|
||||
}
|
||||
|
||||
// Harden against NULL values (like in EntityType and ModelType)
|
||||
$choices = null !== $options['choices'] ? $options['choices'] : [];
|
||||
|
||||
return $this->choiceListFactory->createListFromChoices(
|
||||
$choices,
|
||||
$options['choice_value'],
|
||||
$options['choice_filter']
|
||||
);
|
||||
}
|
||||
|
||||
private function createChoiceListView(ChoiceListInterface $choiceList, array $options)
|
||||
{
|
||||
return $this->choiceListFactory->createView(
|
||||
$choiceList,
|
||||
$options['preferred_choices'],
|
||||
$options['choice_label'],
|
||||
$options['choice_name'],
|
||||
$options['group_by'],
|
||||
$options['choice_attr'],
|
||||
$options['choice_translation_parameters']
|
||||
);
|
||||
}
|
||||
}
|
142
vendor/symfony/form/Extension/Core/Type/CollectionType.php
vendored
Normal file
142
vendor/symfony/form/Extension/Core/Type/CollectionType.php
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class CollectionType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
if ($options['allow_add'] && $options['prototype']) {
|
||||
$prototypeOptions = array_replace([
|
||||
'required' => $options['required'],
|
||||
'label' => $options['prototype_name'].'label__',
|
||||
], $options['entry_options']);
|
||||
|
||||
if (null !== $options['prototype_data']) {
|
||||
$prototypeOptions['data'] = $options['prototype_data'];
|
||||
}
|
||||
|
||||
$prototype = $builder->create($options['prototype_name'], $options['entry_type'], $prototypeOptions);
|
||||
$builder->setAttribute('prototype', $prototype->getForm());
|
||||
}
|
||||
|
||||
$resizeListener = new ResizeFormListener(
|
||||
$options['entry_type'],
|
||||
$options['entry_options'],
|
||||
$options['allow_add'],
|
||||
$options['allow_delete'],
|
||||
$options['delete_empty']
|
||||
);
|
||||
|
||||
$builder->addEventSubscriber($resizeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars = array_replace($view->vars, [
|
||||
'allow_add' => $options['allow_add'],
|
||||
'allow_delete' => $options['allow_delete'],
|
||||
]);
|
||||
|
||||
if ($form->getConfig()->hasAttribute('prototype')) {
|
||||
$prototype = $form->getConfig()->getAttribute('prototype');
|
||||
$view->vars['prototype'] = $prototype->setParent($form)->createView($view);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function finishView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$prefixOffset = -2;
|
||||
// check if the entry type also defines a block prefix
|
||||
/** @var FormInterface $entry */
|
||||
foreach ($form as $entry) {
|
||||
if ($entry->getConfig()->getOption('block_prefix')) {
|
||||
--$prefixOffset;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($view as $entryView) {
|
||||
array_splice($entryView->vars['block_prefixes'], $prefixOffset, 0, 'collection_entry');
|
||||
}
|
||||
|
||||
/** @var FormInterface $prototype */
|
||||
if ($prototype = $form->getConfig()->getAttribute('prototype')) {
|
||||
if ($view->vars['prototype']->vars['multipart']) {
|
||||
$view->vars['multipart'] = true;
|
||||
}
|
||||
|
||||
if ($prefixOffset > -3 && $prototype->getConfig()->getOption('block_prefix')) {
|
||||
--$prefixOffset;
|
||||
}
|
||||
|
||||
array_splice($view->vars['prototype']->vars['block_prefixes'], $prefixOffset, 0, 'collection_entry');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$entryOptionsNormalizer = function (Options $options, $value) {
|
||||
$value['block_name'] = 'entry';
|
||||
|
||||
return $value;
|
||||
};
|
||||
|
||||
$resolver->setDefaults([
|
||||
'allow_add' => false,
|
||||
'allow_delete' => false,
|
||||
'prototype' => true,
|
||||
'prototype_data' => null,
|
||||
'prototype_name' => '__name__',
|
||||
'entry_type' => TextType::class,
|
||||
'entry_options' => [],
|
||||
'delete_empty' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'The collection is invalid.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setNormalizer('entry_options', $entryOptionsNormalizer);
|
||||
$resolver->setAllowedTypes('delete_empty', ['bool', 'callable']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'collection';
|
||||
}
|
||||
}
|
98
vendor/symfony/form/Extension/Core/Type/ColorType.php
vendored
Normal file
98
vendor/symfony/form/Extension/Core/Type/ColorType.php
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class ColorType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @see https://www.w3.org/TR/html52/sec-forms.html#color-state-typecolor
|
||||
*/
|
||||
private const HTML5_PATTERN = '/^#[0-9a-f]{6}$/i';
|
||||
|
||||
private $translator;
|
||||
|
||||
public function __construct(TranslatorInterface $translator = null)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
if (!$options['html5']) {
|
||||
return;
|
||||
}
|
||||
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event): void {
|
||||
$value = $event->getData();
|
||||
if (null === $value || '' === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (\is_string($value) && preg_match(self::HTML5_PATTERN, $value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$messageTemplate = 'This value is not a valid HTML5 color.';
|
||||
$messageParameters = [
|
||||
'{{ value }}' => is_scalar($value) ? (string) $value : \gettype($value),
|
||||
];
|
||||
$message = $this->translator ? $this->translator->trans($messageTemplate, $messageParameters, 'validators') : $messageTemplate;
|
||||
|
||||
$event->getForm()->addError(new FormError($message, $messageTemplate, $messageParameters));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'html5' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please select a valid color.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('html5', 'bool');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'color';
|
||||
}
|
||||
}
|
72
vendor/symfony/form/Extension/Core/Type/CountryType.php
vendored
Normal file
72
vendor/symfony/form/Extension/Core/Type/CountryType.php
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\ChoiceList\ChoiceList;
|
||||
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Intl\Countries;
|
||||
use Symfony\Component\Intl\Intl;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class CountryType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'choice_loader' => function (Options $options) {
|
||||
if (!class_exists(Intl::class)) {
|
||||
throw new LogicException(sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class));
|
||||
}
|
||||
|
||||
$choiceTranslationLocale = $options['choice_translation_locale'];
|
||||
$alpha3 = $options['alpha3'];
|
||||
|
||||
return ChoiceList::loader($this, new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale, $alpha3) {
|
||||
return array_flip($alpha3 ? Countries::getAlpha3Names($choiceTranslationLocale) : Countries::getNames($choiceTranslationLocale));
|
||||
}), [$choiceTranslationLocale, $alpha3]);
|
||||
},
|
||||
'choice_translation_domain' => false,
|
||||
'choice_translation_locale' => null,
|
||||
'alpha3' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please select a valid country.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('choice_translation_locale', ['null', 'string']);
|
||||
$resolver->setAllowedTypes('alpha3', 'bool');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'country';
|
||||
}
|
||||
}
|
69
vendor/symfony/form/Extension/Core/Type/CurrencyType.php
vendored
Normal file
69
vendor/symfony/form/Extension/Core/Type/CurrencyType.php
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\ChoiceList\ChoiceList;
|
||||
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Intl\Currencies;
|
||||
use Symfony\Component\Intl\Intl;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class CurrencyType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'choice_loader' => function (Options $options) {
|
||||
if (!class_exists(Intl::class)) {
|
||||
throw new LogicException(sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class));
|
||||
}
|
||||
|
||||
$choiceTranslationLocale = $options['choice_translation_locale'];
|
||||
|
||||
return ChoiceList::loader($this, new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale) {
|
||||
return array_flip(Currencies::getNames($choiceTranslationLocale));
|
||||
}), $choiceTranslationLocale);
|
||||
},
|
||||
'choice_translation_domain' => false,
|
||||
'choice_translation_locale' => null,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please select a valid currency.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('choice_translation_locale', ['null', 'string']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'currency';
|
||||
}
|
||||
}
|
291
vendor/symfony/form/Extension/Core/Type/DateIntervalType.php
vendored
Normal file
291
vendor/symfony/form/Extension/Core/Type/DateIntervalType.php
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Exception\InvalidConfigurationException;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateIntervalToArrayTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateIntervalToStringTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\IntegerToLocalizedStringTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\ReversedTransformer;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @author Steffen Roßkamp <steffen.rosskamp@gimmickmedia.de>
|
||||
*/
|
||||
class DateIntervalType extends AbstractType
|
||||
{
|
||||
private const TIME_PARTS = [
|
||||
'years',
|
||||
'months',
|
||||
'weeks',
|
||||
'days',
|
||||
'hours',
|
||||
'minutes',
|
||||
'seconds',
|
||||
];
|
||||
private const WIDGETS = [
|
||||
'text' => TextType::class,
|
||||
'integer' => IntegerType::class,
|
||||
'choice' => ChoiceType::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
if (!$options['with_years'] && !$options['with_months'] && !$options['with_weeks'] && !$options['with_days'] && !$options['with_hours'] && !$options['with_minutes'] && !$options['with_seconds']) {
|
||||
throw new InvalidConfigurationException('You must enable at least one interval field.');
|
||||
}
|
||||
if ($options['with_invert'] && 'single_text' === $options['widget']) {
|
||||
throw new InvalidConfigurationException('The single_text widget does not support invertible intervals.');
|
||||
}
|
||||
if ($options['with_weeks'] && $options['with_days']) {
|
||||
throw new InvalidConfigurationException('You cannot enable weeks and days fields together.');
|
||||
}
|
||||
$format = 'P';
|
||||
$parts = [];
|
||||
if ($options['with_years']) {
|
||||
$format .= '%yY';
|
||||
$parts[] = 'years';
|
||||
}
|
||||
if ($options['with_months']) {
|
||||
$format .= '%mM';
|
||||
$parts[] = 'months';
|
||||
}
|
||||
if ($options['with_weeks']) {
|
||||
$format .= '%wW';
|
||||
$parts[] = 'weeks';
|
||||
}
|
||||
if ($options['with_days']) {
|
||||
$format .= '%dD';
|
||||
$parts[] = 'days';
|
||||
}
|
||||
if ($options['with_hours'] || $options['with_minutes'] || $options['with_seconds']) {
|
||||
$format .= 'T';
|
||||
}
|
||||
if ($options['with_hours']) {
|
||||
$format .= '%hH';
|
||||
$parts[] = 'hours';
|
||||
}
|
||||
if ($options['with_minutes']) {
|
||||
$format .= '%iM';
|
||||
$parts[] = 'minutes';
|
||||
}
|
||||
if ($options['with_seconds']) {
|
||||
$format .= '%sS';
|
||||
$parts[] = 'seconds';
|
||||
}
|
||||
if ($options['with_invert']) {
|
||||
$parts[] = 'invert';
|
||||
}
|
||||
if ('single_text' === $options['widget']) {
|
||||
$builder->addViewTransformer(new DateIntervalToStringTransformer($format));
|
||||
} else {
|
||||
foreach (self::TIME_PARTS as $part) {
|
||||
if ($options['with_'.$part]) {
|
||||
$childOptions = [
|
||||
'error_bubbling' => true,
|
||||
'label' => $options['labels'][$part],
|
||||
// Append generic carry-along options
|
||||
'required' => $options['required'],
|
||||
'translation_domain' => $options['translation_domain'],
|
||||
// when compound the array entries are ignored, we need to cascade the configuration here
|
||||
'empty_data' => $options['empty_data'][$part] ?? null,
|
||||
];
|
||||
if ('choice' === $options['widget']) {
|
||||
$childOptions['choice_translation_domain'] = false;
|
||||
$childOptions['choices'] = $options[$part];
|
||||
$childOptions['placeholder'] = $options['placeholder'][$part];
|
||||
}
|
||||
$childForm = $builder->create($part, self::WIDGETS[$options['widget']], $childOptions);
|
||||
if ('integer' === $options['widget']) {
|
||||
$childForm->addModelTransformer(
|
||||
new ReversedTransformer(
|
||||
new IntegerToLocalizedStringTransformer()
|
||||
)
|
||||
);
|
||||
}
|
||||
$builder->add($childForm);
|
||||
}
|
||||
}
|
||||
if ($options['with_invert']) {
|
||||
$builder->add('invert', CheckboxType::class, [
|
||||
'label' => $options['labels']['invert'],
|
||||
'error_bubbling' => true,
|
||||
'required' => false,
|
||||
'translation_domain' => $options['translation_domain'],
|
||||
]);
|
||||
}
|
||||
$builder->addViewTransformer(new DateIntervalToArrayTransformer($parts, 'text' === $options['widget']));
|
||||
}
|
||||
if ('string' === $options['input']) {
|
||||
$builder->addModelTransformer(
|
||||
new ReversedTransformer(
|
||||
new DateIntervalToStringTransformer($format)
|
||||
)
|
||||
);
|
||||
} elseif ('array' === $options['input']) {
|
||||
$builder->addModelTransformer(
|
||||
new ReversedTransformer(
|
||||
new DateIntervalToArrayTransformer($parts)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$vars = [
|
||||
'widget' => $options['widget'],
|
||||
'with_invert' => $options['with_invert'],
|
||||
];
|
||||
foreach (self::TIME_PARTS as $part) {
|
||||
$vars['with_'.$part] = $options['with_'.$part];
|
||||
}
|
||||
$view->vars = array_replace($view->vars, $vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$compound = function (Options $options) {
|
||||
return 'single_text' !== $options['widget'];
|
||||
};
|
||||
$emptyData = function (Options $options) {
|
||||
return 'single_text' === $options['widget'] ? '' : [];
|
||||
};
|
||||
|
||||
$placeholderDefault = function (Options $options) {
|
||||
return $options['required'] ? null : '';
|
||||
};
|
||||
|
||||
$placeholderNormalizer = function (Options $options, $placeholder) use ($placeholderDefault) {
|
||||
if (\is_array($placeholder)) {
|
||||
$default = $placeholderDefault($options);
|
||||
|
||||
return array_merge(array_fill_keys(self::TIME_PARTS, $default), $placeholder);
|
||||
}
|
||||
|
||||
return array_fill_keys(self::TIME_PARTS, $placeholder);
|
||||
};
|
||||
|
||||
$labelsNormalizer = function (Options $options, array $labels) {
|
||||
return array_replace([
|
||||
'years' => null,
|
||||
'months' => null,
|
||||
'days' => null,
|
||||
'weeks' => null,
|
||||
'hours' => null,
|
||||
'minutes' => null,
|
||||
'seconds' => null,
|
||||
'invert' => 'Negative interval',
|
||||
], array_filter($labels, function ($label) {
|
||||
return null !== $label;
|
||||
}));
|
||||
};
|
||||
|
||||
$resolver->setDefaults([
|
||||
'with_years' => true,
|
||||
'with_months' => true,
|
||||
'with_days' => true,
|
||||
'with_weeks' => false,
|
||||
'with_hours' => false,
|
||||
'with_minutes' => false,
|
||||
'with_seconds' => false,
|
||||
'with_invert' => false,
|
||||
'years' => range(0, 100),
|
||||
'months' => range(0, 12),
|
||||
'weeks' => range(0, 52),
|
||||
'days' => range(0, 31),
|
||||
'hours' => range(0, 24),
|
||||
'minutes' => range(0, 60),
|
||||
'seconds' => range(0, 60),
|
||||
'widget' => 'choice',
|
||||
'input' => 'dateinterval',
|
||||
'placeholder' => $placeholderDefault,
|
||||
'by_reference' => true,
|
||||
'error_bubbling' => false,
|
||||
// If initialized with a \DateInterval object, FormType initializes
|
||||
// this option to "\DateInterval". Since the internal, normalized
|
||||
// representation is not \DateInterval, but an array, we need to unset
|
||||
// this option.
|
||||
'data_class' => null,
|
||||
'compound' => $compound,
|
||||
'empty_data' => $emptyData,
|
||||
'labels' => [],
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please choose a valid date interval.';
|
||||
},
|
||||
]);
|
||||
$resolver->setNormalizer('placeholder', $placeholderNormalizer);
|
||||
$resolver->setNormalizer('labels', $labelsNormalizer);
|
||||
|
||||
$resolver->setAllowedValues(
|
||||
'input',
|
||||
[
|
||||
'dateinterval',
|
||||
'string',
|
||||
'array',
|
||||
]
|
||||
);
|
||||
$resolver->setAllowedValues(
|
||||
'widget',
|
||||
[
|
||||
'single_text',
|
||||
'text',
|
||||
'integer',
|
||||
'choice',
|
||||
]
|
||||
);
|
||||
// Don't clone \DateInterval classes, as i.e. format()
|
||||
// does not work after that
|
||||
$resolver->setAllowedValues('by_reference', true);
|
||||
|
||||
$resolver->setAllowedTypes('years', 'array');
|
||||
$resolver->setAllowedTypes('months', 'array');
|
||||
$resolver->setAllowedTypes('weeks', 'array');
|
||||
$resolver->setAllowedTypes('days', 'array');
|
||||
$resolver->setAllowedTypes('hours', 'array');
|
||||
$resolver->setAllowedTypes('minutes', 'array');
|
||||
$resolver->setAllowedTypes('seconds', 'array');
|
||||
$resolver->setAllowedTypes('with_years', 'bool');
|
||||
$resolver->setAllowedTypes('with_months', 'bool');
|
||||
$resolver->setAllowedTypes('with_weeks', 'bool');
|
||||
$resolver->setAllowedTypes('with_days', 'bool');
|
||||
$resolver->setAllowedTypes('with_hours', 'bool');
|
||||
$resolver->setAllowedTypes('with_minutes', 'bool');
|
||||
$resolver->setAllowedTypes('with_seconds', 'bool');
|
||||
$resolver->setAllowedTypes('with_invert', 'bool');
|
||||
$resolver->setAllowedTypes('labels', 'array');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'dateinterval';
|
||||
}
|
||||
}
|
362
vendor/symfony/form/Extension/Core/Type/DateTimeType.php
vendored
Normal file
362
vendor/symfony/form/Extension/Core/Type/DateTimeType.php
vendored
Normal file
@ -0,0 +1,362 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToHtml5LocalDateTimeTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\ReversedTransformer;
|
||||
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class DateTimeType extends AbstractType
|
||||
{
|
||||
public const DEFAULT_DATE_FORMAT = \IntlDateFormatter::MEDIUM;
|
||||
public const DEFAULT_TIME_FORMAT = \IntlDateFormatter::MEDIUM;
|
||||
|
||||
/**
|
||||
* The HTML5 datetime-local format as defined in
|
||||
* http://w3c.github.io/html-reference/datatypes.html#form.data.datetime-local.
|
||||
*/
|
||||
public const HTML5_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
|
||||
|
||||
private const ACCEPTED_FORMATS = [
|
||||
\IntlDateFormatter::FULL,
|
||||
\IntlDateFormatter::LONG,
|
||||
\IntlDateFormatter::MEDIUM,
|
||||
\IntlDateFormatter::SHORT,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$parts = ['year', 'month', 'day', 'hour'];
|
||||
$dateParts = ['year', 'month', 'day'];
|
||||
$timeParts = ['hour'];
|
||||
|
||||
if ($options['with_minutes']) {
|
||||
$parts[] = 'minute';
|
||||
$timeParts[] = 'minute';
|
||||
}
|
||||
|
||||
if ($options['with_seconds']) {
|
||||
$parts[] = 'second';
|
||||
$timeParts[] = 'second';
|
||||
}
|
||||
|
||||
$dateFormat = \is_int($options['date_format']) ? $options['date_format'] : self::DEFAULT_DATE_FORMAT;
|
||||
$timeFormat = self::DEFAULT_TIME_FORMAT;
|
||||
$calendar = \IntlDateFormatter::GREGORIAN;
|
||||
$pattern = \is_string($options['format']) ? $options['format'] : null;
|
||||
|
||||
if (!\in_array($dateFormat, self::ACCEPTED_FORMATS, true)) {
|
||||
throw new InvalidOptionsException('The "date_format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format.');
|
||||
}
|
||||
|
||||
if ('single_text' === $options['widget']) {
|
||||
if (self::HTML5_FORMAT === $pattern) {
|
||||
$builder->addViewTransformer(new DateTimeToHtml5LocalDateTimeTransformer(
|
||||
$options['model_timezone'],
|
||||
$options['view_timezone']
|
||||
));
|
||||
} else {
|
||||
$builder->addViewTransformer(new DateTimeToLocalizedStringTransformer(
|
||||
$options['model_timezone'],
|
||||
$options['view_timezone'],
|
||||
$dateFormat,
|
||||
$timeFormat,
|
||||
$calendar,
|
||||
$pattern
|
||||
));
|
||||
}
|
||||
} else {
|
||||
// when the form is compound the entries of the array are ignored in favor of children data
|
||||
// so we need to handle the cascade setting here
|
||||
$emptyData = $builder->getEmptyData() ?: [];
|
||||
// Only pass a subset of the options to children
|
||||
$dateOptions = array_intersect_key($options, array_flip([
|
||||
'years',
|
||||
'months',
|
||||
'days',
|
||||
'placeholder',
|
||||
'choice_translation_domain',
|
||||
'required',
|
||||
'translation_domain',
|
||||
'html5',
|
||||
'invalid_message',
|
||||
'invalid_message_parameters',
|
||||
]));
|
||||
|
||||
if ($emptyData instanceof \Closure) {
|
||||
$lazyEmptyData = static function ($option) use ($emptyData) {
|
||||
return static function (FormInterface $form) use ($emptyData, $option) {
|
||||
$emptyData = $emptyData($form->getParent());
|
||||
|
||||
return $emptyData[$option] ?? '';
|
||||
};
|
||||
};
|
||||
|
||||
$dateOptions['empty_data'] = $lazyEmptyData('date');
|
||||
} elseif (isset($emptyData['date'])) {
|
||||
$dateOptions['empty_data'] = $emptyData['date'];
|
||||
}
|
||||
|
||||
$timeOptions = array_intersect_key($options, array_flip([
|
||||
'hours',
|
||||
'minutes',
|
||||
'seconds',
|
||||
'with_minutes',
|
||||
'with_seconds',
|
||||
'placeholder',
|
||||
'choice_translation_domain',
|
||||
'required',
|
||||
'translation_domain',
|
||||
'html5',
|
||||
'invalid_message',
|
||||
'invalid_message_parameters',
|
||||
]));
|
||||
|
||||
if ($emptyData instanceof \Closure) {
|
||||
$timeOptions['empty_data'] = $lazyEmptyData('time');
|
||||
} elseif (isset($emptyData['time'])) {
|
||||
$timeOptions['empty_data'] = $emptyData['time'];
|
||||
}
|
||||
|
||||
if (false === $options['label']) {
|
||||
$dateOptions['label'] = false;
|
||||
$timeOptions['label'] = false;
|
||||
}
|
||||
|
||||
if (null !== $options['date_widget']) {
|
||||
$dateOptions['widget'] = $options['date_widget'];
|
||||
}
|
||||
|
||||
if (null !== $options['date_label']) {
|
||||
$dateOptions['label'] = $options['date_label'];
|
||||
}
|
||||
|
||||
if (null !== $options['time_widget']) {
|
||||
$timeOptions['widget'] = $options['time_widget'];
|
||||
}
|
||||
|
||||
if (null !== $options['time_label']) {
|
||||
$timeOptions['label'] = $options['time_label'];
|
||||
}
|
||||
|
||||
if (null !== $options['date_format']) {
|
||||
$dateOptions['format'] = $options['date_format'];
|
||||
}
|
||||
|
||||
$dateOptions['input'] = $timeOptions['input'] = 'array';
|
||||
$dateOptions['error_bubbling'] = $timeOptions['error_bubbling'] = true;
|
||||
|
||||
$builder
|
||||
->addViewTransformer(new DataTransformerChain([
|
||||
new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts),
|
||||
new ArrayToPartsTransformer([
|
||||
'date' => $dateParts,
|
||||
'time' => $timeParts,
|
||||
]),
|
||||
]))
|
||||
->add('date', DateType::class, $dateOptions)
|
||||
->add('time', TimeType::class, $timeOptions)
|
||||
;
|
||||
}
|
||||
|
||||
if ('datetime_immutable' === $options['input']) {
|
||||
$builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer());
|
||||
} elseif ('string' === $options['input']) {
|
||||
$builder->addModelTransformer(new ReversedTransformer(
|
||||
new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], $options['input_format'])
|
||||
));
|
||||
} elseif ('timestamp' === $options['input']) {
|
||||
$builder->addModelTransformer(new ReversedTransformer(
|
||||
new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone'])
|
||||
));
|
||||
} elseif ('array' === $options['input']) {
|
||||
$builder->addModelTransformer(new ReversedTransformer(
|
||||
new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], $parts)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['widget'] = $options['widget'];
|
||||
|
||||
// Change the input to an HTML5 datetime input if
|
||||
// * the widget is set to "single_text"
|
||||
// * the format matches the one expected by HTML5
|
||||
// * the html5 is set to true
|
||||
if ($options['html5'] && 'single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) {
|
||||
$view->vars['type'] = 'datetime-local';
|
||||
|
||||
// we need to force the browser to display the seconds by
|
||||
// adding the HTML attribute step if not already defined.
|
||||
// Otherwise the browser will not display and so not send the seconds
|
||||
// therefore the value will always be considered as invalid.
|
||||
if ($options['with_seconds'] && !isset($view->vars['attr']['step'])) {
|
||||
$view->vars['attr']['step'] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$compound = function (Options $options) {
|
||||
return 'single_text' !== $options['widget'];
|
||||
};
|
||||
|
||||
// Defaults to the value of "widget"
|
||||
$dateWidget = function (Options $options) {
|
||||
return 'single_text' === $options['widget'] ? null : $options['widget'];
|
||||
};
|
||||
|
||||
// Defaults to the value of "widget"
|
||||
$timeWidget = function (Options $options) {
|
||||
return 'single_text' === $options['widget'] ? null : $options['widget'];
|
||||
};
|
||||
|
||||
$resolver->setDefaults([
|
||||
'input' => 'datetime',
|
||||
'model_timezone' => null,
|
||||
'view_timezone' => null,
|
||||
'format' => self::HTML5_FORMAT,
|
||||
'date_format' => null,
|
||||
'widget' => null,
|
||||
'date_widget' => $dateWidget,
|
||||
'time_widget' => $timeWidget,
|
||||
'with_minutes' => true,
|
||||
'with_seconds' => false,
|
||||
'html5' => true,
|
||||
// Don't modify \DateTime classes by reference, we treat
|
||||
// them like immutable value objects
|
||||
'by_reference' => false,
|
||||
'error_bubbling' => false,
|
||||
// If initialized with a \DateTime object, FormType initializes
|
||||
// this option to "\DateTime". Since the internal, normalized
|
||||
// representation is not \DateTime, but an array, we need to unset
|
||||
// this option.
|
||||
'data_class' => null,
|
||||
'compound' => $compound,
|
||||
'date_label' => null,
|
||||
'time_label' => null,
|
||||
'empty_data' => function (Options $options) {
|
||||
return $options['compound'] ? [] : '';
|
||||
},
|
||||
'input_format' => 'Y-m-d H:i:s',
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a valid date and time.';
|
||||
},
|
||||
]);
|
||||
|
||||
// Don't add some defaults in order to preserve the defaults
|
||||
// set in DateType and TimeType
|
||||
$resolver->setDefined([
|
||||
'placeholder',
|
||||
'choice_translation_domain',
|
||||
'years',
|
||||
'months',
|
||||
'days',
|
||||
'hours',
|
||||
'minutes',
|
||||
'seconds',
|
||||
]);
|
||||
|
||||
$resolver->setAllowedValues('input', [
|
||||
'datetime',
|
||||
'datetime_immutable',
|
||||
'string',
|
||||
'timestamp',
|
||||
'array',
|
||||
]);
|
||||
$resolver->setAllowedValues('date_widget', [
|
||||
null, // inherit default from DateType
|
||||
'single_text',
|
||||
'text',
|
||||
'choice',
|
||||
]);
|
||||
$resolver->setAllowedValues('time_widget', [
|
||||
null, // inherit default from TimeType
|
||||
'single_text',
|
||||
'text',
|
||||
'choice',
|
||||
]);
|
||||
// This option will overwrite "date_widget" and "time_widget" options
|
||||
$resolver->setAllowedValues('widget', [
|
||||
null, // default, don't overwrite options
|
||||
'single_text',
|
||||
'text',
|
||||
'choice',
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('input_format', 'string');
|
||||
|
||||
$resolver->setNormalizer('date_format', function (Options $options, $dateFormat) {
|
||||
if (null !== $dateFormat && 'single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) {
|
||||
throw new LogicException(sprintf('Cannot use the "date_format" option of the "%s" with an HTML5 date.', self::class));
|
||||
}
|
||||
|
||||
return $dateFormat;
|
||||
});
|
||||
$resolver->setNormalizer('date_widget', function (Options $options, $dateWidget) {
|
||||
if (null !== $dateWidget && 'single_text' === $options['widget']) {
|
||||
throw new LogicException(sprintf('Cannot use the "date_widget" option of the "%s" when the "widget" option is set to "single_text".', self::class));
|
||||
}
|
||||
|
||||
return $dateWidget;
|
||||
});
|
||||
$resolver->setNormalizer('time_widget', function (Options $options, $timeWidget) {
|
||||
if (null !== $timeWidget && 'single_text' === $options['widget']) {
|
||||
throw new LogicException(sprintf('Cannot use the "time_widget" option of the "%s" when the "widget" option is set to "single_text".', self::class));
|
||||
}
|
||||
|
||||
return $timeWidget;
|
||||
});
|
||||
$resolver->setNormalizer('html5', function (Options $options, $html5) {
|
||||
if ($html5 && self::HTML5_FORMAT !== $options['format']) {
|
||||
throw new LogicException(sprintf('Cannot use the "format" option of "%s" when the "html5" option is enabled.', self::class));
|
||||
}
|
||||
|
||||
return $html5;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'datetime';
|
||||
}
|
||||
}
|
405
vendor/symfony/form/Extension/Core/Type/DateType.php
vendored
Normal file
405
vendor/symfony/form/Extension/Core/Type/DateType.php
vendored
Normal file
@ -0,0 +1,405 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\ReversedTransformer;
|
||||
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class DateType extends AbstractType
|
||||
{
|
||||
public const DEFAULT_FORMAT = \IntlDateFormatter::MEDIUM;
|
||||
public const HTML5_FORMAT = 'yyyy-MM-dd';
|
||||
|
||||
private const ACCEPTED_FORMATS = [
|
||||
\IntlDateFormatter::FULL,
|
||||
\IntlDateFormatter::LONG,
|
||||
\IntlDateFormatter::MEDIUM,
|
||||
\IntlDateFormatter::SHORT,
|
||||
];
|
||||
|
||||
private const WIDGETS = [
|
||||
'text' => TextType::class,
|
||||
'choice' => ChoiceType::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$dateFormat = \is_int($options['format']) ? $options['format'] : self::DEFAULT_FORMAT;
|
||||
$timeFormat = \IntlDateFormatter::NONE;
|
||||
$calendar = \IntlDateFormatter::GREGORIAN;
|
||||
$pattern = \is_string($options['format']) ? $options['format'] : '';
|
||||
|
||||
if (!\in_array($dateFormat, self::ACCEPTED_FORMATS, true)) {
|
||||
throw new InvalidOptionsException('The "format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format.');
|
||||
}
|
||||
|
||||
if ('single_text' === $options['widget']) {
|
||||
if ('' !== $pattern && !str_contains($pattern, 'y') && !str_contains($pattern, 'M') && !str_contains($pattern, 'd')) {
|
||||
throw new InvalidOptionsException(sprintf('The "format" option should contain the letters "y", "M" or "d". Its current value is "%s".', $pattern));
|
||||
}
|
||||
|
||||
$builder->addViewTransformer(new DateTimeToLocalizedStringTransformer(
|
||||
$options['model_timezone'],
|
||||
$options['view_timezone'],
|
||||
$dateFormat,
|
||||
$timeFormat,
|
||||
$calendar,
|
||||
$pattern
|
||||
));
|
||||
} else {
|
||||
if ('' !== $pattern && (!str_contains($pattern, 'y') || !str_contains($pattern, 'M') || !str_contains($pattern, 'd'))) {
|
||||
throw new InvalidOptionsException(sprintf('The "format" option should contain the letters "y", "M" and "d". Its current value is "%s".', $pattern));
|
||||
}
|
||||
|
||||
$yearOptions = $monthOptions = $dayOptions = [
|
||||
'error_bubbling' => true,
|
||||
'empty_data' => '',
|
||||
];
|
||||
// when the form is compound the entries of the array are ignored in favor of children data
|
||||
// so we need to handle the cascade setting here
|
||||
$emptyData = $builder->getEmptyData() ?: [];
|
||||
|
||||
if ($emptyData instanceof \Closure) {
|
||||
$lazyEmptyData = static function ($option) use ($emptyData) {
|
||||
return static function (FormInterface $form) use ($emptyData, $option) {
|
||||
$emptyData = $emptyData($form->getParent());
|
||||
|
||||
return $emptyData[$option] ?? '';
|
||||
};
|
||||
};
|
||||
|
||||
$yearOptions['empty_data'] = $lazyEmptyData('year');
|
||||
$monthOptions['empty_data'] = $lazyEmptyData('month');
|
||||
$dayOptions['empty_data'] = $lazyEmptyData('day');
|
||||
} else {
|
||||
if (isset($emptyData['year'])) {
|
||||
$yearOptions['empty_data'] = $emptyData['year'];
|
||||
}
|
||||
if (isset($emptyData['month'])) {
|
||||
$monthOptions['empty_data'] = $emptyData['month'];
|
||||
}
|
||||
if (isset($emptyData['day'])) {
|
||||
$dayOptions['empty_data'] = $emptyData['day'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['invalid_message'])) {
|
||||
$dayOptions['invalid_message'] = $options['invalid_message'];
|
||||
$monthOptions['invalid_message'] = $options['invalid_message'];
|
||||
$yearOptions['invalid_message'] = $options['invalid_message'];
|
||||
}
|
||||
|
||||
if (isset($options['invalid_message_parameters'])) {
|
||||
$dayOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
|
||||
$monthOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
|
||||
$yearOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
|
||||
}
|
||||
|
||||
$formatter = new \IntlDateFormatter(
|
||||
\Locale::getDefault(),
|
||||
$dateFormat,
|
||||
$timeFormat,
|
||||
// see https://bugs.php.net/66323
|
||||
class_exists(\IntlTimeZone::class, false) ? \IntlTimeZone::createDefault() : null,
|
||||
$calendar,
|
||||
$pattern
|
||||
);
|
||||
|
||||
// new \IntlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/66323
|
||||
if (!$formatter) {
|
||||
throw new InvalidOptionsException(intl_get_error_message(), intl_get_error_code());
|
||||
}
|
||||
|
||||
$formatter->setLenient(false);
|
||||
|
||||
if ('choice' === $options['widget']) {
|
||||
// Only pass a subset of the options to children
|
||||
$yearOptions['choices'] = $this->formatTimestamps($formatter, '/y+/', $this->listYears($options['years']));
|
||||
$yearOptions['placeholder'] = $options['placeholder']['year'];
|
||||
$yearOptions['choice_translation_domain'] = $options['choice_translation_domain']['year'];
|
||||
$monthOptions['choices'] = $this->formatTimestamps($formatter, '/[M|L]+/', $this->listMonths($options['months']));
|
||||
$monthOptions['placeholder'] = $options['placeholder']['month'];
|
||||
$monthOptions['choice_translation_domain'] = $options['choice_translation_domain']['month'];
|
||||
$dayOptions['choices'] = $this->formatTimestamps($formatter, '/d+/', $this->listDays($options['days']));
|
||||
$dayOptions['placeholder'] = $options['placeholder']['day'];
|
||||
$dayOptions['choice_translation_domain'] = $options['choice_translation_domain']['day'];
|
||||
}
|
||||
|
||||
// Append generic carry-along options
|
||||
foreach (['required', 'translation_domain'] as $passOpt) {
|
||||
$yearOptions[$passOpt] = $monthOptions[$passOpt] = $dayOptions[$passOpt] = $options[$passOpt];
|
||||
}
|
||||
|
||||
$builder
|
||||
->add('year', self::WIDGETS[$options['widget']], $yearOptions)
|
||||
->add('month', self::WIDGETS[$options['widget']], $monthOptions)
|
||||
->add('day', self::WIDGETS[$options['widget']], $dayOptions)
|
||||
->addViewTransformer(new DateTimeToArrayTransformer(
|
||||
$options['model_timezone'], $options['view_timezone'], ['year', 'month', 'day']
|
||||
))
|
||||
->setAttribute('formatter', $formatter)
|
||||
;
|
||||
}
|
||||
|
||||
if ('datetime_immutable' === $options['input']) {
|
||||
$builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer());
|
||||
} elseif ('string' === $options['input']) {
|
||||
$builder->addModelTransformer(new ReversedTransformer(
|
||||
new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], $options['input_format'])
|
||||
));
|
||||
} elseif ('timestamp' === $options['input']) {
|
||||
$builder->addModelTransformer(new ReversedTransformer(
|
||||
new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone'])
|
||||
));
|
||||
} elseif ('array' === $options['input']) {
|
||||
$builder->addModelTransformer(new ReversedTransformer(
|
||||
new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], ['year', 'month', 'day'])
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function finishView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['widget'] = $options['widget'];
|
||||
|
||||
// Change the input to an HTML5 date input if
|
||||
// * the widget is set to "single_text"
|
||||
// * the format matches the one expected by HTML5
|
||||
// * the html5 is set to true
|
||||
if ($options['html5'] && 'single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) {
|
||||
$view->vars['type'] = 'date';
|
||||
}
|
||||
|
||||
if ($form->getConfig()->hasAttribute('formatter')) {
|
||||
$pattern = $form->getConfig()->getAttribute('formatter')->getPattern();
|
||||
|
||||
// remove special characters unless the format was explicitly specified
|
||||
if (!\is_string($options['format'])) {
|
||||
// remove quoted strings first
|
||||
$pattern = preg_replace('/\'[^\']+\'/', '', $pattern);
|
||||
|
||||
// remove remaining special chars
|
||||
$pattern = preg_replace('/[^yMd]+/', '', $pattern);
|
||||
}
|
||||
|
||||
// set right order with respect to locale (e.g.: de_DE=dd.MM.yy; en_US=M/d/yy)
|
||||
// lookup various formats at http://userguide.icu-project.org/formatparse/datetime
|
||||
if (preg_match('/^([yMd]+)[^yMd]*([yMd]+)[^yMd]*([yMd]+)$/', $pattern)) {
|
||||
$pattern = preg_replace(['/y+/', '/M+/', '/d+/'], ['{{ year }}', '{{ month }}', '{{ day }}'], $pattern);
|
||||
} else {
|
||||
// default fallback
|
||||
$pattern = '{{ year }}{{ month }}{{ day }}';
|
||||
}
|
||||
|
||||
$view->vars['date_pattern'] = $pattern;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$compound = function (Options $options) {
|
||||
return 'single_text' !== $options['widget'];
|
||||
};
|
||||
|
||||
$placeholderDefault = function (Options $options) {
|
||||
return $options['required'] ? null : '';
|
||||
};
|
||||
|
||||
$placeholderNormalizer = function (Options $options, $placeholder) use ($placeholderDefault) {
|
||||
if (\is_array($placeholder)) {
|
||||
$default = $placeholderDefault($options);
|
||||
|
||||
return array_merge(
|
||||
['year' => $default, 'month' => $default, 'day' => $default],
|
||||
$placeholder
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'year' => $placeholder,
|
||||
'month' => $placeholder,
|
||||
'day' => $placeholder,
|
||||
];
|
||||
};
|
||||
|
||||
$choiceTranslationDomainNormalizer = function (Options $options, $choiceTranslationDomain) {
|
||||
if (\is_array($choiceTranslationDomain)) {
|
||||
$default = false;
|
||||
|
||||
return array_replace(
|
||||
['year' => $default, 'month' => $default, 'day' => $default],
|
||||
$choiceTranslationDomain
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'year' => $choiceTranslationDomain,
|
||||
'month' => $choiceTranslationDomain,
|
||||
'day' => $choiceTranslationDomain,
|
||||
];
|
||||
};
|
||||
|
||||
$format = function (Options $options) {
|
||||
return 'single_text' === $options['widget'] ? self::HTML5_FORMAT : self::DEFAULT_FORMAT;
|
||||
};
|
||||
|
||||
$resolver->setDefaults([
|
||||
'years' => range((int) date('Y') - 5, (int) date('Y') + 5),
|
||||
'months' => range(1, 12),
|
||||
'days' => range(1, 31),
|
||||
'widget' => 'choice',
|
||||
'input' => 'datetime',
|
||||
'format' => $format,
|
||||
'model_timezone' => null,
|
||||
'view_timezone' => null,
|
||||
'placeholder' => $placeholderDefault,
|
||||
'html5' => true,
|
||||
// Don't modify \DateTime classes by reference, we treat
|
||||
// them like immutable value objects
|
||||
'by_reference' => false,
|
||||
'error_bubbling' => false,
|
||||
// If initialized with a \DateTime object, FormType initializes
|
||||
// this option to "\DateTime". Since the internal, normalized
|
||||
// representation is not \DateTime, but an array, we need to unset
|
||||
// this option.
|
||||
'data_class' => null,
|
||||
'compound' => $compound,
|
||||
'empty_data' => function (Options $options) {
|
||||
return $options['compound'] ? [] : '';
|
||||
},
|
||||
'choice_translation_domain' => false,
|
||||
'input_format' => 'Y-m-d',
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a valid date.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setNormalizer('placeholder', $placeholderNormalizer);
|
||||
$resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer);
|
||||
|
||||
$resolver->setAllowedValues('input', [
|
||||
'datetime',
|
||||
'datetime_immutable',
|
||||
'string',
|
||||
'timestamp',
|
||||
'array',
|
||||
]);
|
||||
$resolver->setAllowedValues('widget', [
|
||||
'single_text',
|
||||
'text',
|
||||
'choice',
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('format', ['int', 'string']);
|
||||
$resolver->setAllowedTypes('years', 'array');
|
||||
$resolver->setAllowedTypes('months', 'array');
|
||||
$resolver->setAllowedTypes('days', 'array');
|
||||
$resolver->setAllowedTypes('input_format', 'string');
|
||||
|
||||
$resolver->setNormalizer('html5', function (Options $options, $html5) {
|
||||
if ($html5 && 'single_text' === $options['widget'] && self::HTML5_FORMAT !== $options['format']) {
|
||||
throw new LogicException(sprintf('Cannot use the "format" option of "%s" when the "html5" option is enabled.', self::class));
|
||||
}
|
||||
|
||||
return $html5;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'date';
|
||||
}
|
||||
|
||||
private function formatTimestamps(\IntlDateFormatter $formatter, string $regex, array $timestamps)
|
||||
{
|
||||
$pattern = $formatter->getPattern();
|
||||
$timezone = $formatter->getTimeZoneId();
|
||||
$formattedTimestamps = [];
|
||||
|
||||
$formatter->setTimeZone('UTC');
|
||||
|
||||
if (preg_match($regex, $pattern, $matches)) {
|
||||
$formatter->setPattern($matches[0]);
|
||||
|
||||
foreach ($timestamps as $timestamp => $choice) {
|
||||
$formattedTimestamps[$formatter->format($timestamp)] = $choice;
|
||||
}
|
||||
|
||||
// I'd like to clone the formatter above, but then we get a
|
||||
// segmentation fault, so let's restore the old state instead
|
||||
$formatter->setPattern($pattern);
|
||||
}
|
||||
|
||||
$formatter->setTimeZone($timezone);
|
||||
|
||||
return $formattedTimestamps;
|
||||
}
|
||||
|
||||
private function listYears(array $years)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($years as $year) {
|
||||
$result[\PHP_INT_SIZE === 4 ? \DateTime::createFromFormat('Y e', $year.' UTC')->format('U') : gmmktime(0, 0, 0, 6, 15, $year)] = $year;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function listMonths(array $months)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($months as $month) {
|
||||
$result[gmmktime(0, 0, 0, $month, 15)] = $month;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function listDays(array $days)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($days as $day) {
|
||||
$result[gmmktime(0, 0, 0, 5, $day)] = $day;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
49
vendor/symfony/form/Extension/Core/Type/EmailType.php
vendored
Normal file
49
vendor/symfony/form/Extension/Core/Type/EmailType.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class EmailType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a valid email address.';
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'email';
|
||||
}
|
||||
}
|
57
vendor/symfony/form/Extension/Core/Type/EnumType.php
vendored
Normal file
57
vendor/symfony/form/Extension/Core/Type/EnumType.php
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* A choice type for native PHP enums.
|
||||
*
|
||||
* @author Alexander M. Turek <me@derrabus.de>
|
||||
*/
|
||||
final class EnumType extends AbstractType
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver
|
||||
->setRequired(['class'])
|
||||
->setAllowedTypes('class', 'string')
|
||||
->setAllowedValues('class', \Closure::fromCallable('enum_exists'))
|
||||
->setDefault('choices', static function (Options $options): array {
|
||||
return $options['class']::cases();
|
||||
})
|
||||
->setDefault('choice_label', static function (\UnitEnum $choice): string {
|
||||
return $choice->name;
|
||||
})
|
||||
->setDefault('choice_value', static function (Options $options): ?\Closure {
|
||||
if (!is_a($options['class'], \BackedEnum::class, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return static function (?\BackedEnum $choice): ?string {
|
||||
if (null === $choice) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string) $choice->value;
|
||||
};
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
}
|
255
vendor/symfony/form/Extension/Core/Type/FileType.php
vendored
Normal file
255
vendor/symfony/form/Extension/Core/Type/FileType.php
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FileUploadError;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class FileType extends AbstractType
|
||||
{
|
||||
public const KIB_BYTES = 1024;
|
||||
public const MIB_BYTES = 1048576;
|
||||
|
||||
private const SUFFIXES = [
|
||||
1 => 'bytes',
|
||||
self::KIB_BYTES => 'KiB',
|
||||
self::MIB_BYTES => 'MiB',
|
||||
];
|
||||
|
||||
private $translator;
|
||||
|
||||
public function __construct(TranslatorInterface $translator = null)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
// Ensure that submitted data is always an uploaded file or an array of some
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) {
|
||||
$form = $event->getForm();
|
||||
$requestHandler = $form->getConfig()->getRequestHandler();
|
||||
|
||||
if ($options['multiple']) {
|
||||
$data = [];
|
||||
$files = $event->getData();
|
||||
|
||||
if (!\is_array($files)) {
|
||||
$files = [];
|
||||
}
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ($requestHandler->isFileUpload($file)) {
|
||||
$data[] = $file;
|
||||
|
||||
if (method_exists($requestHandler, 'getUploadFileError') && null !== $errorCode = $requestHandler->getUploadFileError($file)) {
|
||||
$form->addError($this->getFileUploadError($errorCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since the array is never considered empty in the view data format
|
||||
// on submission, we need to evaluate the configured empty data here
|
||||
if ([] === $data) {
|
||||
$emptyData = $form->getConfig()->getEmptyData();
|
||||
$data = $emptyData instanceof \Closure ? $emptyData($form, $data) : $emptyData;
|
||||
}
|
||||
|
||||
$event->setData($data);
|
||||
} elseif ($requestHandler->isFileUpload($event->getData()) && method_exists($requestHandler, 'getUploadFileError') && null !== $errorCode = $requestHandler->getUploadFileError($event->getData())) {
|
||||
$form->addError($this->getFileUploadError($errorCode));
|
||||
} elseif (!$requestHandler->isFileUpload($event->getData())) {
|
||||
$event->setData(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
if ($options['multiple']) {
|
||||
$view->vars['full_name'] .= '[]';
|
||||
$view->vars['attr']['multiple'] = 'multiple';
|
||||
}
|
||||
|
||||
$view->vars = array_replace($view->vars, [
|
||||
'type' => 'file',
|
||||
'value' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function finishView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['multipart'] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$dataClass = null;
|
||||
if (class_exists(\Symfony\Component\HttpFoundation\File\File::class)) {
|
||||
$dataClass = function (Options $options) {
|
||||
return $options['multiple'] ? null : 'Symfony\Component\HttpFoundation\File\File';
|
||||
};
|
||||
}
|
||||
|
||||
$emptyData = function (Options $options) {
|
||||
return $options['multiple'] ? [] : null;
|
||||
};
|
||||
|
||||
$resolver->setDefaults([
|
||||
'compound' => false,
|
||||
'data_class' => $dataClass,
|
||||
'empty_data' => $emptyData,
|
||||
'multiple' => false,
|
||||
'allow_file_upload' => true,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please select a valid file.';
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'file';
|
||||
}
|
||||
|
||||
private function getFileUploadError(int $errorCode)
|
||||
{
|
||||
$messageParameters = [];
|
||||
|
||||
if (\UPLOAD_ERR_INI_SIZE === $errorCode) {
|
||||
[$limitAsString, $suffix] = $this->factorizeSizes(0, self::getMaxFilesize());
|
||||
$messageTemplate = 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.';
|
||||
$messageParameters = [
|
||||
'{{ limit }}' => $limitAsString,
|
||||
'{{ suffix }}' => $suffix,
|
||||
];
|
||||
} elseif (\UPLOAD_ERR_FORM_SIZE === $errorCode) {
|
||||
$messageTemplate = 'The file is too large.';
|
||||
} else {
|
||||
$messageTemplate = 'The file could not be uploaded.';
|
||||
}
|
||||
|
||||
if (null !== $this->translator) {
|
||||
$message = $this->translator->trans($messageTemplate, $messageParameters, 'validators');
|
||||
} else {
|
||||
$message = strtr($messageTemplate, $messageParameters);
|
||||
}
|
||||
|
||||
return new FileUploadError($message, $messageTemplate, $messageParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum size of an uploaded file as configured in php.ini.
|
||||
*
|
||||
* This method should be kept in sync with Symfony\Component\HttpFoundation\File\UploadedFile::getMaxFilesize().
|
||||
*
|
||||
* @return int|float The maximum size of an uploaded file in bytes (returns float if size > PHP_INT_MAX)
|
||||
*/
|
||||
private static function getMaxFilesize()
|
||||
{
|
||||
$iniMax = strtolower(ini_get('upload_max_filesize'));
|
||||
|
||||
if ('' === $iniMax) {
|
||||
return \PHP_INT_MAX;
|
||||
}
|
||||
|
||||
$max = ltrim($iniMax, '+');
|
||||
if (str_starts_with($max, '0x')) {
|
||||
$max = \intval($max, 16);
|
||||
} elseif (str_starts_with($max, '0')) {
|
||||
$max = \intval($max, 8);
|
||||
} else {
|
||||
$max = (int) $max;
|
||||
}
|
||||
|
||||
switch (substr($iniMax, -1)) {
|
||||
case 't': $max *= 1024;
|
||||
// no break
|
||||
case 'g': $max *= 1024;
|
||||
// no break
|
||||
case 'm': $max *= 1024;
|
||||
// no break
|
||||
case 'k': $max *= 1024;
|
||||
}
|
||||
|
||||
return $max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the limit to the smallest possible number
|
||||
* (i.e. try "MB", then "kB", then "bytes").
|
||||
*
|
||||
* This method should be kept in sync with Symfony\Component\Validator\Constraints\FileValidator::factorizeSizes().
|
||||
*
|
||||
* @param int|float $limit
|
||||
*/
|
||||
private function factorizeSizes(int $size, $limit)
|
||||
{
|
||||
$coef = self::MIB_BYTES;
|
||||
$coefFactor = self::KIB_BYTES;
|
||||
|
||||
$limitAsString = (string) ($limit / $coef);
|
||||
|
||||
// Restrict the limit to 2 decimals (without rounding! we
|
||||
// need the precise value)
|
||||
while (self::moreDecimalsThan($limitAsString, 2)) {
|
||||
$coef /= $coefFactor;
|
||||
$limitAsString = (string) ($limit / $coef);
|
||||
}
|
||||
|
||||
// Convert size to the same measure, but round to 2 decimals
|
||||
$sizeAsString = (string) round($size / $coef, 2);
|
||||
|
||||
// If the size and limit produce the same string output
|
||||
// (due to rounding), reduce the coefficient
|
||||
while ($sizeAsString === $limitAsString) {
|
||||
$coef /= $coefFactor;
|
||||
$limitAsString = (string) ($limit / $coef);
|
||||
$sizeAsString = (string) round($size / $coef, 2);
|
||||
}
|
||||
|
||||
return [$limitAsString, self::SUFFIXES[$coef]];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be kept in sync with Symfony\Component\Validator\Constraints\FileValidator::moreDecimalsThan().
|
||||
*/
|
||||
private static function moreDecimalsThan(string $double, int $numberOfDecimals): bool
|
||||
{
|
||||
return \strlen($double) > \strlen(round($double, $numberOfDecimals));
|
||||
}
|
||||
}
|
257
vendor/symfony/form/Extension/Core/Type/FormType.php
vendored
Normal file
257
vendor/symfony/form/Extension/Core/Type/FormType.php
vendored
Normal file
@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\DataAccessor\CallbackAccessor;
|
||||
use Symfony\Component\Form\Extension\Core\DataAccessor\ChainAccessor;
|
||||
use Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor;
|
||||
use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper;
|
||||
use Symfony\Component\Form\Extension\Core\EventListener\TrimListener;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormConfigBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
use Symfony\Component\Translation\TranslatableMessage;
|
||||
|
||||
class FormType extends BaseType
|
||||
{
|
||||
private $dataMapper;
|
||||
|
||||
public function __construct(PropertyAccessorInterface $propertyAccessor = null)
|
||||
{
|
||||
$this->dataMapper = new DataMapper(new ChainAccessor([
|
||||
new CallbackAccessor(),
|
||||
new PropertyPathAccessor($propertyAccessor ?? PropertyAccess::createPropertyAccessor()),
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
parent::buildForm($builder, $options);
|
||||
|
||||
$isDataOptionSet = \array_key_exists('data', $options);
|
||||
|
||||
$builder
|
||||
->setRequired($options['required'])
|
||||
->setErrorBubbling($options['error_bubbling'])
|
||||
->setEmptyData($options['empty_data'])
|
||||
->setPropertyPath($options['property_path'])
|
||||
->setMapped($options['mapped'])
|
||||
->setByReference($options['by_reference'])
|
||||
->setInheritData($options['inherit_data'])
|
||||
->setCompound($options['compound'])
|
||||
->setData($isDataOptionSet ? $options['data'] : null)
|
||||
->setDataLocked($isDataOptionSet)
|
||||
->setDataMapper($options['compound'] ? $this->dataMapper : null)
|
||||
->setMethod($options['method'])
|
||||
->setAction($options['action']);
|
||||
|
||||
if ($options['trim']) {
|
||||
$builder->addEventSubscriber(new TrimListener());
|
||||
}
|
||||
|
||||
if (!method_exists($builder, 'setIsEmptyCallback')) {
|
||||
trigger_deprecation('symfony/form', '5.1', 'Not implementing the "%s::setIsEmptyCallback()" method in "%s" is deprecated.', FormConfigBuilderInterface::class, get_debug_type($builder));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$builder->setIsEmptyCallback($options['is_empty_callback']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
parent::buildView($view, $form, $options);
|
||||
|
||||
$name = $form->getName();
|
||||
$helpTranslationParameters = $options['help_translation_parameters'];
|
||||
|
||||
if ($view->parent) {
|
||||
if ('' === $name) {
|
||||
throw new LogicException('Form node with empty name can be used only as root form node.');
|
||||
}
|
||||
|
||||
// Complex fields are read-only if they themselves or their parents are.
|
||||
if (!isset($view->vars['attr']['readonly']) && isset($view->parent->vars['attr']['readonly']) && false !== $view->parent->vars['attr']['readonly']) {
|
||||
$view->vars['attr']['readonly'] = true;
|
||||
}
|
||||
|
||||
$helpTranslationParameters = array_merge($view->parent->vars['help_translation_parameters'], $helpTranslationParameters);
|
||||
|
||||
$rootFormAttrOption = $form->getRoot()->getConfig()->getOption('form_attr');
|
||||
if ($options['form_attr'] || $rootFormAttrOption) {
|
||||
$view->vars['attr']['form'] = \is_string($rootFormAttrOption) ? $rootFormAttrOption : $form->getRoot()->getName();
|
||||
if (empty($view->vars['attr']['form'])) {
|
||||
throw new LogicException('"form_attr" option must be a string identifier on root form when it has no id.');
|
||||
}
|
||||
}
|
||||
} elseif (\is_string($options['form_attr'])) {
|
||||
$view->vars['id'] = $options['form_attr'];
|
||||
}
|
||||
|
||||
$formConfig = $form->getConfig();
|
||||
$view->vars = array_replace($view->vars, [
|
||||
'errors' => $form->getErrors(),
|
||||
'valid' => $form->isSubmitted() ? $form->isValid() : true,
|
||||
'value' => $form->getViewData(),
|
||||
'data' => $form->getNormData(),
|
||||
'required' => $form->isRequired(),
|
||||
'size' => null,
|
||||
'label_attr' => $options['label_attr'],
|
||||
'help' => $options['help'],
|
||||
'help_attr' => $options['help_attr'],
|
||||
'help_html' => $options['help_html'],
|
||||
'help_translation_parameters' => $helpTranslationParameters,
|
||||
'compound' => $formConfig->getCompound(),
|
||||
'method' => $formConfig->getMethod(),
|
||||
'action' => $formConfig->getAction(),
|
||||
'submitted' => $form->isSubmitted(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function finishView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$multipart = false;
|
||||
|
||||
foreach ($view->children as $child) {
|
||||
if ($child->vars['multipart']) {
|
||||
$multipart = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$view->vars['multipart'] = $multipart;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
// Derive "data_class" option from passed "data" object
|
||||
$dataClass = function (Options $options) {
|
||||
return isset($options['data']) && \is_object($options['data']) ? \get_class($options['data']) : null;
|
||||
};
|
||||
|
||||
// Derive "empty_data" closure from "data_class" option
|
||||
$emptyData = function (Options $options) {
|
||||
$class = $options['data_class'];
|
||||
|
||||
if (null !== $class) {
|
||||
return function (FormInterface $form) use ($class) {
|
||||
return $form->isEmpty() && !$form->isRequired() ? null : new $class();
|
||||
};
|
||||
}
|
||||
|
||||
return function (FormInterface $form) {
|
||||
return $form->getConfig()->getCompound() ? [] : '';
|
||||
};
|
||||
};
|
||||
|
||||
// Wrap "post_max_size_message" in a closure to translate it lazily
|
||||
$uploadMaxSizeMessage = function (Options $options) {
|
||||
return function () use ($options) {
|
||||
return $options['post_max_size_message'];
|
||||
};
|
||||
};
|
||||
|
||||
// For any form that is not represented by a single HTML control,
|
||||
// errors should bubble up by default
|
||||
$errorBubbling = function (Options $options) {
|
||||
return $options['compound'] && !$options['inherit_data'];
|
||||
};
|
||||
|
||||
// If data is given, the form is locked to that data
|
||||
// (independent of its value)
|
||||
$resolver->setDefined([
|
||||
'data',
|
||||
]);
|
||||
|
||||
$resolver->setDefaults([
|
||||
'data_class' => $dataClass,
|
||||
'empty_data' => $emptyData,
|
||||
'trim' => true,
|
||||
'required' => true,
|
||||
'property_path' => null,
|
||||
'mapped' => true,
|
||||
'by_reference' => true,
|
||||
'error_bubbling' => $errorBubbling,
|
||||
'label_attr' => [],
|
||||
'inherit_data' => false,
|
||||
'compound' => true,
|
||||
'method' => 'POST',
|
||||
// According to RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt)
|
||||
// section 4.2., empty URIs are considered same-document references
|
||||
'action' => '',
|
||||
'attr' => [],
|
||||
'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.',
|
||||
'upload_max_size_message' => $uploadMaxSizeMessage, // internal
|
||||
'allow_file_upload' => false,
|
||||
'help' => null,
|
||||
'help_attr' => [],
|
||||
'help_html' => false,
|
||||
'help_translation_parameters' => [],
|
||||
'invalid_message' => 'This value is not valid.',
|
||||
'invalid_message_parameters' => [],
|
||||
'is_empty_callback' => null,
|
||||
'getter' => null,
|
||||
'setter' => null,
|
||||
'form_attr' => false,
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('label_attr', 'array');
|
||||
$resolver->setAllowedTypes('action', 'string');
|
||||
$resolver->setAllowedTypes('upload_max_size_message', ['callable']);
|
||||
$resolver->setAllowedTypes('help', ['string', 'null', TranslatableMessage::class]);
|
||||
$resolver->setAllowedTypes('help_attr', 'array');
|
||||
$resolver->setAllowedTypes('help_html', 'bool');
|
||||
$resolver->setAllowedTypes('is_empty_callback', ['null', 'callable']);
|
||||
$resolver->setAllowedTypes('getter', ['null', 'callable']);
|
||||
$resolver->setAllowedTypes('setter', ['null', 'callable']);
|
||||
$resolver->setAllowedTypes('form_attr', ['bool', 'string']);
|
||||
|
||||
$resolver->setInfo('getter', 'A callable that accepts two arguments (the view data and the current form field) and must return a value.');
|
||||
$resolver->setInfo('setter', 'A callable that accepts three arguments (a reference to the view data, the submitted value and the current form field).');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
}
|
46
vendor/symfony/form/Extension/Core/Type/HiddenType.php
vendored
Normal file
46
vendor/symfony/form/Extension/Core/Type/HiddenType.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class HiddenType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
// hidden fields cannot have a required attribute
|
||||
'required' => false,
|
||||
// Pass errors to the parent
|
||||
'error_bubbling' => true,
|
||||
'compound' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'The hidden field is invalid.';
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'hidden';
|
||||
}
|
||||
}
|
77
vendor/symfony/form/Extension/Core/Type/IntegerType.php
vendored
Normal file
77
vendor/symfony/form/Extension/Core/Type/IntegerType.php
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\IntegerToLocalizedStringTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class IntegerType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addViewTransformer(new IntegerToLocalizedStringTransformer($options['grouping'], $options['rounding_mode'], !$options['grouping'] ? 'en' : null));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
if ($options['grouping']) {
|
||||
$view->vars['type'] = 'text';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'grouping' => false,
|
||||
// Integer cast rounds towards 0, so do the same when displaying fractions
|
||||
'rounding_mode' => \NumberFormatter::ROUND_DOWN,
|
||||
'compound' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter an integer.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedValues('rounding_mode', [
|
||||
\NumberFormatter::ROUND_FLOOR,
|
||||
\NumberFormatter::ROUND_DOWN,
|
||||
\NumberFormatter::ROUND_HALFDOWN,
|
||||
\NumberFormatter::ROUND_HALFEVEN,
|
||||
\NumberFormatter::ROUND_HALFUP,
|
||||
\NumberFormatter::ROUND_UP,
|
||||
\NumberFormatter::ROUND_CEILING,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'integer';
|
||||
}
|
||||
}
|
96
vendor/symfony/form/Extension/Core/Type/LanguageType.php
vendored
Normal file
96
vendor/symfony/form/Extension/Core/Type/LanguageType.php
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\ChoiceList\ChoiceList;
|
||||
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Intl\Exception\MissingResourceException;
|
||||
use Symfony\Component\Intl\Intl;
|
||||
use Symfony\Component\Intl\Languages;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class LanguageType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'choice_loader' => function (Options $options) {
|
||||
if (!class_exists(Intl::class)) {
|
||||
throw new LogicException(sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class));
|
||||
}
|
||||
$choiceTranslationLocale = $options['choice_translation_locale'];
|
||||
$useAlpha3Codes = $options['alpha3'];
|
||||
$choiceSelfTranslation = $options['choice_self_translation'];
|
||||
|
||||
return ChoiceList::loader($this, new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale, $useAlpha3Codes, $choiceSelfTranslation) {
|
||||
if (true === $choiceSelfTranslation) {
|
||||
foreach (Languages::getLanguageCodes() as $alpha2Code) {
|
||||
try {
|
||||
$languageCode = $useAlpha3Codes ? Languages::getAlpha3Code($alpha2Code) : $alpha2Code;
|
||||
$languagesList[$languageCode] = Languages::getName($alpha2Code, $alpha2Code);
|
||||
} catch (MissingResourceException $e) {
|
||||
// ignore errors like "Couldn't read the indices for the locale 'meta'"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$languagesList = $useAlpha3Codes ? Languages::getAlpha3Names($choiceTranslationLocale) : Languages::getNames($choiceTranslationLocale);
|
||||
}
|
||||
|
||||
return array_flip($languagesList);
|
||||
}), [$choiceTranslationLocale, $useAlpha3Codes, $choiceSelfTranslation]);
|
||||
},
|
||||
'choice_translation_domain' => false,
|
||||
'choice_translation_locale' => null,
|
||||
'alpha3' => false,
|
||||
'choice_self_translation' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please select a valid language.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('choice_self_translation', ['bool']);
|
||||
$resolver->setAllowedTypes('choice_translation_locale', ['null', 'string']);
|
||||
$resolver->setAllowedTypes('alpha3', 'bool');
|
||||
|
||||
$resolver->setNormalizer('choice_self_translation', function (Options $options, $value) {
|
||||
if (true === $value && $options['choice_translation_locale']) {
|
||||
throw new LogicException('Cannot use the "choice_self_translation" and "choice_translation_locale" options at the same time. Remove one of them.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'language';
|
||||
}
|
||||
}
|
69
vendor/symfony/form/Extension/Core/Type/LocaleType.php
vendored
Normal file
69
vendor/symfony/form/Extension/Core/Type/LocaleType.php
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\ChoiceList\ChoiceList;
|
||||
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Intl\Intl;
|
||||
use Symfony\Component\Intl\Locales;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class LocaleType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'choice_loader' => function (Options $options) {
|
||||
if (!class_exists(Intl::class)) {
|
||||
throw new LogicException(sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class));
|
||||
}
|
||||
|
||||
$choiceTranslationLocale = $options['choice_translation_locale'];
|
||||
|
||||
return ChoiceList::loader($this, new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale) {
|
||||
return array_flip(Locales::getNames($choiceTranslationLocale));
|
||||
}), $choiceTranslationLocale);
|
||||
},
|
||||
'choice_translation_domain' => false,
|
||||
'choice_translation_locale' => null,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please select a valid locale.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('choice_translation_locale', ['null', 'string']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'locale';
|
||||
}
|
||||
}
|
149
vendor/symfony/form/Extension/Core/Type/MoneyType.php
vendored
Normal file
149
vendor/symfony/form/Extension/Core/Type/MoneyType.php
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\MoneyToLocalizedStringTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class MoneyType extends AbstractType
|
||||
{
|
||||
protected static $patterns = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
// Values used in HTML5 number inputs should be formatted as in "1234.5", ie. 'en' format without grouping,
|
||||
// according to https://www.w3.org/TR/html51/sec-forms.html#date-time-and-number-formats
|
||||
$builder
|
||||
->addViewTransformer(new MoneyToLocalizedStringTransformer(
|
||||
$options['scale'],
|
||||
$options['grouping'],
|
||||
$options['rounding_mode'],
|
||||
$options['divisor'],
|
||||
$options['html5'] ? 'en' : null
|
||||
))
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['money_pattern'] = self::getPattern($options['currency']);
|
||||
|
||||
if ($options['html5']) {
|
||||
$view->vars['type'] = 'number';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'scale' => 2,
|
||||
'grouping' => false,
|
||||
'rounding_mode' => \NumberFormatter::ROUND_HALFUP,
|
||||
'divisor' => 1,
|
||||
'currency' => 'EUR',
|
||||
'compound' => false,
|
||||
'html5' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a valid money amount.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedValues('rounding_mode', [
|
||||
\NumberFormatter::ROUND_FLOOR,
|
||||
\NumberFormatter::ROUND_DOWN,
|
||||
\NumberFormatter::ROUND_HALFDOWN,
|
||||
\NumberFormatter::ROUND_HALFEVEN,
|
||||
\NumberFormatter::ROUND_HALFUP,
|
||||
\NumberFormatter::ROUND_UP,
|
||||
\NumberFormatter::ROUND_CEILING,
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('scale', 'int');
|
||||
|
||||
$resolver->setAllowedTypes('html5', 'bool');
|
||||
|
||||
$resolver->setNormalizer('grouping', function (Options $options, $value) {
|
||||
if ($value && $options['html5']) {
|
||||
throw new LogicException('Cannot use the "grouping" option when the "html5" option is enabled.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'money';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pattern for this locale in UTF-8.
|
||||
*
|
||||
* The pattern contains the placeholder "{{ widget }}" where the HTML tag should
|
||||
* be inserted
|
||||
*/
|
||||
protected static function getPattern(?string $currency)
|
||||
{
|
||||
if (!$currency) {
|
||||
return '{{ widget }}';
|
||||
}
|
||||
|
||||
$locale = \Locale::getDefault();
|
||||
|
||||
if (!isset(self::$patterns[$locale])) {
|
||||
self::$patterns[$locale] = [];
|
||||
}
|
||||
|
||||
if (!isset(self::$patterns[$locale][$currency])) {
|
||||
$format = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
|
||||
$pattern = $format->formatCurrency('123', $currency);
|
||||
|
||||
// the spacings between currency symbol and number are ignored, because
|
||||
// a single space leads to better readability in combination with input
|
||||
// fields
|
||||
|
||||
// the regex also considers non-break spaces (0xC2 or 0xA0 in UTF-8)
|
||||
|
||||
preg_match('/^([^\s\xc2\xa0]*)[\s\xc2\xa0]*123(?:[,.]0+)?[\s\xc2\xa0]*([^\s\xc2\xa0]*)$/u', $pattern, $matches);
|
||||
|
||||
if (!empty($matches[1])) {
|
||||
self::$patterns[$locale][$currency] = $matches[1].' {{ widget }}';
|
||||
} elseif (!empty($matches[2])) {
|
||||
self::$patterns[$locale][$currency] = '{{ widget }} '.$matches[2];
|
||||
} else {
|
||||
self::$patterns[$locale][$currency] = '{{ widget }}';
|
||||
}
|
||||
}
|
||||
|
||||
return self::$patterns[$locale][$currency];
|
||||
}
|
||||
}
|
102
vendor/symfony/form/Extension/Core/Type/NumberType.php
vendored
Normal file
102
vendor/symfony/form/Extension/Core/Type/NumberType.php
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\NumberToLocalizedStringTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\StringToFloatTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class NumberType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addViewTransformer(new NumberToLocalizedStringTransformer(
|
||||
$options['scale'],
|
||||
$options['grouping'],
|
||||
$options['rounding_mode'],
|
||||
$options['html5'] ? 'en' : null
|
||||
));
|
||||
|
||||
if ('string' === $options['input']) {
|
||||
$builder->addModelTransformer(new StringToFloatTransformer($options['scale']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
if ($options['html5']) {
|
||||
$view->vars['type'] = 'number';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
// default scale is locale specific (usually around 3)
|
||||
'scale' => null,
|
||||
'grouping' => false,
|
||||
'rounding_mode' => \NumberFormatter::ROUND_HALFUP,
|
||||
'compound' => false,
|
||||
'input' => 'number',
|
||||
'html5' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a number.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedValues('rounding_mode', [
|
||||
\NumberFormatter::ROUND_FLOOR,
|
||||
\NumberFormatter::ROUND_DOWN,
|
||||
\NumberFormatter::ROUND_HALFDOWN,
|
||||
\NumberFormatter::ROUND_HALFEVEN,
|
||||
\NumberFormatter::ROUND_HALFUP,
|
||||
\NumberFormatter::ROUND_UP,
|
||||
\NumberFormatter::ROUND_CEILING,
|
||||
]);
|
||||
$resolver->setAllowedValues('input', ['number', 'string']);
|
||||
$resolver->setAllowedTypes('scale', ['null', 'int']);
|
||||
$resolver->setAllowedTypes('html5', 'bool');
|
||||
|
||||
$resolver->setNormalizer('grouping', function (Options $options, $value) {
|
||||
if (true === $value && $options['html5']) {
|
||||
throw new LogicException('Cannot use the "grouping" option when the "html5" option is enabled.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'number';
|
||||
}
|
||||
}
|
63
vendor/symfony/form/Extension/Core/Type/PasswordType.php
vendored
Normal file
63
vendor/symfony/form/Extension/Core/Type/PasswordType.php
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class PasswordType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
if ($options['always_empty'] || !$form->isSubmitted()) {
|
||||
$view->vars['value'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'always_empty' => true,
|
||||
'trim' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'The password is invalid.';
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'password';
|
||||
}
|
||||
}
|
105
vendor/symfony/form/Extension/Core/Type/PercentType.php
vendored
Normal file
105
vendor/symfony/form/Extension/Core/Type/PercentType.php
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\PercentToLocalizedStringTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class PercentType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addViewTransformer(new PercentToLocalizedStringTransformer(
|
||||
$options['scale'],
|
||||
$options['type'],
|
||||
$options['rounding_mode'],
|
||||
$options['html5']
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['symbol'] = $options['symbol'];
|
||||
|
||||
if ($options['html5']) {
|
||||
$view->vars['type'] = 'number';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'scale' => 0,
|
||||
'rounding_mode' => function (Options $options) {
|
||||
trigger_deprecation('symfony/form', '5.1', 'Not configuring the "rounding_mode" option is deprecated. It will default to "\NumberFormatter::ROUND_HALFUP" in Symfony 6.0.');
|
||||
|
||||
return null;
|
||||
},
|
||||
'symbol' => '%',
|
||||
'type' => 'fractional',
|
||||
'compound' => false,
|
||||
'html5' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a percentage value.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedValues('type', [
|
||||
'fractional',
|
||||
'integer',
|
||||
]);
|
||||
$resolver->setAllowedValues('rounding_mode', [
|
||||
null,
|
||||
\NumberFormatter::ROUND_FLOOR,
|
||||
\NumberFormatter::ROUND_DOWN,
|
||||
\NumberFormatter::ROUND_HALFDOWN,
|
||||
\NumberFormatter::ROUND_HALFEVEN,
|
||||
\NumberFormatter::ROUND_HALFUP,
|
||||
\NumberFormatter::ROUND_UP,
|
||||
\NumberFormatter::ROUND_CEILING,
|
||||
]);
|
||||
$resolver->setAllowedTypes('scale', 'int');
|
||||
$resolver->setAllowedTypes('symbol', ['bool', 'string']);
|
||||
$resolver->setDeprecated('rounding_mode', 'symfony/form', '5.1', function (Options $options, $roundingMode) {
|
||||
if (null === $roundingMode) {
|
||||
return 'Not configuring the "rounding_mode" option is deprecated. It will default to "\NumberFormatter::ROUND_HALFUP" in Symfony 6.0.';
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
$resolver->setAllowedTypes('html5', 'bool');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'percent';
|
||||
}
|
||||
}
|
49
vendor/symfony/form/Extension/Core/Type/RadioType.php
vendored
Normal file
49
vendor/symfony/form/Extension/Core/Type/RadioType.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class RadioType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please select a valid option.';
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return CheckboxType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'radio';
|
||||
}
|
||||
}
|
49
vendor/symfony/form/Extension/Core/Type/RangeType.php
vendored
Normal file
49
vendor/symfony/form/Extension/Core/Type/RangeType.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class RangeType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please choose a valid range.';
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'range';
|
||||
}
|
||||
}
|
80
vendor/symfony/form/Extension/Core/Type/RepeatedType.php
vendored
Normal file
80
vendor/symfony/form/Extension/Core/Type/RepeatedType.php
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\ValueToDuplicatesTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class RepeatedType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
// Overwrite required option for child fields
|
||||
$options['first_options']['required'] = $options['required'];
|
||||
$options['second_options']['required'] = $options['required'];
|
||||
|
||||
if (!isset($options['options']['error_bubbling'])) {
|
||||
$options['options']['error_bubbling'] = $options['error_bubbling'];
|
||||
}
|
||||
|
||||
// children fields must always be mapped
|
||||
$defaultOptions = ['mapped' => true];
|
||||
|
||||
$builder
|
||||
->addViewTransformer(new ValueToDuplicatesTransformer([
|
||||
$options['first_name'],
|
||||
$options['second_name'],
|
||||
]))
|
||||
->add($options['first_name'], $options['type'], array_merge($options['options'], $options['first_options'], $defaultOptions))
|
||||
->add($options['second_name'], $options['type'], array_merge($options['options'], $options['second_options'], $defaultOptions))
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'type' => TextType::class,
|
||||
'options' => [],
|
||||
'first_options' => [],
|
||||
'second_options' => [],
|
||||
'first_name' => 'first',
|
||||
'second_name' => 'second',
|
||||
'error_bubbling' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'The values do not match.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('options', 'array');
|
||||
$resolver->setAllowedTypes('first_options', 'array');
|
||||
$resolver->setAllowedTypes('second_options', 'array');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'repeated';
|
||||
}
|
||||
}
|
39
vendor/symfony/form/Extension/Core/Type/ResetType.php
vendored
Normal file
39
vendor/symfony/form/Extension/Core/Type/ResetType.php
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\ButtonTypeInterface;
|
||||
|
||||
/**
|
||||
* A reset button.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ResetType extends AbstractType implements ButtonTypeInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return ButtonType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'reset';
|
||||
}
|
||||
}
|
49
vendor/symfony/form/Extension/Core/Type/SearchType.php
vendored
Normal file
49
vendor/symfony/form/Extension/Core/Type/SearchType.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class SearchType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a valid search term.';
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'search';
|
||||
}
|
||||
}
|
60
vendor/symfony/form/Extension/Core/Type/SubmitType.php
vendored
Normal file
60
vendor/symfony/form/Extension/Core/Type/SubmitType.php
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\SubmitButtonTypeInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* A submit button.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class SubmitType extends AbstractType implements SubmitButtonTypeInterface
|
||||
{
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['clicked'] = $form->isClicked();
|
||||
|
||||
if (!$options['validate']) {
|
||||
$view->vars['attr']['formnovalidate'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefault('validate', true);
|
||||
$resolver->setAllowedTypes('validate', 'bool');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return ButtonType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'submit';
|
||||
}
|
||||
}
|
49
vendor/symfony/form/Extension/Core/Type/TelType.php
vendored
Normal file
49
vendor/symfony/form/Extension/Core/Type/TelType.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class TelType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please provide a valid phone number.';
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'tel';
|
||||
}
|
||||
}
|
67
vendor/symfony/form/Extension/Core/Type/TextType.php
vendored
Normal file
67
vendor/symfony/form/Extension/Core/Type/TextType.php
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class TextType extends AbstractType implements DataTransformerInterface
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
// When empty_data is explicitly set to an empty string,
|
||||
// a string should always be returned when NULL is submitted
|
||||
// This gives more control and thus helps preventing some issues
|
||||
// with PHP 7 which allows type hinting strings in functions
|
||||
// See https://github.com/symfony/symfony/issues/5906#issuecomment-203189375
|
||||
if ('' === $options['empty_data']) {
|
||||
$builder->addViewTransformer($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'compound' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'text';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($data)
|
||||
{
|
||||
// Model data should not be transformed
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reverseTransform($data)
|
||||
{
|
||||
return $data ?? '';
|
||||
}
|
||||
}
|
44
vendor/symfony/form/Extension/Core/Type/TextareaType.php
vendored
Normal file
44
vendor/symfony/form/Extension/Core/Type/TextareaType.php
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
|
||||
class TextareaType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['pattern'] = null;
|
||||
unset($view->vars['attr']['pattern']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'textarea';
|
||||
}
|
||||
}
|
386
vendor/symfony/form/Extension/Core/Type/TimeType.php
vendored
Normal file
386
vendor/symfony/form/Extension/Core/Type/TimeType.php
vendored
Normal file
@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Exception\InvalidConfigurationException;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\ReversedTransformer;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class TimeType extends AbstractType
|
||||
{
|
||||
private const WIDGETS = [
|
||||
'text' => TextType::class,
|
||||
'choice' => ChoiceType::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$parts = ['hour'];
|
||||
$format = 'H';
|
||||
|
||||
if ($options['with_seconds'] && !$options['with_minutes']) {
|
||||
throw new InvalidConfigurationException('You cannot disable minutes if you have enabled seconds.');
|
||||
}
|
||||
|
||||
if (null !== $options['reference_date'] && $options['reference_date']->getTimezone()->getName() !== $options['model_timezone']) {
|
||||
throw new InvalidConfigurationException(sprintf('The configured "model_timezone" (%s) must match the timezone of the "reference_date" (%s).', $options['model_timezone'], $options['reference_date']->getTimezone()->getName()));
|
||||
}
|
||||
|
||||
if ($options['with_minutes']) {
|
||||
$format .= ':i';
|
||||
$parts[] = 'minute';
|
||||
}
|
||||
|
||||
if ($options['with_seconds']) {
|
||||
$format .= ':s';
|
||||
$parts[] = 'second';
|
||||
}
|
||||
|
||||
if ('single_text' === $options['widget']) {
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $e) use ($options) {
|
||||
$data = $e->getData();
|
||||
if ($data && preg_match('/^(?P<hours>\d{2}):(?P<minutes>\d{2})(?::(?P<seconds>\d{2})(?:\.\d+)?)?$/', $data, $matches)) {
|
||||
if ($options['with_seconds']) {
|
||||
// handle seconds ignored by user's browser when with_seconds enabled
|
||||
// https://codereview.chromium.org/450533009/
|
||||
$e->setData(sprintf('%s:%s:%s', $matches['hours'], $matches['minutes'], $matches['seconds'] ?? '00'));
|
||||
} else {
|
||||
$e->setData(sprintf('%s:%s', $matches['hours'], $matches['minutes']));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (null !== $options['reference_date']) {
|
||||
$format = 'Y-m-d '.$format;
|
||||
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) {
|
||||
$data = $event->getData();
|
||||
|
||||
if (preg_match('/^\d{2}:\d{2}(:\d{2})?$/', $data)) {
|
||||
$event->setData($options['reference_date']->format('Y-m-d ').$data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format));
|
||||
} else {
|
||||
$hourOptions = $minuteOptions = $secondOptions = [
|
||||
'error_bubbling' => true,
|
||||
'empty_data' => '',
|
||||
];
|
||||
// when the form is compound the entries of the array are ignored in favor of children data
|
||||
// so we need to handle the cascade setting here
|
||||
$emptyData = $builder->getEmptyData() ?: [];
|
||||
|
||||
if ($emptyData instanceof \Closure) {
|
||||
$lazyEmptyData = static function ($option) use ($emptyData) {
|
||||
return static function (FormInterface $form) use ($emptyData, $option) {
|
||||
$emptyData = $emptyData($form->getParent());
|
||||
|
||||
return $emptyData[$option] ?? '';
|
||||
};
|
||||
};
|
||||
|
||||
$hourOptions['empty_data'] = $lazyEmptyData('hour');
|
||||
} elseif (isset($emptyData['hour'])) {
|
||||
$hourOptions['empty_data'] = $emptyData['hour'];
|
||||
}
|
||||
|
||||
if (isset($options['invalid_message'])) {
|
||||
$hourOptions['invalid_message'] = $options['invalid_message'];
|
||||
$minuteOptions['invalid_message'] = $options['invalid_message'];
|
||||
$secondOptions['invalid_message'] = $options['invalid_message'];
|
||||
}
|
||||
|
||||
if (isset($options['invalid_message_parameters'])) {
|
||||
$hourOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
|
||||
$minuteOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
|
||||
$secondOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
|
||||
}
|
||||
|
||||
if ('choice' === $options['widget']) {
|
||||
$hours = $minutes = [];
|
||||
|
||||
foreach ($options['hours'] as $hour) {
|
||||
$hours[str_pad($hour, 2, '0', \STR_PAD_LEFT)] = $hour;
|
||||
}
|
||||
|
||||
// Only pass a subset of the options to children
|
||||
$hourOptions['choices'] = $hours;
|
||||
$hourOptions['placeholder'] = $options['placeholder']['hour'];
|
||||
$hourOptions['choice_translation_domain'] = $options['choice_translation_domain']['hour'];
|
||||
|
||||
if ($options['with_minutes']) {
|
||||
foreach ($options['minutes'] as $minute) {
|
||||
$minutes[str_pad($minute, 2, '0', \STR_PAD_LEFT)] = $minute;
|
||||
}
|
||||
|
||||
$minuteOptions['choices'] = $minutes;
|
||||
$minuteOptions['placeholder'] = $options['placeholder']['minute'];
|
||||
$minuteOptions['choice_translation_domain'] = $options['choice_translation_domain']['minute'];
|
||||
}
|
||||
|
||||
if ($options['with_seconds']) {
|
||||
$seconds = [];
|
||||
|
||||
foreach ($options['seconds'] as $second) {
|
||||
$seconds[str_pad($second, 2, '0', \STR_PAD_LEFT)] = $second;
|
||||
}
|
||||
|
||||
$secondOptions['choices'] = $seconds;
|
||||
$secondOptions['placeholder'] = $options['placeholder']['second'];
|
||||
$secondOptions['choice_translation_domain'] = $options['choice_translation_domain']['second'];
|
||||
}
|
||||
|
||||
// Append generic carry-along options
|
||||
foreach (['required', 'translation_domain'] as $passOpt) {
|
||||
$hourOptions[$passOpt] = $options[$passOpt];
|
||||
|
||||
if ($options['with_minutes']) {
|
||||
$minuteOptions[$passOpt] = $options[$passOpt];
|
||||
}
|
||||
|
||||
if ($options['with_seconds']) {
|
||||
$secondOptions[$passOpt] = $options[$passOpt];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$builder->add('hour', self::WIDGETS[$options['widget']], $hourOptions);
|
||||
|
||||
if ($options['with_minutes']) {
|
||||
if ($emptyData instanceof \Closure) {
|
||||
$minuteOptions['empty_data'] = $lazyEmptyData('minute');
|
||||
} elseif (isset($emptyData['minute'])) {
|
||||
$minuteOptions['empty_data'] = $emptyData['minute'];
|
||||
}
|
||||
$builder->add('minute', self::WIDGETS[$options['widget']], $minuteOptions);
|
||||
}
|
||||
|
||||
if ($options['with_seconds']) {
|
||||
if ($emptyData instanceof \Closure) {
|
||||
$secondOptions['empty_data'] = $lazyEmptyData('second');
|
||||
} elseif (isset($emptyData['second'])) {
|
||||
$secondOptions['empty_data'] = $emptyData['second'];
|
||||
}
|
||||
$builder->add('second', self::WIDGETS[$options['widget']], $secondOptions);
|
||||
}
|
||||
|
||||
$builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget'], $options['reference_date']));
|
||||
}
|
||||
|
||||
if ('datetime_immutable' === $options['input']) {
|
||||
$builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer());
|
||||
} elseif ('string' === $options['input']) {
|
||||
$builder->addModelTransformer(new ReversedTransformer(
|
||||
new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], $options['input_format'])
|
||||
));
|
||||
} elseif ('timestamp' === $options['input']) {
|
||||
$builder->addModelTransformer(new ReversedTransformer(
|
||||
new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone'])
|
||||
));
|
||||
} elseif ('array' === $options['input']) {
|
||||
$builder->addModelTransformer(new ReversedTransformer(
|
||||
new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], $parts, 'text' === $options['widget'], $options['reference_date'])
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars = array_replace($view->vars, [
|
||||
'widget' => $options['widget'],
|
||||
'with_minutes' => $options['with_minutes'],
|
||||
'with_seconds' => $options['with_seconds'],
|
||||
]);
|
||||
|
||||
// Change the input to an HTML5 time input if
|
||||
// * the widget is set to "single_text"
|
||||
// * the html5 is set to true
|
||||
if ($options['html5'] && 'single_text' === $options['widget']) {
|
||||
$view->vars['type'] = 'time';
|
||||
|
||||
// we need to force the browser to display the seconds by
|
||||
// adding the HTML attribute step if not already defined.
|
||||
// Otherwise the browser will not display and so not send the seconds
|
||||
// therefore the value will always be considered as invalid.
|
||||
if ($options['with_seconds'] && !isset($view->vars['attr']['step'])) {
|
||||
$view->vars['attr']['step'] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$compound = function (Options $options) {
|
||||
return 'single_text' !== $options['widget'];
|
||||
};
|
||||
|
||||
$placeholderDefault = function (Options $options) {
|
||||
return $options['required'] ? null : '';
|
||||
};
|
||||
|
||||
$placeholderNormalizer = function (Options $options, $placeholder) use ($placeholderDefault) {
|
||||
if (\is_array($placeholder)) {
|
||||
$default = $placeholderDefault($options);
|
||||
|
||||
return array_merge(
|
||||
['hour' => $default, 'minute' => $default, 'second' => $default],
|
||||
$placeholder
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'hour' => $placeholder,
|
||||
'minute' => $placeholder,
|
||||
'second' => $placeholder,
|
||||
];
|
||||
};
|
||||
|
||||
$choiceTranslationDomainNormalizer = function (Options $options, $choiceTranslationDomain) {
|
||||
if (\is_array($choiceTranslationDomain)) {
|
||||
$default = false;
|
||||
|
||||
return array_replace(
|
||||
['hour' => $default, 'minute' => $default, 'second' => $default],
|
||||
$choiceTranslationDomain
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'hour' => $choiceTranslationDomain,
|
||||
'minute' => $choiceTranslationDomain,
|
||||
'second' => $choiceTranslationDomain,
|
||||
];
|
||||
};
|
||||
|
||||
$modelTimezone = static function (Options $options, $value): ?string {
|
||||
if (null !== $value) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (null !== $options['reference_date']) {
|
||||
return $options['reference_date']->getTimezone()->getName();
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
$viewTimezone = static function (Options $options, $value): ?string {
|
||||
if (null !== $value) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (null !== $options['model_timezone'] && null === $options['reference_date']) {
|
||||
return $options['model_timezone'];
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
$resolver->setDefaults([
|
||||
'hours' => range(0, 23),
|
||||
'minutes' => range(0, 59),
|
||||
'seconds' => range(0, 59),
|
||||
'widget' => 'choice',
|
||||
'input' => 'datetime',
|
||||
'input_format' => 'H:i:s',
|
||||
'with_minutes' => true,
|
||||
'with_seconds' => false,
|
||||
'model_timezone' => $modelTimezone,
|
||||
'view_timezone' => $viewTimezone,
|
||||
'reference_date' => null,
|
||||
'placeholder' => $placeholderDefault,
|
||||
'html5' => true,
|
||||
// Don't modify \DateTime classes by reference, we treat
|
||||
// them like immutable value objects
|
||||
'by_reference' => false,
|
||||
'error_bubbling' => false,
|
||||
// If initialized with a \DateTime object, FormType initializes
|
||||
// this option to "\DateTime". Since the internal, normalized
|
||||
// representation is not \DateTime, but an array, we need to unset
|
||||
// this option.
|
||||
'data_class' => null,
|
||||
'empty_data' => function (Options $options) {
|
||||
return $options['compound'] ? [] : '';
|
||||
},
|
||||
'compound' => $compound,
|
||||
'choice_translation_domain' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a valid time.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setNormalizer('view_timezone', function (Options $options, $viewTimezone): ?string {
|
||||
if (null !== $options['model_timezone'] && $viewTimezone !== $options['model_timezone'] && null === $options['reference_date']) {
|
||||
throw new LogicException('Using different values for the "model_timezone" and "view_timezone" options without configuring a reference date is not supported.');
|
||||
}
|
||||
|
||||
return $viewTimezone;
|
||||
});
|
||||
|
||||
$resolver->setNormalizer('placeholder', $placeholderNormalizer);
|
||||
$resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer);
|
||||
|
||||
$resolver->setAllowedValues('input', [
|
||||
'datetime',
|
||||
'datetime_immutable',
|
||||
'string',
|
||||
'timestamp',
|
||||
'array',
|
||||
]);
|
||||
$resolver->setAllowedValues('widget', [
|
||||
'single_text',
|
||||
'text',
|
||||
'choice',
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('hours', 'array');
|
||||
$resolver->setAllowedTypes('minutes', 'array');
|
||||
$resolver->setAllowedTypes('seconds', 'array');
|
||||
$resolver->setAllowedTypes('input_format', 'string');
|
||||
$resolver->setAllowedTypes('model_timezone', ['null', 'string']);
|
||||
$resolver->setAllowedTypes('view_timezone', ['null', 'string']);
|
||||
$resolver->setAllowedTypes('reference_date', ['null', \DateTimeInterface::class]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'time';
|
||||
}
|
||||
}
|
143
vendor/symfony/form/Extension/Core/Type/TimezoneType.php
vendored
Normal file
143
vendor/symfony/form/Extension/Core/Type/TimezoneType.php
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\ChoiceList\ChoiceList;
|
||||
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeZoneToStringTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\IntlTimeZoneToStringTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Intl\Intl;
|
||||
use Symfony\Component\Intl\Timezones;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class TimezoneType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
if ('datetimezone' === $options['input']) {
|
||||
$builder->addModelTransformer(new DateTimeZoneToStringTransformer($options['multiple']));
|
||||
} elseif ('intltimezone' === $options['input']) {
|
||||
$builder->addModelTransformer(new IntlTimeZoneToStringTransformer($options['multiple']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'intl' => false,
|
||||
'choice_loader' => function (Options $options) {
|
||||
$input = $options['input'];
|
||||
|
||||
if ($options['intl']) {
|
||||
if (!class_exists(Intl::class)) {
|
||||
throw new LogicException(sprintf('The "symfony/intl" component is required to use "%s" with option "intl=true". Try running "composer require symfony/intl".', static::class));
|
||||
}
|
||||
|
||||
$choiceTranslationLocale = $options['choice_translation_locale'];
|
||||
|
||||
return ChoiceList::loader($this, new IntlCallbackChoiceLoader(function () use ($input, $choiceTranslationLocale) {
|
||||
return self::getIntlTimezones($input, $choiceTranslationLocale);
|
||||
}), [$input, $choiceTranslationLocale]);
|
||||
}
|
||||
|
||||
return ChoiceList::lazy($this, function () use ($input) {
|
||||
return self::getPhpTimezones($input);
|
||||
}, $input);
|
||||
},
|
||||
'choice_translation_domain' => false,
|
||||
'choice_translation_locale' => null,
|
||||
'input' => 'string',
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please select a valid timezone.';
|
||||
},
|
||||
'regions' => \DateTimeZone::ALL,
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('intl', ['bool']);
|
||||
|
||||
$resolver->setAllowedTypes('choice_translation_locale', ['null', 'string']);
|
||||
$resolver->setNormalizer('choice_translation_locale', function (Options $options, $value) {
|
||||
if (null !== $value && !$options['intl']) {
|
||||
throw new LogicException('The "choice_translation_locale" option can only be used if the "intl" option is set to true.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
});
|
||||
|
||||
$resolver->setAllowedValues('input', ['string', 'datetimezone', 'intltimezone']);
|
||||
$resolver->setNormalizer('input', function (Options $options, $value) {
|
||||
if ('intltimezone' === $value && !class_exists(\IntlTimeZone::class)) {
|
||||
throw new LogicException('Cannot use "intltimezone" input because the PHP intl extension is not available.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'timezone';
|
||||
}
|
||||
|
||||
private static function getPhpTimezones(string $input): array
|
||||
{
|
||||
$timezones = [];
|
||||
|
||||
foreach (\DateTimeZone::listIdentifiers(\DateTimeZone::ALL) as $timezone) {
|
||||
if ('intltimezone' === $input && 'Etc/Unknown' === \IntlTimeZone::createTimeZone($timezone)->getID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$timezones[str_replace(['/', '_'], [' / ', ' '], $timezone)] = $timezone;
|
||||
}
|
||||
|
||||
return $timezones;
|
||||
}
|
||||
|
||||
private static function getIntlTimezones(string $input, string $locale = null): array
|
||||
{
|
||||
$timezones = array_flip(Timezones::getNames($locale));
|
||||
|
||||
if ('intltimezone' === $input) {
|
||||
foreach ($timezones as $name => $timezone) {
|
||||
if ('Etc/Unknown' === \IntlTimeZone::createTimeZone($timezone)->getID()) {
|
||||
unset($timezones[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $timezones;
|
||||
}
|
||||
}
|
45
vendor/symfony/form/Extension/Core/Type/TransformationFailureExtension.php
vendored
Normal file
45
vendor/symfony/form/Extension/Core/Type/TransformationFailureExtension.php
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractTypeExtension;
|
||||
use Symfony\Component\Form\Extension\Core\EventListener\TransformationFailureListener;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @author Christian Flothmann <christian.flothmann@sensiolabs.de>
|
||||
*/
|
||||
class TransformationFailureExtension extends AbstractTypeExtension
|
||||
{
|
||||
private $translator;
|
||||
|
||||
public function __construct(TranslatorInterface $translator = null)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
if (!isset($options['constraints'])) {
|
||||
$builder->addEventSubscriber(new TransformationFailureListener($this->translator));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getExtendedTypes(): iterable
|
||||
{
|
||||
return [FormType::class];
|
||||
}
|
||||
}
|
49
vendor/symfony/form/Extension/Core/Type/UlidType.php
vendored
Normal file
49
vendor/symfony/form/Extension/Core/Type/UlidType.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\UlidToStringTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @author Pavel Dyakonov <wapinet@mail.ru>
|
||||
*/
|
||||
class UlidType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->addViewTransformer(new UlidToStringTransformer())
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'compound' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a valid ULID.';
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
77
vendor/symfony/form/Extension/Core/Type/UrlType.php
vendored
Normal file
77
vendor/symfony/form/Extension/Core/Type/UrlType.php
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class UrlType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
if (null !== $options['default_protocol']) {
|
||||
$builder->addEventSubscriber(new FixUrlProtocolListener($options['default_protocol']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
if ($options['default_protocol']) {
|
||||
$view->vars['attr']['inputmode'] = 'url';
|
||||
$view->vars['type'] = 'text';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'default_protocol' => 'http',
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a valid URL.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('default_protocol', ['null', 'string']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'url';
|
||||
}
|
||||
}
|
49
vendor/symfony/form/Extension/Core/Type/UuidType.php
vendored
Normal file
49
vendor/symfony/form/Extension/Core/Type/UuidType.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\UuidToStringTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @author Pavel Dyakonov <wapinet@mail.ru>
|
||||
*/
|
||||
class UuidType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->addViewTransformer(new UuidToStringTransformer())
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'compound' => false,
|
||||
'invalid_message' => function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true)
|
||||
? $previousValue
|
||||
: 'Please enter a valid UUID.';
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
195
vendor/symfony/form/Extension/Core/Type/WeekType.php
vendored
Normal file
195
vendor/symfony/form/Extension/Core/Type/WeekType.php
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\WeekToArrayTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\ReversedTransformer;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class WeekType extends AbstractType
|
||||
{
|
||||
private const WIDGETS = [
|
||||
'text' => IntegerType::class,
|
||||
'choice' => ChoiceType::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
if ('string' === $options['input']) {
|
||||
$builder->addModelTransformer(new WeekToArrayTransformer());
|
||||
}
|
||||
|
||||
if ('single_text' === $options['widget']) {
|
||||
$builder->addViewTransformer(new ReversedTransformer(new WeekToArrayTransformer()));
|
||||
} else {
|
||||
$yearOptions = $weekOptions = [
|
||||
'error_bubbling' => true,
|
||||
'empty_data' => '',
|
||||
];
|
||||
// when the form is compound the entries of the array are ignored in favor of children data
|
||||
// so we need to handle the cascade setting here
|
||||
$emptyData = $builder->getEmptyData() ?: [];
|
||||
|
||||
$yearOptions['empty_data'] = $emptyData['year'] ?? '';
|
||||
$weekOptions['empty_data'] = $emptyData['week'] ?? '';
|
||||
|
||||
if (isset($options['invalid_message'])) {
|
||||
$yearOptions['invalid_message'] = $options['invalid_message'];
|
||||
$weekOptions['invalid_message'] = $options['invalid_message'];
|
||||
}
|
||||
|
||||
if (isset($options['invalid_message_parameters'])) {
|
||||
$yearOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
|
||||
$weekOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
|
||||
}
|
||||
|
||||
if ('choice' === $options['widget']) {
|
||||
// Only pass a subset of the options to children
|
||||
$yearOptions['choices'] = array_combine($options['years'], $options['years']);
|
||||
$yearOptions['placeholder'] = $options['placeholder']['year'];
|
||||
$yearOptions['choice_translation_domain'] = $options['choice_translation_domain']['year'];
|
||||
|
||||
$weekOptions['choices'] = array_combine($options['weeks'], $options['weeks']);
|
||||
$weekOptions['placeholder'] = $options['placeholder']['week'];
|
||||
$weekOptions['choice_translation_domain'] = $options['choice_translation_domain']['week'];
|
||||
|
||||
// Append generic carry-along options
|
||||
foreach (['required', 'translation_domain'] as $passOpt) {
|
||||
$yearOptions[$passOpt] = $options[$passOpt];
|
||||
$weekOptions[$passOpt] = $options[$passOpt];
|
||||
}
|
||||
}
|
||||
|
||||
$builder->add('year', self::WIDGETS[$options['widget']], $yearOptions);
|
||||
$builder->add('week', self::WIDGETS[$options['widget']], $weekOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['widget'] = $options['widget'];
|
||||
|
||||
if ($options['html5']) {
|
||||
$view->vars['type'] = 'week';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$compound = function (Options $options) {
|
||||
return 'single_text' !== $options['widget'];
|
||||
};
|
||||
|
||||
$placeholderDefault = function (Options $options) {
|
||||
return $options['required'] ? null : '';
|
||||
};
|
||||
|
||||
$placeholderNormalizer = function (Options $options, $placeholder) use ($placeholderDefault) {
|
||||
if (\is_array($placeholder)) {
|
||||
$default = $placeholderDefault($options);
|
||||
|
||||
return array_merge(
|
||||
['year' => $default, 'week' => $default],
|
||||
$placeholder
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'year' => $placeholder,
|
||||
'week' => $placeholder,
|
||||
];
|
||||
};
|
||||
|
||||
$choiceTranslationDomainNormalizer = function (Options $options, $choiceTranslationDomain) {
|
||||
if (\is_array($choiceTranslationDomain)) {
|
||||
$default = false;
|
||||
|
||||
return array_replace(
|
||||
['year' => $default, 'week' => $default],
|
||||
$choiceTranslationDomain
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'year' => $choiceTranslationDomain,
|
||||
'week' => $choiceTranslationDomain,
|
||||
];
|
||||
};
|
||||
|
||||
$resolver->setDefaults([
|
||||
'years' => range(date('Y') - 10, date('Y') + 10),
|
||||
'weeks' => array_combine(range(1, 53), range(1, 53)),
|
||||
'widget' => 'single_text',
|
||||
'input' => 'array',
|
||||
'placeholder' => $placeholderDefault,
|
||||
'html5' => static function (Options $options) {
|
||||
return 'single_text' === $options['widget'];
|
||||
},
|
||||
'error_bubbling' => false,
|
||||
'empty_data' => function (Options $options) {
|
||||
return $options['compound'] ? [] : '';
|
||||
},
|
||||
'compound' => $compound,
|
||||
'choice_translation_domain' => false,
|
||||
'invalid_message' => static function (Options $options, $previousValue) {
|
||||
return ($options['legacy_error_messages'] ?? true) ? $previousValue : 'Please enter a valid week.';
|
||||
},
|
||||
]);
|
||||
|
||||
$resolver->setNormalizer('placeholder', $placeholderNormalizer);
|
||||
$resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer);
|
||||
$resolver->setNormalizer('html5', function (Options $options, $html5) {
|
||||
if ($html5 && 'single_text' !== $options['widget']) {
|
||||
throw new LogicException(sprintf('The "widget" option of "%s" must be set to "single_text" when the "html5" option is enabled.', self::class));
|
||||
}
|
||||
|
||||
return $html5;
|
||||
});
|
||||
|
||||
$resolver->setAllowedValues('input', [
|
||||
'string',
|
||||
'array',
|
||||
]);
|
||||
|
||||
$resolver->setAllowedValues('widget', [
|
||||
'single_text',
|
||||
'text',
|
||||
'choice',
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('years', 'int[]');
|
||||
$resolver->setAllowedTypes('weeks', 'int[]');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'week';
|
||||
}
|
||||
}
|
45
vendor/symfony/form/Extension/Csrf/CsrfExtension.php
vendored
Normal file
45
vendor/symfony/form/Extension/Csrf/CsrfExtension.php
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Csrf;
|
||||
|
||||
use Symfony\Component\Form\AbstractExtension;
|
||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* This extension protects forms by using a CSRF token.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class CsrfExtension extends AbstractExtension
|
||||
{
|
||||
private $tokenManager;
|
||||
private $translator;
|
||||
private $translationDomain;
|
||||
|
||||
public function __construct(CsrfTokenManagerInterface $tokenManager, TranslatorInterface $translator = null, string $translationDomain = null)
|
||||
{
|
||||
$this->tokenManager = $tokenManager;
|
||||
$this->translator = $translator;
|
||||
$this->translationDomain = $translationDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTypeExtensions()
|
||||
{
|
||||
return [
|
||||
new Type\FormTypeCsrfExtension($this->tokenManager, true, '_token', $this->translator, $this->translationDomain),
|
||||
];
|
||||
}
|
||||
}
|
81
vendor/symfony/form/Extension/Csrf/EventListener/CsrfValidationListener.php
vendored
Normal file
81
vendor/symfony/form/Extension/Csrf/EventListener/CsrfValidationListener.php
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Csrf\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\Util\ServerParams;
|
||||
use Symfony\Component\Security\Csrf\CsrfToken;
|
||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class CsrfValidationListener implements EventSubscriberInterface
|
||||
{
|
||||
private $fieldName;
|
||||
private $tokenManager;
|
||||
private $tokenId;
|
||||
private $errorMessage;
|
||||
private $translator;
|
||||
private $translationDomain;
|
||||
private $serverParams;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
FormEvents::PRE_SUBMIT => 'preSubmit',
|
||||
];
|
||||
}
|
||||
|
||||
public function __construct(string $fieldName, CsrfTokenManagerInterface $tokenManager, string $tokenId, string $errorMessage, TranslatorInterface $translator = null, string $translationDomain = null, ServerParams $serverParams = null)
|
||||
{
|
||||
$this->fieldName = $fieldName;
|
||||
$this->tokenManager = $tokenManager;
|
||||
$this->tokenId = $tokenId;
|
||||
$this->errorMessage = $errorMessage;
|
||||
$this->translator = $translator;
|
||||
$this->translationDomain = $translationDomain;
|
||||
$this->serverParams = $serverParams ?? new ServerParams();
|
||||
}
|
||||
|
||||
public function preSubmit(FormEvent $event)
|
||||
{
|
||||
$form = $event->getForm();
|
||||
$postRequestSizeExceeded = 'POST' === $form->getConfig()->getMethod() && $this->serverParams->hasPostMaxSizeBeenExceeded();
|
||||
|
||||
if ($form->isRoot() && $form->getConfig()->getOption('compound') && !$postRequestSizeExceeded) {
|
||||
$data = $event->getData();
|
||||
|
||||
$csrfValue = \is_string($data[$this->fieldName] ?? null) ? $data[$this->fieldName] : null;
|
||||
$csrfToken = new CsrfToken($this->tokenId, $csrfValue);
|
||||
|
||||
if (null === $csrfValue || !$this->tokenManager->isTokenValid($csrfToken)) {
|
||||
$errorMessage = $this->errorMessage;
|
||||
|
||||
if (null !== $this->translator) {
|
||||
$errorMessage = $this->translator->trans($errorMessage, [], $this->translationDomain);
|
||||
}
|
||||
|
||||
$form->addError(new FormError($errorMessage, $errorMessage, [], null, $csrfToken));
|
||||
}
|
||||
|
||||
if (\is_array($data)) {
|
||||
unset($data[$this->fieldName]);
|
||||
$event->setData($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
110
vendor/symfony/form/Extension/Csrf/Type/FormTypeCsrfExtension.php
vendored
Normal file
110
vendor/symfony/form/Extension/Csrf/Type/FormTypeCsrfExtension.php
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Csrf\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractTypeExtension;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\Util\ServerParams;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FormTypeCsrfExtension extends AbstractTypeExtension
|
||||
{
|
||||
private $defaultTokenManager;
|
||||
private $defaultEnabled;
|
||||
private $defaultFieldName;
|
||||
private $translator;
|
||||
private $translationDomain;
|
||||
private $serverParams;
|
||||
|
||||
public function __construct(CsrfTokenManagerInterface $defaultTokenManager, bool $defaultEnabled = true, string $defaultFieldName = '_token', TranslatorInterface $translator = null, string $translationDomain = null, ServerParams $serverParams = null)
|
||||
{
|
||||
$this->defaultTokenManager = $defaultTokenManager;
|
||||
$this->defaultEnabled = $defaultEnabled;
|
||||
$this->defaultFieldName = $defaultFieldName;
|
||||
$this->translator = $translator;
|
||||
$this->translationDomain = $translationDomain;
|
||||
$this->serverParams = $serverParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a CSRF field to the form when the CSRF protection is enabled.
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
if (!$options['csrf_protection']) {
|
||||
return;
|
||||
}
|
||||
|
||||
$builder
|
||||
->addEventSubscriber(new CsrfValidationListener(
|
||||
$options['csrf_field_name'],
|
||||
$options['csrf_token_manager'],
|
||||
$options['csrf_token_id'] ?: ($builder->getName() ?: \get_class($builder->getType()->getInnerType())),
|
||||
$options['csrf_message'],
|
||||
$this->translator,
|
||||
$this->translationDomain,
|
||||
$this->serverParams
|
||||
))
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a CSRF field to the root form view.
|
||||
*/
|
||||
public function finishView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
if ($options['csrf_protection'] && !$view->parent && $options['compound']) {
|
||||
$factory = $form->getConfig()->getFormFactory();
|
||||
$tokenId = $options['csrf_token_id'] ?: ($form->getName() ?: \get_class($form->getConfig()->getType()->getInnerType()));
|
||||
$data = (string) $options['csrf_token_manager']->getToken($tokenId);
|
||||
|
||||
$csrfForm = $factory->createNamed($options['csrf_field_name'], HiddenType::class, $data, [
|
||||
'block_prefix' => 'csrf_token',
|
||||
'mapped' => false,
|
||||
]);
|
||||
|
||||
$view->children[$options['csrf_field_name']] = $csrfForm->createView($view);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'csrf_protection' => $this->defaultEnabled,
|
||||
'csrf_field_name' => $this->defaultFieldName,
|
||||
'csrf_message' => 'The CSRF token is invalid. Please try to resubmit the form.',
|
||||
'csrf_token_manager' => $this->defaultTokenManager,
|
||||
'csrf_token_id' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getExtendedTypes(): iterable
|
||||
{
|
||||
return [FormType::class];
|
||||
}
|
||||
}
|
40
vendor/symfony/form/Extension/DataCollector/DataCollectorExtension.php
vendored
Normal file
40
vendor/symfony/form/Extension/DataCollector/DataCollectorExtension.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\Form\AbstractExtension;
|
||||
|
||||
/**
|
||||
* Extension for collecting data of the forms on a page.
|
||||
*
|
||||
* @author Robert Schönthal <robert.schoenthal@gmail.com>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class DataCollectorExtension extends AbstractExtension
|
||||
{
|
||||
private $dataCollector;
|
||||
|
||||
public function __construct(FormDataCollectorInterface $dataCollector)
|
||||
{
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTypeExtensions()
|
||||
{
|
||||
return [
|
||||
new Type\DataCollectorTypeExtension($this->dataCollector),
|
||||
];
|
||||
}
|
||||
}
|
75
vendor/symfony/form/Extension/DataCollector/EventListener/DataCollectorListener.php
vendored
Normal file
75
vendor/symfony/form/Extension/DataCollector/EventListener/DataCollectorListener.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
/**
|
||||
* Listener that invokes a data collector for the {@link FormEvents::POST_SET_DATA}
|
||||
* and {@link FormEvents::POST_SUBMIT} events.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class DataCollectorListener implements EventSubscriberInterface
|
||||
{
|
||||
private $dataCollector;
|
||||
|
||||
public function __construct(FormDataCollectorInterface $dataCollector)
|
||||
{
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
// High priority in order to be called as soon as possible
|
||||
FormEvents::POST_SET_DATA => ['postSetData', 255],
|
||||
// Low priority in order to be called as late as possible
|
||||
FormEvents::POST_SUBMIT => ['postSubmit', -255],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for the {@link FormEvents::POST_SET_DATA} event.
|
||||
*/
|
||||
public function postSetData(FormEvent $event)
|
||||
{
|
||||
if ($event->getForm()->isRoot()) {
|
||||
// Collect basic information about each form
|
||||
$this->dataCollector->collectConfiguration($event->getForm());
|
||||
|
||||
// Collect the default data
|
||||
$this->dataCollector->collectDefaultData($event->getForm());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for the {@link FormEvents::POST_SUBMIT} event.
|
||||
*/
|
||||
public function postSubmit(FormEvent $event)
|
||||
{
|
||||
if ($event->getForm()->isRoot()) {
|
||||
// Collect the submitted data of each form
|
||||
$this->dataCollector->collectSubmittedData($event->getForm());
|
||||
|
||||
// Assemble a form tree
|
||||
// This is done again after the view is built, but we need it here as the view is not always created.
|
||||
$this->dataCollector->buildPreliminaryFormTree($event->getForm());
|
||||
}
|
||||
}
|
||||
}
|
343
vendor/symfony/form/Extension/DataCollector/FormDataCollector.php
vendored
Normal file
343
vendor/symfony/form/Extension/DataCollector/FormDataCollector.php
vendored
Normal file
@ -0,0 +1,343 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||
use Symfony\Component\VarDumper\Caster\Caster;
|
||||
use Symfony\Component\VarDumper\Caster\ClassStub;
|
||||
use Symfony\Component\VarDumper\Caster\StubCaster;
|
||||
use Symfony\Component\VarDumper\Cloner\Stub;
|
||||
|
||||
/**
|
||||
* Data collector for {@link FormInterface} instances.
|
||||
*
|
||||
* @author Robert Schönthal <robert.schoenthal@gmail.com>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class FormDataCollector extends DataCollector implements FormDataCollectorInterface
|
||||
{
|
||||
private $dataExtractor;
|
||||
|
||||
/**
|
||||
* Stores the collected data per {@link FormInterface} instance.
|
||||
*
|
||||
* Uses the hashes of the forms as keys. This is preferable over using
|
||||
* {@link \SplObjectStorage}, because in this way no references are kept
|
||||
* to the {@link FormInterface} instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $dataByForm;
|
||||
|
||||
/**
|
||||
* Stores the collected data per {@link FormView} instance.
|
||||
*
|
||||
* Uses the hashes of the views as keys. This is preferable over using
|
||||
* {@link \SplObjectStorage}, because in this way no references are kept
|
||||
* to the {@link FormView} instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $dataByView;
|
||||
|
||||
/**
|
||||
* Connects {@link FormView} with {@link FormInterface} instances.
|
||||
*
|
||||
* Uses the hashes of the views as keys and the hashes of the forms as
|
||||
* values. This is preferable over storing the objects directly, because
|
||||
* this way they can safely be discarded by the GC.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $formsByView;
|
||||
|
||||
public function __construct(FormDataExtractorInterface $dataExtractor)
|
||||
{
|
||||
if (!class_exists(ClassStub::class)) {
|
||||
throw new \LogicException(sprintf('The VarDumper component is needed for using the "%s" class. Install symfony/var-dumper version 3.4 or above.', __CLASS__));
|
||||
}
|
||||
|
||||
$this->dataExtractor = $dataExtractor;
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. The data is collected during the form event listeners.
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Throwable $exception = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->data = [
|
||||
'forms' => [],
|
||||
'forms_by_hash' => [],
|
||||
'nb_errors' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function associateFormWithView(FormInterface $form, FormView $view)
|
||||
{
|
||||
$this->formsByView[spl_object_hash($view)] = spl_object_hash($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectConfiguration(FormInterface $form)
|
||||
{
|
||||
$hash = spl_object_hash($form);
|
||||
|
||||
if (!isset($this->dataByForm[$hash])) {
|
||||
$this->dataByForm[$hash] = [];
|
||||
}
|
||||
|
||||
$this->dataByForm[$hash] = array_replace(
|
||||
$this->dataByForm[$hash],
|
||||
$this->dataExtractor->extractConfiguration($form)
|
||||
);
|
||||
|
||||
foreach ($form as $child) {
|
||||
$this->collectConfiguration($child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectDefaultData(FormInterface $form)
|
||||
{
|
||||
$hash = spl_object_hash($form);
|
||||
|
||||
if (!isset($this->dataByForm[$hash])) {
|
||||
// field was created by form event
|
||||
$this->collectConfiguration($form);
|
||||
}
|
||||
|
||||
$this->dataByForm[$hash] = array_replace(
|
||||
$this->dataByForm[$hash],
|
||||
$this->dataExtractor->extractDefaultData($form)
|
||||
);
|
||||
|
||||
foreach ($form as $child) {
|
||||
$this->collectDefaultData($child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectSubmittedData(FormInterface $form)
|
||||
{
|
||||
$hash = spl_object_hash($form);
|
||||
|
||||
if (!isset($this->dataByForm[$hash])) {
|
||||
// field was created by form event
|
||||
$this->collectConfiguration($form);
|
||||
$this->collectDefaultData($form);
|
||||
}
|
||||
|
||||
$this->dataByForm[$hash] = array_replace(
|
||||
$this->dataByForm[$hash],
|
||||
$this->dataExtractor->extractSubmittedData($form)
|
||||
);
|
||||
|
||||
// Count errors
|
||||
if (isset($this->dataByForm[$hash]['errors'])) {
|
||||
$this->data['nb_errors'] += \count($this->dataByForm[$hash]['errors']);
|
||||
}
|
||||
|
||||
foreach ($form as $child) {
|
||||
$this->collectSubmittedData($child);
|
||||
|
||||
// Expand current form if there are children with errors
|
||||
if (empty($this->dataByForm[$hash]['has_children_error'])) {
|
||||
$childData = $this->dataByForm[spl_object_hash($child)];
|
||||
$this->dataByForm[$hash]['has_children_error'] = !empty($childData['has_children_error']) || !empty($childData['errors']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectViewVariables(FormView $view)
|
||||
{
|
||||
$hash = spl_object_hash($view);
|
||||
|
||||
if (!isset($this->dataByView[$hash])) {
|
||||
$this->dataByView[$hash] = [];
|
||||
}
|
||||
|
||||
$this->dataByView[$hash] = array_replace(
|
||||
$this->dataByView[$hash],
|
||||
$this->dataExtractor->extractViewVariables($view)
|
||||
);
|
||||
|
||||
foreach ($view->children as $child) {
|
||||
$this->collectViewVariables($child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildPreliminaryFormTree(FormInterface $form)
|
||||
{
|
||||
$this->data['forms'][$form->getName()] = &$this->recursiveBuildPreliminaryFormTree($form, $this->data['forms_by_hash']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFinalFormTree(FormInterface $form, FormView $view)
|
||||
{
|
||||
$this->data['forms'][$form->getName()] = &$this->recursiveBuildFinalFormTree($form, $view, $this->data['forms_by_hash']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function __sleep(): array
|
||||
{
|
||||
foreach ($this->data['forms_by_hash'] as &$form) {
|
||||
if (isset($form['type_class']) && !$form['type_class'] instanceof ClassStub) {
|
||||
$form['type_class'] = new ClassStub($form['type_class']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->data = $this->cloneVar($this->data);
|
||||
|
||||
return parent::__sleep();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCasters(): array
|
||||
{
|
||||
return parent::getCasters() + [
|
||||
\Exception::class => function (\Exception $e, array $a, Stub $s) {
|
||||
foreach (["\0Exception\0previous", "\0Exception\0trace"] as $k) {
|
||||
if (isset($a[$k])) {
|
||||
unset($a[$k]);
|
||||
++$s->cut;
|
||||
}
|
||||
}
|
||||
|
||||
return $a;
|
||||
},
|
||||
FormInterface::class => function (FormInterface $f, array $a) {
|
||||
return [
|
||||
Caster::PREFIX_VIRTUAL.'name' => $f->getName(),
|
||||
Caster::PREFIX_VIRTUAL.'type_class' => new ClassStub(\get_class($f->getConfig()->getType()->getInnerType())),
|
||||
];
|
||||
},
|
||||
FormView::class => [StubCaster::class, 'cutInternals'],
|
||||
ConstraintViolationInterface::class => function (ConstraintViolationInterface $v, array $a) {
|
||||
return [
|
||||
Caster::PREFIX_VIRTUAL.'root' => $v->getRoot(),
|
||||
Caster::PREFIX_VIRTUAL.'path' => $v->getPropertyPath(),
|
||||
Caster::PREFIX_VIRTUAL.'value' => $v->getInvalidValue(),
|
||||
];
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
private function &recursiveBuildPreliminaryFormTree(FormInterface $form, array &$outputByHash)
|
||||
{
|
||||
$hash = spl_object_hash($form);
|
||||
|
||||
$output = &$outputByHash[$hash];
|
||||
$output = $this->dataByForm[$hash]
|
||||
?? [];
|
||||
|
||||
$output['children'] = [];
|
||||
|
||||
foreach ($form as $name => $child) {
|
||||
$output['children'][$name] = &$this->recursiveBuildPreliminaryFormTree($child, $outputByHash);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function &recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, array &$outputByHash)
|
||||
{
|
||||
$viewHash = spl_object_hash($view);
|
||||
$formHash = null;
|
||||
|
||||
if (null !== $form) {
|
||||
$formHash = spl_object_hash($form);
|
||||
} elseif (isset($this->formsByView[$viewHash])) {
|
||||
// The FormInterface instance of the CSRF token is never contained in
|
||||
// the FormInterface tree of the form, so we need to get the
|
||||
// corresponding FormInterface instance for its view in a different way
|
||||
$formHash = $this->formsByView[$viewHash];
|
||||
}
|
||||
if (null !== $formHash) {
|
||||
$output = &$outputByHash[$formHash];
|
||||
}
|
||||
|
||||
$output = $this->dataByView[$viewHash]
|
||||
?? [];
|
||||
|
||||
if (null !== $formHash) {
|
||||
$output = array_replace(
|
||||
$output,
|
||||
$this->dataByForm[$formHash]
|
||||
?? []
|
||||
);
|
||||
}
|
||||
|
||||
$output['children'] = [];
|
||||
|
||||
foreach ($view->children as $name => $childView) {
|
||||
// The CSRF token, for example, is never added to the form tree.
|
||||
// It is only present in the view.
|
||||
$childForm = null !== $form && $form->has($name)
|
||||
? $form->get($name)
|
||||
: null;
|
||||
|
||||
$output['children'][$name] = &$this->recursiveBuildFinalFormTree($childForm, $childView, $outputByHash);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
85
vendor/symfony/form/Extension/DataCollector/FormDataCollectorInterface.php
vendored
Normal file
85
vendor/symfony/form/Extension/DataCollector/FormDataCollectorInterface.php
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
|
||||
/**
|
||||
* Collects and structures information about forms.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface FormDataCollectorInterface extends DataCollectorInterface
|
||||
{
|
||||
/**
|
||||
* Stores configuration data of the given form and its children.
|
||||
*/
|
||||
public function collectConfiguration(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Stores the default data of the given form and its children.
|
||||
*/
|
||||
public function collectDefaultData(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Stores the submitted data of the given form and its children.
|
||||
*/
|
||||
public function collectSubmittedData(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Stores the view variables of the given form view and its children.
|
||||
*/
|
||||
public function collectViewVariables(FormView $view);
|
||||
|
||||
/**
|
||||
* Specifies that the given objects represent the same conceptual form.
|
||||
*/
|
||||
public function associateFormWithView(FormInterface $form, FormView $view);
|
||||
|
||||
/**
|
||||
* Assembles the data collected about the given form and its children as
|
||||
* a tree-like data structure.
|
||||
*
|
||||
* The result can be queried using {@link getData()}.
|
||||
*/
|
||||
public function buildPreliminaryFormTree(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Assembles the data collected about the given form and its children as
|
||||
* a tree-like data structure.
|
||||
*
|
||||
* The result can be queried using {@link getData()}.
|
||||
*
|
||||
* Contrary to {@link buildPreliminaryFormTree()}, a {@link FormView}
|
||||
* object has to be passed. The tree structure of this view object will be
|
||||
* used for structuring the resulting data. That means, if a child is
|
||||
* present in the view, but not in the form, it will be present in the final
|
||||
* data array anyway.
|
||||
*
|
||||
* When {@link FormView} instances are present in the view tree, for which
|
||||
* no corresponding {@link FormInterface} objects can be found in the form
|
||||
* tree, only the view data will be included in the result. If a
|
||||
* corresponding {@link FormInterface} exists otherwise, call
|
||||
* {@link associateFormWithView()} before calling this method.
|
||||
*/
|
||||
public function buildFinalFormTree(FormInterface $form, FormView $view);
|
||||
|
||||
/**
|
||||
* Returns all collected data.
|
||||
*
|
||||
* @return array|Data
|
||||
*/
|
||||
public function getData();
|
||||
}
|
168
vendor/symfony/form/Extension/DataCollector/FormDataExtractor.php
vendored
Normal file
168
vendor/symfony/form/Extension/DataCollector/FormDataExtractor.php
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link FormDataExtractorInterface}.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FormDataExtractor implements FormDataExtractorInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extractConfiguration(FormInterface $form)
|
||||
{
|
||||
$data = [
|
||||
'id' => $this->buildId($form),
|
||||
'name' => $form->getName(),
|
||||
'type_class' => \get_class($form->getConfig()->getType()->getInnerType()),
|
||||
'synchronized' => $form->isSynchronized(),
|
||||
'passed_options' => [],
|
||||
'resolved_options' => [],
|
||||
];
|
||||
|
||||
foreach ($form->getConfig()->getAttribute('data_collector/passed_options', []) as $option => $value) {
|
||||
$data['passed_options'][$option] = $value;
|
||||
}
|
||||
|
||||
foreach ($form->getConfig()->getOptions() as $option => $value) {
|
||||
$data['resolved_options'][$option] = $value;
|
||||
}
|
||||
|
||||
ksort($data['passed_options']);
|
||||
ksort($data['resolved_options']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extractDefaultData(FormInterface $form)
|
||||
{
|
||||
$data = [
|
||||
'default_data' => [
|
||||
'norm' => $form->getNormData(),
|
||||
],
|
||||
'submitted_data' => [],
|
||||
];
|
||||
|
||||
if ($form->getData() !== $form->getNormData()) {
|
||||
$data['default_data']['model'] = $form->getData();
|
||||
}
|
||||
|
||||
if ($form->getViewData() !== $form->getNormData()) {
|
||||
$data['default_data']['view'] = $form->getViewData();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extractSubmittedData(FormInterface $form)
|
||||
{
|
||||
$data = [
|
||||
'submitted_data' => [
|
||||
'norm' => $form->getNormData(),
|
||||
],
|
||||
'errors' => [],
|
||||
];
|
||||
|
||||
if ($form->getViewData() !== $form->getNormData()) {
|
||||
$data['submitted_data']['view'] = $form->getViewData();
|
||||
}
|
||||
|
||||
if ($form->getData() !== $form->getNormData()) {
|
||||
$data['submitted_data']['model'] = $form->getData();
|
||||
}
|
||||
|
||||
foreach ($form->getErrors() as $error) {
|
||||
$errorData = [
|
||||
'message' => $error->getMessage(),
|
||||
'origin' => \is_object($error->getOrigin())
|
||||
? spl_object_hash($error->getOrigin())
|
||||
: null,
|
||||
'trace' => [],
|
||||
];
|
||||
|
||||
$cause = $error->getCause();
|
||||
|
||||
while (null !== $cause) {
|
||||
if ($cause instanceof ConstraintViolationInterface) {
|
||||
$errorData['trace'][] = $cause;
|
||||
$cause = method_exists($cause, 'getCause') ? $cause->getCause() : null;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($cause instanceof \Exception) {
|
||||
$errorData['trace'][] = $cause;
|
||||
$cause = $cause->getPrevious();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$errorData['trace'][] = $cause;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$data['errors'][] = $errorData;
|
||||
}
|
||||
|
||||
$data['synchronized'] = $form->isSynchronized();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extractViewVariables(FormView $view)
|
||||
{
|
||||
$data = [
|
||||
'id' => $view->vars['id'] ?? null,
|
||||
'name' => $view->vars['name'] ?? null,
|
||||
'view_vars' => [],
|
||||
];
|
||||
|
||||
foreach ($view->vars as $varName => $value) {
|
||||
$data['view_vars'][$varName] = $value;
|
||||
}
|
||||
|
||||
ksort($data['view_vars']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively builds an HTML ID for a form.
|
||||
*/
|
||||
private function buildId(FormInterface $form): string
|
||||
{
|
||||
$id = $form->getName();
|
||||
|
||||
if (null !== $form->getParent()) {
|
||||
$id = $this->buildId($form->getParent()).'_'.$id;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
}
|
51
vendor/symfony/form/Extension/DataCollector/FormDataExtractorInterface.php
vendored
Normal file
51
vendor/symfony/form/Extension/DataCollector/FormDataExtractorInterface.php
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
|
||||
/**
|
||||
* Extracts arrays of information out of forms.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface FormDataExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Extracts the configuration data of a form.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extractConfiguration(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Extracts the default data of a form.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extractDefaultData(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Extracts the submitted data of a form.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extractSubmittedData(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Extracts the view variables of a form.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extractViewVariables(FormView $view);
|
||||
}
|
140
vendor/symfony/form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php
vendored
Normal file
140
vendor/symfony/form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector\Proxy;
|
||||
|
||||
use Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\ResolvedFormTypeInterface;
|
||||
|
||||
/**
|
||||
* Proxy that invokes a data collector when creating a form and its view.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ResolvedTypeDataCollectorProxy implements ResolvedFormTypeInterface
|
||||
{
|
||||
private $proxiedType;
|
||||
private $dataCollector;
|
||||
|
||||
public function __construct(ResolvedFormTypeInterface $proxiedType, FormDataCollectorInterface $dataCollector)
|
||||
{
|
||||
$this->proxiedType = $proxiedType;
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return $this->proxiedType->getBlockPrefix();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->proxiedType->getParent();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInnerType()
|
||||
{
|
||||
return $this->proxiedType->getInnerType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeExtensions()
|
||||
{
|
||||
return $this->proxiedType->getTypeExtensions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createBuilder(FormFactoryInterface $factory, string $name, array $options = [])
|
||||
{
|
||||
$builder = $this->proxiedType->createBuilder($factory, $name, $options);
|
||||
|
||||
$builder->setAttribute('data_collector/passed_options', $options);
|
||||
$builder->setType($this);
|
||||
|
||||
return $builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createView(FormInterface $form, FormView $parent = null)
|
||||
{
|
||||
return $this->proxiedType->createView($form, $parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->proxiedType->buildForm($builder, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$this->proxiedType->buildView($view, $form, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function finishView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$this->proxiedType->finishView($view, $form, $options);
|
||||
|
||||
// Remember which view belongs to which form instance, so that we can
|
||||
// get the collected data for a view when its form instance is not
|
||||
// available (e.g. CSRF token)
|
||||
$this->dataCollector->associateFormWithView($form, $view);
|
||||
|
||||
// Since the CSRF token is only present in the FormView tree, we also
|
||||
// need to check the FormView tree instead of calling isRoot() on the
|
||||
// FormInterface tree
|
||||
if (null === $view->parent) {
|
||||
$this->dataCollector->collectViewVariables($view);
|
||||
|
||||
// Re-assemble data, in case FormView instances were added, for
|
||||
// which no FormInterface instances were present (e.g. CSRF token).
|
||||
// Since finishView() is called after finishing the views of all
|
||||
// children, we can safely assume that information has been
|
||||
// collected about the complete form tree.
|
||||
$this->dataCollector->buildFinalFormTree($form, $view);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOptionsResolver()
|
||||
{
|
||||
return $this->proxiedType->getOptionsResolver();
|
||||
}
|
||||
}
|
46
vendor/symfony/form/Extension/DataCollector/Proxy/ResolvedTypeFactoryDataCollectorProxy.php
vendored
Normal file
46
vendor/symfony/form/Extension/DataCollector/Proxy/ResolvedTypeFactoryDataCollectorProxy.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector\Proxy;
|
||||
|
||||
use Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface;
|
||||
use Symfony\Component\Form\FormTypeInterface;
|
||||
use Symfony\Component\Form\ResolvedFormTypeFactoryInterface;
|
||||
use Symfony\Component\Form\ResolvedFormTypeInterface;
|
||||
|
||||
/**
|
||||
* Proxy that wraps resolved types into {@link ResolvedTypeDataCollectorProxy}
|
||||
* instances.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ResolvedTypeFactoryDataCollectorProxy implements ResolvedFormTypeFactoryInterface
|
||||
{
|
||||
private $proxiedFactory;
|
||||
private $dataCollector;
|
||||
|
||||
public function __construct(ResolvedFormTypeFactoryInterface $proxiedFactory, FormDataCollectorInterface $dataCollector)
|
||||
{
|
||||
$this->proxiedFactory = $proxiedFactory;
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createResolvedType(FormTypeInterface $type, array $typeExtensions, ResolvedFormTypeInterface $parent = null)
|
||||
{
|
||||
return new ResolvedTypeDataCollectorProxy(
|
||||
$this->proxiedFactory->createResolvedType($type, $typeExtensions, $parent),
|
||||
$this->dataCollector
|
||||
);
|
||||
}
|
||||
}
|
53
vendor/symfony/form/Extension/DataCollector/Type/DataCollectorTypeExtension.php
vendored
Normal file
53
vendor/symfony/form/Extension/DataCollector/Type/DataCollectorTypeExtension.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractTypeExtension;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\Extension\DataCollector\EventListener\DataCollectorListener;
|
||||
use Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* Type extension for collecting data of a form with this type.
|
||||
*
|
||||
* @author Robert Schönthal <robert.schoenthal@gmail.com>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class DataCollectorTypeExtension extends AbstractTypeExtension
|
||||
{
|
||||
/**
|
||||
* @var DataCollectorListener
|
||||
*/
|
||||
private $listener;
|
||||
|
||||
public function __construct(FormDataCollectorInterface $dataCollector)
|
||||
{
|
||||
$this->listener = new DataCollectorListener($dataCollector);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addEventSubscriber($this->listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getExtendedTypes(): iterable
|
||||
{
|
||||
return [FormType::class];
|
||||
}
|
||||
}
|
111
vendor/symfony/form/Extension/DependencyInjection/DependencyInjectionExtension.php
vendored
Normal file
111
vendor/symfony/form/Extension/DependencyInjection/DependencyInjectionExtension.php
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DependencyInjection;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Form\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Form\FormExtensionInterface;
|
||||
use Symfony\Component\Form\FormTypeGuesserChain;
|
||||
|
||||
class DependencyInjectionExtension implements FormExtensionInterface
|
||||
{
|
||||
private $guesser;
|
||||
private $guesserLoaded = false;
|
||||
private $typeContainer;
|
||||
private $typeExtensionServices;
|
||||
private $guesserServices;
|
||||
|
||||
/**
|
||||
* @param iterable[] $typeExtensionServices
|
||||
*/
|
||||
public function __construct(ContainerInterface $typeContainer, array $typeExtensionServices, iterable $guesserServices)
|
||||
{
|
||||
$this->typeContainer = $typeContainer;
|
||||
$this->typeExtensionServices = $typeExtensionServices;
|
||||
$this->guesserServices = $guesserServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType(string $name)
|
||||
{
|
||||
if (!$this->typeContainer->has($name)) {
|
||||
throw new InvalidArgumentException(sprintf('The field type "%s" is not registered in the service container.', $name));
|
||||
}
|
||||
|
||||
return $this->typeContainer->get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasType(string $name)
|
||||
{
|
||||
return $this->typeContainer->has($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeExtensions(string $name)
|
||||
{
|
||||
$extensions = [];
|
||||
|
||||
if (isset($this->typeExtensionServices[$name])) {
|
||||
foreach ($this->typeExtensionServices[$name] as $extension) {
|
||||
$extensions[] = $extension;
|
||||
|
||||
$extendedTypes = [];
|
||||
foreach ($extension::getExtendedTypes() as $extendedType) {
|
||||
$extendedTypes[] = $extendedType;
|
||||
}
|
||||
|
||||
// validate the result of getExtendedTypes() to ensure it is consistent with the service definition
|
||||
if (!\in_array($name, $extendedTypes, true)) {
|
||||
throw new InvalidArgumentException(sprintf('The extended type "%s" specified for the type extension class "%s" does not match any of the actual extended types (["%s"]).', $name, \get_class($extension), implode('", "', $extendedTypes)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasTypeExtensions(string $name)
|
||||
{
|
||||
return isset($this->typeExtensionServices[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeGuesser()
|
||||
{
|
||||
if (!$this->guesserLoaded) {
|
||||
$this->guesserLoaded = true;
|
||||
$guessers = [];
|
||||
|
||||
foreach ($this->guesserServices as $serviceId => $service) {
|
||||
$guessers[] = $service;
|
||||
}
|
||||
|
||||
if ($guessers) {
|
||||
$this->guesser = new FormTypeGuesserChain($guessers);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->guesser;
|
||||
}
|
||||
}
|
29
vendor/symfony/form/Extension/HttpFoundation/HttpFoundationExtension.php
vendored
Normal file
29
vendor/symfony/form/Extension/HttpFoundation/HttpFoundationExtension.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\HttpFoundation;
|
||||
|
||||
use Symfony\Component\Form\AbstractExtension;
|
||||
|
||||
/**
|
||||
* Integrates the HttpFoundation component with the Form library.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class HttpFoundationExtension extends AbstractExtension
|
||||
{
|
||||
protected function loadTypeExtensions()
|
||||
{
|
||||
return [
|
||||
new Type\FormTypeHttpFoundationExtension(),
|
||||
];
|
||||
}
|
||||
}
|
131
vendor/symfony/form/Extension/HttpFoundation/HttpFoundationRequestHandler.php
vendored
Normal file
131
vendor/symfony/form/Extension/HttpFoundation/HttpFoundationRequestHandler.php
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\HttpFoundation;
|
||||
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\RequestHandlerInterface;
|
||||
use Symfony\Component\Form\Util\ServerParams;
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* A request processor using the {@link Request} class of the HttpFoundation
|
||||
* component.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class HttpFoundationRequestHandler implements RequestHandlerInterface
|
||||
{
|
||||
private $serverParams;
|
||||
|
||||
public function __construct(ServerParams $serverParams = null)
|
||||
{
|
||||
$this->serverParams = $serverParams ?? new ServerParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(FormInterface $form, $request = null)
|
||||
{
|
||||
if (!$request instanceof Request) {
|
||||
throw new UnexpectedTypeException($request, 'Symfony\Component\HttpFoundation\Request');
|
||||
}
|
||||
|
||||
$name = $form->getName();
|
||||
$method = $form->getConfig()->getMethod();
|
||||
|
||||
if ($method !== $request->getMethod()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For request methods that must not have a request body we fetch data
|
||||
// from the query string. Otherwise we look for data in the request body.
|
||||
if ('GET' === $method || 'HEAD' === $method || 'TRACE' === $method) {
|
||||
if ('' === $name) {
|
||||
$data = $request->query->all();
|
||||
} else {
|
||||
// Don't submit GET requests if the form's name does not exist
|
||||
// in the request
|
||||
if (!$request->query->has($name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $request->query->all()[$name];
|
||||
}
|
||||
} else {
|
||||
// Mark the form with an error if the uploaded size was too large
|
||||
// This is done here and not in FormValidator because $_POST is
|
||||
// empty when that error occurs. Hence the form is never submitted.
|
||||
if ($this->serverParams->hasPostMaxSizeBeenExceeded()) {
|
||||
// Submit the form, but don't clear the default values
|
||||
$form->submit(null, false);
|
||||
|
||||
$form->addError(new FormError(
|
||||
$form->getConfig()->getOption('upload_max_size_message')(),
|
||||
null,
|
||||
['{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize()]
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ('' === $name) {
|
||||
$params = $request->request->all();
|
||||
$files = $request->files->all();
|
||||
} elseif ($request->request->has($name) || $request->files->has($name)) {
|
||||
$default = $form->getConfig()->getCompound() ? [] : null;
|
||||
$params = $request->request->all()[$name] ?? $default;
|
||||
$files = $request->files->get($name, $default);
|
||||
} else {
|
||||
// Don't submit the form if it is not present in the request
|
||||
return;
|
||||
}
|
||||
|
||||
if (\is_array($params) && \is_array($files)) {
|
||||
$data = array_replace_recursive($params, $files);
|
||||
} else {
|
||||
$data = $params ?: $files;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't auto-submit the form unless at least one field is present.
|
||||
if ('' === $name && \count(array_intersect_key($data, $form->all())) <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$form->submit($data, 'PATCH' !== $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFileUpload($data)
|
||||
{
|
||||
return $data instanceof File;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getUploadFileError($data)
|
||||
{
|
||||
if (!$data instanceof UploadedFile || $data->isValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $data->getError();
|
||||
}
|
||||
}
|
47
vendor/symfony/form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php
vendored
Normal file
47
vendor/symfony/form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\HttpFoundation\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractTypeExtension;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FormTypeHttpFoundationExtension extends AbstractTypeExtension
|
||||
{
|
||||
private $requestHandler;
|
||||
|
||||
public function __construct(RequestHandlerInterface $requestHandler = null)
|
||||
{
|
||||
$this->requestHandler = $requestHandler ?? new HttpFoundationRequestHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->setRequestHandler($this->requestHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getExtendedTypes(): iterable
|
||||
{
|
||||
return [FormType::class];
|
||||
}
|
||||
}
|
36
vendor/symfony/form/Extension/Validator/Constraints/Form.php
vendored
Normal file
36
vendor/symfony/form/Extension/Validator/Constraints/Form.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class Form extends Constraint
|
||||
{
|
||||
public const NOT_SYNCHRONIZED_ERROR = '1dafa156-89e1-4736-b832-419c2e501fca';
|
||||
public const NO_SUCH_FIELD_ERROR = '6e5212ed-a197-4339-99aa-5654798a4854';
|
||||
|
||||
protected static $errorNames = [
|
||||
self::NOT_SYNCHRONIZED_ERROR => 'NOT_SYNCHRONIZED_ERROR',
|
||||
self::NO_SUCH_FIELD_ERROR => 'NO_SUCH_FIELD_ERROR',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTargets()
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
}
|
279
vendor/symfony/form/Extension/Validator/Constraints/FormValidator.php
vendored
Normal file
279
vendor/symfony/form/Extension/Validator/Constraints/FormValidator.php
vendored
Normal file
@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\Constraints\Composite;
|
||||
use Symfony\Component\Validator\Constraints\GroupSequence;
|
||||
use Symfony\Component\Validator\Constraints\Valid;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FormValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @var \SplObjectStorage<FormInterface, array<int, string|string[]|GroupSequence>>
|
||||
*/
|
||||
private $resolvedGroups;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($form, Constraint $formConstraint)
|
||||
{
|
||||
if (!$formConstraint instanceof Form) {
|
||||
throw new UnexpectedTypeException($formConstraint, Form::class);
|
||||
}
|
||||
|
||||
if (!$form instanceof FormInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* @var FormInterface $form */
|
||||
$config = $form->getConfig();
|
||||
|
||||
$validator = $this->context->getValidator()->inContext($this->context);
|
||||
|
||||
if ($form->isSubmitted() && $form->isSynchronized()) {
|
||||
// Validate the form data only if transformation succeeded
|
||||
$groups = $this->getValidationGroups($form);
|
||||
|
||||
if (!$groups) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $form->getData();
|
||||
// Validate the data against its own constraints
|
||||
$validateDataGraph = $form->isRoot()
|
||||
&& (\is_object($data) || \is_array($data))
|
||||
&& (($groups && \is_array($groups)) || ($groups instanceof GroupSequence && $groups->groups))
|
||||
;
|
||||
|
||||
// Validate the data against the constraints defined in the form
|
||||
/** @var Constraint[] $constraints */
|
||||
$constraints = $config->getOption('constraints', []);
|
||||
|
||||
$hasChildren = $form->count() > 0;
|
||||
|
||||
if ($hasChildren && $form->isRoot()) {
|
||||
$this->resolvedGroups = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
if ($groups instanceof GroupSequence) {
|
||||
// Validate the data, the form AND nested fields in sequence
|
||||
$violationsCount = $this->context->getViolations()->count();
|
||||
|
||||
foreach ($groups->groups as $group) {
|
||||
if ($validateDataGraph) {
|
||||
$validator->atPath('data')->validate($data, null, $group);
|
||||
}
|
||||
|
||||
if ($groupedConstraints = self::getConstraintsInGroups($constraints, $group)) {
|
||||
$validator->atPath('data')->validate($data, $groupedConstraints, $group);
|
||||
}
|
||||
|
||||
foreach ($form->all() as $field) {
|
||||
if ($field->isSubmitted()) {
|
||||
// remember to validate this field in one group only
|
||||
// otherwise resolving the groups would reuse the same
|
||||
// sequence recursively, thus some fields could fail
|
||||
// in different steps without breaking early enough
|
||||
$this->resolvedGroups[$field] = (array) $group;
|
||||
$fieldFormConstraint = new Form();
|
||||
$fieldFormConstraint->groups = $group;
|
||||
$this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath());
|
||||
$validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint, $group);
|
||||
}
|
||||
}
|
||||
|
||||
if ($violationsCount < $this->context->getViolations()->count()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($validateDataGraph) {
|
||||
$validator->atPath('data')->validate($data, null, $groups);
|
||||
}
|
||||
|
||||
$groupedConstraints = [];
|
||||
|
||||
foreach ($constraints as $constraint) {
|
||||
// For the "Valid" constraint, validate the data in all groups
|
||||
if ($constraint instanceof Valid) {
|
||||
if (\is_object($data) || \is_array($data)) {
|
||||
$validator->atPath('data')->validate($data, $constraint, $groups);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise validate a constraint only once for the first
|
||||
// matching group
|
||||
foreach ($groups as $group) {
|
||||
if (\in_array($group, $constraint->groups)) {
|
||||
$groupedConstraints[$group][] = $constraint;
|
||||
|
||||
// Prevent duplicate validation
|
||||
if (!$constraint instanceof Composite) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($groupedConstraints as $group => $constraint) {
|
||||
$validator->atPath('data')->validate($data, $constraint, $group);
|
||||
}
|
||||
|
||||
foreach ($form->all() as $field) {
|
||||
if ($field->isSubmitted()) {
|
||||
$this->resolvedGroups[$field] = $groups;
|
||||
$this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath());
|
||||
$validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $formConstraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasChildren && $form->isRoot()) {
|
||||
// destroy storage to avoid memory leaks
|
||||
$this->resolvedGroups = new \SplObjectStorage();
|
||||
}
|
||||
} elseif (!$form->isSynchronized()) {
|
||||
$childrenSynchronized = true;
|
||||
|
||||
/** @var FormInterface $child */
|
||||
foreach ($form as $child) {
|
||||
if (!$child->isSynchronized()) {
|
||||
$childrenSynchronized = false;
|
||||
$this->context->setNode($this->context->getValue(), $child, $this->context->getMetadata(), $this->context->getPropertyPath());
|
||||
$validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $formConstraint);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the form with an error if it is not synchronized BUT all
|
||||
// of its children are synchronized. If any child is not
|
||||
// synchronized, an error is displayed there already and showing
|
||||
// a second error in its parent form is pointless, or worse, may
|
||||
// lead to duplicate errors if error bubbling is enabled on the
|
||||
// child.
|
||||
// See also https://github.com/symfony/symfony/issues/4359
|
||||
if ($childrenSynchronized) {
|
||||
$clientDataAsString = is_scalar($form->getViewData())
|
||||
? (string) $form->getViewData()
|
||||
: get_debug_type($form->getViewData());
|
||||
|
||||
$failure = $form->getTransformationFailure();
|
||||
|
||||
$this->context->setConstraint($formConstraint);
|
||||
$this->context->buildViolation($failure->getInvalidMessage() ?? $config->getOption('invalid_message'))
|
||||
->setParameters(array_replace(
|
||||
['{{ value }}' => $clientDataAsString],
|
||||
$config->getOption('invalid_message_parameters'),
|
||||
$failure->getInvalidMessageParameters()
|
||||
))
|
||||
->setInvalidValue($form->getViewData())
|
||||
->setCode(Form::NOT_SYNCHRONIZED_ERROR)
|
||||
->setCause($failure)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the form with an error if it contains extra fields
|
||||
if (!$config->getOption('allow_extra_fields') && \count($form->getExtraData()) > 0) {
|
||||
$this->context->setConstraint($formConstraint);
|
||||
$this->context->buildViolation($config->getOption('extra_fields_message', ''))
|
||||
->setParameter('{{ extra_fields }}', '"'.implode('", "', array_keys($form->getExtraData())).'"')
|
||||
->setPlural(\count($form->getExtraData()))
|
||||
->setInvalidValue($form->getExtraData())
|
||||
->setCode(Form::NO_SUCH_FIELD_ERROR)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the validation groups of the given form.
|
||||
*
|
||||
* @return string|GroupSequence|array<string|GroupSequence>
|
||||
*/
|
||||
private function getValidationGroups(FormInterface $form)
|
||||
{
|
||||
// Determine the clicked button of the complete form tree
|
||||
$clickedButton = null;
|
||||
|
||||
if (method_exists($form, 'getClickedButton')) {
|
||||
$clickedButton = $form->getClickedButton();
|
||||
}
|
||||
|
||||
if (null !== $clickedButton) {
|
||||
$groups = $clickedButton->getConfig()->getOption('validation_groups');
|
||||
|
||||
if (null !== $groups) {
|
||||
return self::resolveValidationGroups($groups, $form);
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
$groups = $form->getConfig()->getOption('validation_groups');
|
||||
|
||||
if (null !== $groups) {
|
||||
return self::resolveValidationGroups($groups, $form);
|
||||
}
|
||||
|
||||
if (isset($this->resolvedGroups[$form])) {
|
||||
return $this->resolvedGroups[$form];
|
||||
}
|
||||
|
||||
$form = $form->getParent();
|
||||
} while (null !== $form);
|
||||
|
||||
return [Constraint::DEFAULT_GROUP];
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-processes the validation groups option for a given form.
|
||||
*
|
||||
* @param string|GroupSequence|array<string|GroupSequence>|callable $groups The validation groups
|
||||
*
|
||||
* @return GroupSequence|array<string|GroupSequence>
|
||||
*/
|
||||
private static function resolveValidationGroups($groups, FormInterface $form)
|
||||
{
|
||||
if (!\is_string($groups) && \is_callable($groups)) {
|
||||
$groups = $groups($form);
|
||||
}
|
||||
|
||||
if ($groups instanceof GroupSequence) {
|
||||
return $groups;
|
||||
}
|
||||
|
||||
return (array) $groups;
|
||||
}
|
||||
|
||||
private static function getConstraintsInGroups($constraints, $group)
|
||||
{
|
||||
$groups = (array) $group;
|
||||
|
||||
return array_filter($constraints, static function (Constraint $constraint) use ($groups) {
|
||||
foreach ($groups as $group) {
|
||||
if (\in_array($group, $constraint->groups, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
59
vendor/symfony/form/Extension/Validator/EventListener/ValidationListener.php
vendored
Normal file
59
vendor/symfony/form/Extension/Validator/EventListener/ValidationListener.php
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Validator\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
|
||||
use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ValidationListener implements EventSubscriberInterface
|
||||
{
|
||||
private $validator;
|
||||
|
||||
private $violationMapper;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [FormEvents::POST_SUBMIT => 'validateForm'];
|
||||
}
|
||||
|
||||
public function __construct(ValidatorInterface $validator, ViolationMapperInterface $violationMapper)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
$this->violationMapper = $violationMapper;
|
||||
}
|
||||
|
||||
public function validateForm(FormEvent $event)
|
||||
{
|
||||
$form = $event->getForm();
|
||||
|
||||
if ($form->isRoot()) {
|
||||
// Form groups are validated internally (FormValidator). Here we don't set groups as they are retrieved into the validator.
|
||||
foreach ($this->validator->validate($form) as $violation) {
|
||||
// Allow the "invalid" constraint to be put onto
|
||||
// non-synchronized forms
|
||||
$allowNonSynchronized = $violation->getConstraint() instanceof Form && Form::NOT_SYNCHRONIZED_ERROR === $violation->getCode();
|
||||
|
||||
$this->violationMapper->mapViolation($violation, $form, $allowNonSynchronized);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
vendor/symfony/form/Extension/Validator/Type/BaseValidatorExtension.php
vendored
Normal file
59
vendor/symfony/form/Extension/Validator/Type/BaseValidatorExtension.php
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Validator\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractTypeExtension;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\GroupSequence;
|
||||
|
||||
/**
|
||||
* Encapsulates common logic of {@link FormTypeValidatorExtension} and
|
||||
* {@link SubmitTypeValidatorExtension}.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
abstract class BaseValidatorExtension extends AbstractTypeExtension
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
// Make sure that validation groups end up as null, closure or array
|
||||
$validationGroupsNormalizer = function (Options $options, $groups) {
|
||||
if (false === $groups) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (empty($groups)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (\is_callable($groups)) {
|
||||
return $groups;
|
||||
}
|
||||
|
||||
if ($groups instanceof GroupSequence) {
|
||||
return $groups;
|
||||
}
|
||||
|
||||
return (array) $groups;
|
||||
};
|
||||
|
||||
$resolver->setDefaults([
|
||||
'validation_groups' => null,
|
||||
]);
|
||||
|
||||
$resolver->setNormalizer('validation_groups', $validationGroupsNormalizer);
|
||||
}
|
||||
}
|
90
vendor/symfony/form/Extension/Validator/Type/FormTypeValidatorExtension.php
vendored
Normal file
90
vendor/symfony/form/Extension/Validator/Type/FormTypeValidatorExtension.php
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Validator\Type;
|
||||
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener;
|
||||
use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormRendererInterface;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FormTypeValidatorExtension extends BaseValidatorExtension
|
||||
{
|
||||
private $validator;
|
||||
private $violationMapper;
|
||||
private $legacyErrorMessages;
|
||||
|
||||
public function __construct(ValidatorInterface $validator, bool $legacyErrorMessages = true, FormRendererInterface $formRenderer = null, TranslatorInterface $translator = null)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
$this->violationMapper = new ViolationMapper($formRenderer, $translator);
|
||||
$this->legacyErrorMessages = $legacyErrorMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addEventSubscriber(new ValidationListener($this->validator, $this->violationMapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
// Constraint should always be converted to an array
|
||||
$constraintsNormalizer = function (Options $options, $constraints) {
|
||||
return \is_object($constraints) ? [$constraints] : (array) $constraints;
|
||||
};
|
||||
|
||||
$resolver->setDefaults([
|
||||
'error_mapping' => [],
|
||||
'constraints' => [],
|
||||
'invalid_message' => 'This value is not valid.',
|
||||
'invalid_message_parameters' => [],
|
||||
'legacy_error_messages' => $this->legacyErrorMessages,
|
||||
'allow_extra_fields' => false,
|
||||
'extra_fields_message' => 'This form should not contain extra fields.',
|
||||
]);
|
||||
$resolver->setAllowedTypes('constraints', [Constraint::class, Constraint::class.'[]']);
|
||||
$resolver->setAllowedTypes('legacy_error_messages', 'bool');
|
||||
$resolver->setDeprecated('legacy_error_messages', 'symfony/form', '5.2', function (Options $options, $value) {
|
||||
if (true === $value) {
|
||||
return 'Setting the "legacy_error_messages" option to "true" is deprecated. It will be disabled in Symfony 6.0.';
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
|
||||
$resolver->setNormalizer('constraints', $constraintsNormalizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getExtendedTypes(): iterable
|
||||
{
|
||||
return [FormType::class];
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user