Adapted Form, Validator, File and I18N component to new coding standards

This commit is contained in:
Bernhard Schussek 2010-06-24 11:24:08 +02:00 committed by Fabien Potencier
parent ee83847cec
commit bcd4b6d140
227 changed files with 12877 additions and 13518 deletions

View File

@ -17,13 +17,13 @@ namespace Symfony\Components\File\Exception;
*/
class AccessDeniedException extends FileException
{
/**
* Constructor.
*
* @param string $path The path to the accessed file
*/
public function __construct($path)
{
parent::__construct(sprintf('The file %s could not be accessed', $path));
}
/**
* Constructor.
*
* @param string $path The path to the accessed file
*/
public function __construct($path)
{
parent::__construct(sprintf('The file %s could not be accessed', $path));
}
}

View File

@ -17,13 +17,13 @@ namespace Symfony\Components\File\Exception;
*/
class FileNotFoundException extends FileException
{
/**
* Constructor.
*
* @param string $path The path to the file that was not found
*/
public function __construct($path)
{
parent::__construct(sprintf('The file %s does not exist', $path));
}
/**
* Constructor.
*
* @param string $path The path to the file that was not found
*/
public function __construct($path)
{
parent::__construct(sprintf('The file %s does not exist', $path));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -20,46 +20,42 @@ use Symfony\Components\File\Exception\AccessDeniedException;
*/
class ContentTypeMimeTypeGuesser implements MimeTypeGuesserInterface
{
/**
* Returns whether this guesser is supported on the corrent OS/PHP setup
*
* @return boolean
*/
static public function isSupported()
{
return function_exists('mime_content_type');
}
/**
* Guesses the mime type of the file with the given path
*
* @see MimeTypeGuesserInterface::guess()
*/
public function guess($path)
{
if (!is_file($path))
/**
* Returns whether this guesser is supported on the corrent OS/PHP setup
*
* @return boolean
*/
static public function isSupported()
{
throw new FileNotFoundException($path);
return function_exists('mime_content_type');
}
if (!is_readable($path))
/**
* Guesses the mime type of the file with the given path
*
* @see MimeTypeGuesserInterface::guess()
*/
public function guess($path)
{
throw new AccessDeniedException($path);
if (!is_file($path)) {
throw new FileNotFoundException($path);
}
if (!is_readable($path)) {
throw new AccessDeniedException($path);
}
if (!self::isSupported() || !is_readable($path)) {
return null;
}
$type = mime_content_type($path);
// remove charset (added as of PHP 5.3)
if (false !== $pos = strpos($type, ';')) {
$type = substr($type, 0, $pos);
}
return $type;
}
if (!self::isSupported() || !is_readable($path))
{
return null;
}
$type = mime_content_type($path);
// remove charset (added as of PHP 5.3)
if (false !== $pos = strpos($type, ';'))
{
$type = substr($type, 0, $pos);
}
return $type;
}
}

View File

@ -20,42 +20,38 @@ use Symfony\Components\File\Exception\AccessDeniedException;
*/
class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
{
/**
* Guesses the mime type of the file with the given path
*
* @see MimeTypeGuesserInterface::guess()
*/
public function guess($path)
{
if (!is_file($path))
/**
* Guesses the mime type of the file with the given path
*
* @see MimeTypeGuesserInterface::guess()
*/
public function guess($path)
{
throw new FileNotFoundException($path);
if (!is_file($path)) {
throw new FileNotFoundException($path);
}
if (!is_readable($path)) {
throw new AccessDeniedException($path);
}
ob_start();
// need to use --mime instead of -i. see #6641
passthru(sprintf('file -b --mime %s 2>/dev/null', escapeshellarg($path)), $return);
if ($return > 0) {
ob_end_clean();
return null;
}
$type = trim(ob_get_clean());
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-]+)#i', $type, $match)) {
// it's not a type, but an error message
return null;
}
return $match[1];
}
if (!is_readable($path))
{
throw new AccessDeniedException($path);
}
ob_start();
// need to use --mime instead of -i. see #6641
passthru(sprintf('file -b --mime %s 2>/dev/null', escapeshellarg($path)), $return);
if ($return > 0)
{
ob_end_clean();
return null;
}
$type = trim(ob_get_clean());
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-]+)#i', $type, $match))
{
// it's not a type, but an error message
return null;
}
return $match[1];
}
}

View File

@ -20,51 +20,46 @@ use Symfony\Components\File\Exception\AccessDeniedException;
*/
class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
{
/**
* Returns whether this guesser is supported on the corrent OS/PHP setup
*
* @return boolean
*/
static public function isSupported()
{
return function_exists('finfo_open');
}
/**
* Guesses the mime type of the file with the given path
*
* @see MimeTypeGuesserInterface::guess()
*/
public function guess($path)
{
if (!is_file($path))
/**
* Returns whether this guesser is supported on the corrent OS/PHP setup
*
* @return boolean
*/
static public function isSupported()
{
throw new FileNotFoundException($path);
return function_exists('finfo_open');
}
if (!is_readable($path))
/**
* Guesses the mime type of the file with the given path
*
* @see MimeTypeGuesserInterface::guess()
*/
public function guess($path)
{
throw new AccessDeniedException($path);
if (!is_file($path)) {
throw new FileNotFoundException($path);
}
if (!is_readable($path)) {
throw new AccessDeniedException($path);
}
if (!self::isSupported()) {
return null;
}
if (!$finfo = new \finfo(FILEINFO_MIME)) {
return null;
}
$type = $finfo->file($path);
// remove charset (added as of PHP 5.3)
if (false !== $pos = strpos($type, ';')) {
$type = substr($type, 0, $pos);
}
return $type;
}
if (!self::isSupported())
{
return null;
}
if (!$finfo = new \finfo(FILEINFO_MIME))
{
return null;
}
$type = $finfo->file($path);
// remove charset (added as of PHP 5.3)
if (false !== $pos = strpos($type, ';'))
{
$type = substr($type, 0, $pos);
}
return $type;
}
}

View File

@ -31,99 +31,92 @@ use Symfony\Components\File\Exception\AccessDeniedException;
*/
class MimeTypeGuesser implements MimeTypeGuesserInterface
{
/**
* The singleton instance
* @var MimeTypeGuesser
*/
static private $instance = null;
/**
* The singleton instance
* @var MimeTypeGuesser
*/
static private $instance = null;
/**
* All registered MimeTypeGuesserInterface instances
* @var array
*/
protected $guessers = array();
/**
* All registered MimeTypeGuesserInterface instances
* @var array
*/
protected $guessers = array();
/**
* Returns the singleton instance
*
* @return MimeTypeGuesser
*/
static public function getInstance()
{
if (is_null(self::$instance))
/**
* Returns the singleton instance
*
* @return MimeTypeGuesser
*/
static public function getInstance()
{
self::$instance = new self();
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
return self::$instance;
}
/**
* Registers all natively provided mime type guessers
*/
private function __construct()
{
$this->register(new FileBinaryMimeTypeGuesser());
if (ContentTypeMimeTypeGuesser::isSupported())
/**
* Registers all natively provided mime type guessers
*/
private function __construct()
{
$this->register(new ContentTypeMimeTypeGuesser());
$this->register(new FileBinaryMimeTypeGuesser());
if (ContentTypeMimeTypeGuesser::isSupported()) {
$this->register(new ContentTypeMimeTypeGuesser());
}
if (FileinfoMimeTypeGuesser::isSupported()) {
$this->register(new FileinfoMimeTypeGuesser());
}
}
if (FileinfoMimeTypeGuesser::isSupported())
/**
* Registers a new mime type guesser
*
* When guessing, this guesser is preferred over previously registered ones.
*
* @param MimeTypeGuesserInterface $guesser
*/
public function register(MimeTypeGuesserInterface $guesser)
{
$this->register(new FileinfoMimeTypeGuesser());
}
}
/**
* Registers a new mime type guesser
*
* When guessing, this guesser is preferred over previously registered ones.
*
* @param MimeTypeGuesserInterface $guesser
*/
public function register(MimeTypeGuesserInterface $guesser)
{
array_unshift($this->guessers, $guesser);
}
/**
* Tries to guess the mime type of the given file
*
* The file is passed to each registered mime type guesser in reverse order
* of their registration (last registered is queried first). Once a guesser
* returns a value that is not NULL, this method terminates and returns the
* value.
*
* @param string $path The path to the file
* @return string The mime type or NULL, if none could be guessed
* @throws FileException If the file does not exist
*/
public function guess($path)
{
if (!is_file($path))
{
throw new FileNotFoundException($path);
array_unshift($this->guessers, $guesser);
}
if (!is_readable($path))
/**
* Tries to guess the mime type of the given file
*
* The file is passed to each registered mime type guesser in reverse order
* of their registration (last registered is queried first). Once a guesser
* returns a value that is not NULL, this method terminates and returns the
* value.
*
* @param string $path The path to the file
* @return string The mime type or NULL, if none could be guessed
* @throws FileException If the file does not exist
*/
public function guess($path)
{
throw new AccessDeniedException($path);
if (!is_file($path)) {
throw new FileNotFoundException($path);
}
if (!is_readable($path)) {
throw new AccessDeniedException($path);
}
$mimeType = null;
foreach ($this->guessers as $guesser) {
$mimeType = $guesser->guess($path);
if (!is_null($mimeType)) {
break;
}
}
return $mimeType;
}
$mimeType = null;
foreach ($this->guessers as $guesser)
{
$mimeType = $guesser->guess($path);
if (!is_null($mimeType))
{
break;
}
}
return $mimeType;
}
}

View File

@ -17,13 +17,13 @@ namespace Symfony\Components\File\MimeType;
*/
interface MimeTypeGuesserInterface
{
/**
* Guesses the mime type of the file with the given path
*
* @param string $path The path to the file
* @return string The mime type or NULL, if none could be guessed
* @throws FileNotFoundException If the file does not exist
* @throws AccessDeniedException If the file could not be read
*/
public function guess($path);
/**
* Guesses the mime type of the file with the given path
*
* @param string $path The path to the file
* @return string The mime type or NULL, if none could be guessed
* @throws FileNotFoundException If the file does not exist
* @throws AccessDeniedException If the file could not be read
*/
public function guess($path);
}

View File

@ -20,111 +20,103 @@ use Symfony\Components\File\Exception\FileException;
*/
class UploadedFile extends File
{
protected $originalName;
protected $mimeType;
protected $size;
protected $error;
protected $moved = false;
protected $originalName;
protected $mimeType;
protected $size;
protected $error;
protected $moved = false;
/**
* Accepts the information of the uploaded file as provided by the PHP
* global $_FILES.
*
* @param string $tmpName The full temporary path to the file
* @param string $name The original file name
* @param string $type The type of the file as provided by PHP
* @param integer $size The file size
* @param string $error The error constant of the upload. Should be
* one of PHP's UPLOAD_XXX constants.
*/
public function __construct($path, $originalName, $mimeType, $size, $error)
{
if (!ini_get('file_uploads'))
/**
* Accepts the information of the uploaded file as provided by the PHP
* global $_FILES.
*
* @param string $tmpName The full temporary path to the file
* @param string $name The original file name
* @param string $type The type of the file as provided by PHP
* @param integer $size The file size
* @param string $error The error constant of the upload. Should be
* one of PHP's UPLOAD_XXX constants.
*/
public function __construct($path, $originalName, $mimeType, $size, $error)
{
throw new FileException(sprintf('Unable to create UploadedFile because "file_uploads" is disabled in your php.ini file (%s)', get_cfg_var('cfg_file_path')));
if (!ini_get('file_uploads')) {
throw new FileException(sprintf('Unable to create UploadedFile because "file_uploads" is disabled in your php.ini file (%s)', get_cfg_var('cfg_file_path')));
}
parent::__construct($path);
if (is_null($error)) {
$error = UPLOAD_ERR_OK;
}
if (is_null($mimeType)) {
$mimeType = 'application/octet-stream';
}
$this->originalName = (string)$originalName;
$this->mimeType = $mimeType;
$this->size = $size;
$this->error = $error;
}
parent::__construct($path);
if (is_null($error))
/**
* Returns the mime type of the file.
*
* The mime type is guessed using the functions finfo(), mime_content_type()
* and the system binary "file" (in this order), depending on which of those
* is available on the current operating system.
*
* @returns string The guessed mime type, e.g. "application/pdf"
*/
public function getMimeType()
{
$error = UPLOAD_ERR_OK;
$mimeType = parent::getMimeType();
if (is_null($mimeType)) {
$mimeType = $this->mimeType;
}
return $mimeType;
}
if (is_null($mimeType))
/**
* Returns the original file name including its extension.
*
* @returns string The file name
*/
public function getOriginalName()
{
$mimeType = 'application/octet-stream';
return $this->originalName;
}
$this->originalName = (string)$originalName;
$this->mimeType = $mimeType;
$this->size = $size;
$this->error = $error;
}
/**
* Returns the mime type of the file.
*
* The mime type is guessed using the functions finfo(), mime_content_type()
* and the system binary "file" (in this order), depending on which of those
* is available on the current operating system.
*
* @returns string The guessed mime type, e.g. "application/pdf"
*/
public function getMimeType()
{
$mimeType = parent::getMimeType();
if (is_null($mimeType))
/**
* Returns the upload error.
*
* If the upload was successful, the constant UPLOAD_ERR_OK is returned.
* Otherwise one of the other UPLOAD_ERR_XXX constants is returned.
*
* @returns integer The upload error
*/
public function getError()
{
$mimeType = $this->mimeType;
return $this->error;
}
return $mimeType;
}
/**
* Returns the original file name including its extension.
*
* @returns string The file name
*/
public function getOriginalName()
{
return $this->originalName;
}
/**
* Returns the upload error.
*
* If the upload was successful, the constant UPLOAD_ERR_OK is returned.
* Otherwise one of the other UPLOAD_ERR_XXX constants is returned.
*
* @returns integer The upload error
*/
public function getError()
{
return $this->error;
}
/**
* Moves the file to a new location.
*
* @param string $newPath
*/
public function move($newPath)
{
if (!$this->moved)
/**
* Moves the file to a new location.
*
* @param string $newPath
*/
public function move($newPath)
{
if (!move_uploaded_file($this->getPath(), $newPath))
{
throw new FileException(sprintf('Could not move file %s to %s', $this->getPath(), $newPath));
}
if (!$this->moved) {
if (!move_uploaded_file($this->getPath(), $newPath)) {
throw new FileException(sprintf('Could not move file %s to %s', $this->getPath(), $newPath));
}
$this->moved = true;
$this->moved = true;
} else {
parent::move($newPath);
}
}
else
{
parent::move($newPath);
}
}
}

View File

@ -20,15 +20,15 @@ namespace Symfony\Components\Form;
*/
class BirthdayField extends DateField
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$currentYear = date('Y');
/**
* {@inheritDoc}
*/
protected function configure()
{
$currentYear = date('Y');
$this->addOption('years', range($currentYear-120, $currentYear));
$this->addOption('years', range($currentYear-120, $currentYear));
parent::configure();
}
parent::configure();
}
}

View File

@ -20,13 +20,13 @@ use Symfony\Components\Form\ValueTransformer\BooleanToStringTransformer;
*/
class CheckboxField extends ToggleField
{
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'type' => 'checkbox',
), $attributes));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'type' => 'checkbox',
), $attributes));
}
}

View File

@ -15,274 +15,237 @@ use Symfony\Components\Form\ValueTransformer\BooleanToStringTransformer;
*/
class ChoiceField extends HybridField
{
/**
* Stores the preferred choices with the choices as keys
* @var array
*/
protected $preferredChoices = array();
/**
* Stores the preferred choices with the choices as keys
* @var array
*/
protected $preferredChoices = array();
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addRequiredOption('choices');
$this->addOption('preferred_choices', array());
$this->addOption('separator', '----------');
$this->addOption('multiple', false);
$this->addOption('expanded', false);
$this->addOption('empty_value', '');
$this->addOption('translate_choices', false);
if (count($this->getOption('preferred_choices')) > 0)
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->preferredChoices = array_flip($this->getOption('preferred_choices'));
$this->addRequiredOption('choices');
$this->addOption('preferred_choices', array());
$this->addOption('separator', '----------');
$this->addOption('multiple', false);
$this->addOption('expanded', false);
$this->addOption('empty_value', '');
$this->addOption('translate_choices', false);
if (false && $diff = array_diff_key($this->options, $this->knownOptions))
{
//throw new InvalidOptionsException(sprintf('%s does not support the following options: "%s".', get_class($this), implode('", "', array_keys($diff))), array_keys($diff));
}
}
if (count($this->getOption('preferred_choices')) > 0) {
$this->preferredChoices = array_flip($this->getOption('preferred_choices'));
if ($this->getOption('expanded'))
{
$this->setFieldMode(self::GROUP);
$choices = $this->getOption('choices');
foreach ($this->getOption('preferred_choices') as $choice)
{
$this->add($this->newChoiceField($choice, $choices[$choice]));
unset($choices[$choice]);
}
foreach ($this->getOption('choices') as $choice => $value)
{
$this->add($this->newChoiceField($choice, $value));
}
}
else
{
$this->setFieldMode(self::FIELD);
}
}
/**
* Returns a new field of type radio button or checkbox.
*
* @param string $key The key for the option
* @param string $label The label for the option
*/
protected function newChoiceField($choice, $label)
{
if ($this->getOption('multiple'))
{
return new CheckboxField($choice, array(
'value' => $choice,
'label' => $label,
'translate_label' => $this->getOption('translate_choices'),
));
}
else
{
return new RadioField($choice, array(
'value' => $choice,
'label' => $label,
'translate_label' => $this->getOption('translate_choices'),
));
}
}
/**
* {@inheritDoc}
*
* Takes care of converting the input from a single radio button
* to an array.
*/
public function bind($value)
{
if (!$this->getOption('multiple') && $this->getOption('expanded'))
{
$value = $value === null ? array() : array($value => true);
}
parent::bind($value);
}
/**
* Transforms a single choice or an array of choices to a format appropriate
* for the nested checkboxes/radio buttons.
*
* The result is an array with the options as keys and true/false as values,
* depending on whether a given option is selected. If this field is rendered
* as select tag, the value is not modified.
*
* @param mixed $value An array if "multiple" is set to true, a scalar
* value otherwise.
* @return mixed An array if "expanded" or "multiple" is set to true,
* a scalar value otherwise.
*/
protected function transform($value)
{
if ($this->getOption('expanded'))
{
$choices = $this->getOption('choices');
foreach ($choices as $choice => $_)
{
$choices[$choice] = $this->getOption('multiple')
? in_array($choice, (array)$value, true)
: ($choice === $value);
}
return $choices;
}
else
{
return parent::transform($value);
}
}
/**
* Transforms a checkbox/radio button array to a single choice or an array
* of choices.
*
* The input value is an array with the choices as keys and true/false as
* values, depending on whether a given choice is selected. The output
* is an array with the selected choices or a single selected choice.
*
* @param mixed $value An array if "expanded" or "multiple" is set to true,
* a scalar value otherwise.
* @return mixed $value An array if "multiple" is set to true, a scalar
* value otherwise.
*/
protected function reverseTransform($value)
{
if ($this->getOption('expanded'))
{
$choices = array();
foreach ($value as $choice => $selected)
{
if ($selected)
{
$choices[] = $choice;
}
}
if ($this->getOption('multiple'))
{
return $choices;
}
else
{
return count($choices) > 0 ? current($choices) : null;
}
}
else
{
return parent::reverseTransform($value);
}
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
if ($this->getOption('expanded'))
{
$html = "";
foreach ($this as $field)
{
$html .= $field->render()."\n";
}
return $html;
}
else
{
$attrs['id'] = $this->getId();
$attrs['name'] = $this->getName();
$attrs['disabled'] = $this->isDisabled();
// 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.
if ($this->getOption('multiple') && !$this->getOption('expanded'))
{
$attrs['name'] .= '[]';
}
if ($this->getOption('multiple'))
{
$attrs['multiple'] = 'multiple';
}
$selected = array_flip(array_map('strval', (array)$this->getDisplayedData()));
$html = "\n";
if (!$this->isRequired())
{
$html .= $this->renderChoices(array('' => $this->getOption('empty_value')), $selected)."\n";
}
$choices = $this->getOption('choices');
if (count($this->preferredChoices) > 0)
{
$html .= $this->renderChoices(array_intersect_key($choices, $this->preferredChoices), $selected)."\n";
$html .= $this->generator->contentTag('option', $this->getOption('separator'), array('disabled' => true))."\n";
}
$html .= $this->renderChoices(array_diff_key($choices, $this->preferredChoices), $selected)."\n";
return $this->generator->contentTag('select', $html, array_merge($attrs, $attributes));
}
}
/**
* Returns an array of option tags for the choice field
*
* @return array An array of option tags
*/
protected function renderChoices(array $choices, array $selected)
{
$options = array();
foreach ($choices as $key => $option)
{
if (is_array($option))
{
$options[] = $this->generator->contentTag(
'optgroup',
"\n".$this->renderChoices($option, $selected)."\n",
array('label' => $this->generator->escape($key))
);
}
else
{
$attributes = array('value' => $this->generator->escape($key));
if (isset($selected[strval($key)]))
{
$attributes['selected'] = true;
if (false && $diff = array_diff_key($this->options, $this->knownOptions)) {
//throw new InvalidOptionsException(sprintf('%s does not support the following options: "%s".', get_class($this), implode('", "', array_keys($diff))), array_keys($diff));
}
}
if ($this->getOption('translate_choices'))
{
$option = $this->translate($option);
}
if ($this->getOption('expanded')) {
$this->setFieldMode(self::GROUP);
$options[] = $this->generator->contentTag(
'option',
$this->generator->escape($option),
$attributes
);
}
$choices = $this->getOption('choices');
foreach ($this->getOption('preferred_choices') as $choice) {
$this->add($this->newChoiceField($choice, $choices[$choice]));
unset($choices[$choice]);
}
foreach ($this->getOption('choices') as $choice => $value) {
$this->add($this->newChoiceField($choice, $value));
}
} else {
$this->setFieldMode(self::FIELD);
}
}
return implode("\n", $options);
}
/**
* Returns a new field of type radio button or checkbox.
*
* @param string $key The key for the option
* @param string $label The label for the option
*/
protected function newChoiceField($choice, $label)
{
if ($this->getOption('multiple')) {
return new CheckboxField($choice, array(
'value' => $choice,
'label' => $label,
'translate_label' => $this->getOption('translate_choices'),
));
} else {
return new RadioField($choice, array(
'value' => $choice,
'label' => $label,
'translate_label' => $this->getOption('translate_choices'),
));
}
}
/**
* {@inheritDoc}
*
* Takes care of converting the input from a single radio button
* to an array.
*/
public function bind($value)
{
if (!$this->getOption('multiple') && $this->getOption('expanded')) {
$value = $value === null ? array() : array($value => true);
}
parent::bind($value);
}
/**
* Transforms a single choice or an array of choices to a format appropriate
* for the nested checkboxes/radio buttons.
*
* The result is an array with the options as keys and true/false as values,
* depending on whether a given option is selected. If this field is rendered
* as select tag, the value is not modified.
*
* @param mixed $value An array if "multiple" is set to true, a scalar
* value otherwise.
* @return mixed An array if "expanded" or "multiple" is set to true,
* a scalar value otherwise.
*/
protected function transform($value)
{
if ($this->getOption('expanded')) {
$choices = $this->getOption('choices');
foreach ($choices as $choice => $_) {
$choices[$choice] = $this->getOption('multiple')
? in_array($choice, (array)$value, true)
: ($choice === $value);
}
return $choices;
} else {
return parent::transform($value);
}
}
/**
* Transforms a checkbox/radio button array to a single choice or an array
* of choices.
*
* The input value is an array with the choices as keys and true/false as
* values, depending on whether a given choice is selected. The output
* is an array with the selected choices or a single selected choice.
*
* @param mixed $value An array if "expanded" or "multiple" is set to true,
* a scalar value otherwise.
* @return mixed $value An array if "multiple" is set to true, a scalar
* value otherwise.
*/
protected function reverseTransform($value)
{
if ($this->getOption('expanded')) {
$choices = array();
foreach ($value as $choice => $selected) {
if ($selected) {
$choices[] = $choice;
}
}
if ($this->getOption('multiple')) {
return $choices;
} else {
return count($choices) > 0 ? current($choices) : null;
}
} else {
return parent::reverseTransform($value);
}
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
if ($this->getOption('expanded')) {
$html = "";
foreach ($this as $field) {
$html .= $field->render()."\n";
}
return $html;
} else {
$attrs['id'] = $this->getId();
$attrs['name'] = $this->getName();
$attrs['disabled'] = $this->isDisabled();
// 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.
if ($this->getOption('multiple') && !$this->getOption('expanded')) {
$attrs['name'] .= '[]';
}
if ($this->getOption('multiple')) {
$attrs['multiple'] = 'multiple';
}
$selected = array_flip(array_map('strval', (array)$this->getDisplayedData()));
$html = "\n";
if (!$this->isRequired()) {
$html .= $this->renderChoices(array('' => $this->getOption('empty_value')), $selected)."\n";
}
$choices = $this->getOption('choices');
if (count($this->preferredChoices) > 0) {
$html .= $this->renderChoices(array_intersect_key($choices, $this->preferredChoices), $selected)."\n";
$html .= $this->generator->contentTag('option', $this->getOption('separator'), array('disabled' => true))."\n";
}
$html .= $this->renderChoices(array_diff_key($choices, $this->preferredChoices), $selected)."\n";
return $this->generator->contentTag('select', $html, array_merge($attrs, $attributes));
}
}
/**
* Returns an array of option tags for the choice field
*
* @return array An array of option tags
*/
protected function renderChoices(array $choices, array $selected)
{
$options = array();
foreach ($choices as $key => $option) {
if (is_array($option)) {
$options[] = $this->generator->contentTag(
'optgroup',
"\n".$this->renderChoices($option, $selected)."\n",
array('label' => $this->generator->escape($key))
);
} else {
$attributes = array('value' => $this->generator->escape($key));
if (isset($selected[strval($key)])) {
$attributes['selected'] = true;
}
if ($this->getOption('translate_choices')) {
$option = $this->translate($option);
}
$options[] = $this->generator->contentTag(
'option',
$this->generator->escape($option),
$attributes
);
}
}
return implode("\n", $options);
}
}

View File

@ -21,83 +21,75 @@ use Symfony\Components\Form\Exception\UnexpectedTypeException;
*/
class CollectionField extends FieldGroup
{
/**
* The prototype for the inner fields
* @var FieldInterface
*/
protected $prototype;
/**
* The prototype for the inner fields
* @var FieldInterface
*/
protected $prototype;
/**
* Repeats the given field twice to verify the user's input
*
* @param FieldInterface $innerField
*/
public function __construct(FieldInterface $innerField, array $options = array())
{
$this->prototype = $innerField;
parent::__construct($innerField->getKey(), $options);
}
protected function configure()
{
$this->addOption('modifiable', false);
if ($this->getOption('modifiable'))
/**
* Repeats the given field twice to verify the user's input
*
* @param FieldInterface $innerField
*/
public function __construct(FieldInterface $innerField, array $options = array())
{
$field = $this->newField('$$key$$', null);
// TESTME
$field->setRequired(false);
$this->add($field);
}
}
$this->prototype = $innerField;
public function setData($collection)
{
if (!is_array($collection) && !$collection instanceof Traversable)
{
throw new UnexpectedTypeException('The data must be an array');
parent::__construct($innerField->getKey(), $options);
}
foreach ($collection as $name => $value)
protected function configure()
{
$this->add($this->newField($name, $name));
$this->addOption('modifiable', false);
if ($this->getOption('modifiable')) {
$field = $this->newField('$$key$$', null);
// TESTME
$field->setRequired(false);
$this->add($field);
}
}
parent::setData($collection);
}
public function bind($taintedData)
{
if (is_null($taintedData))
public function setData($collection)
{
$taintedData = array();
if (!is_array($collection) && !$collection instanceof Traversable) {
throw new UnexpectedTypeException('The data must be an array');
}
foreach ($collection as $name => $value) {
$this->add($this->newField($name, $name));
}
parent::setData($collection);
}
foreach ($this as $name => $field)
public function bind($taintedData)
{
if (!isset($taintedData[$name]) && $this->getOption('modifiable') && $name != '$$key$$')
{
$this->remove($name);
}
if (is_null($taintedData)) {
$taintedData = array();
}
foreach ($this as $name => $field) {
if (!isset($taintedData[$name]) && $this->getOption('modifiable') && $name != '$$key$$') {
$this->remove($name);
}
}
foreach ($taintedData as $name => $value) {
if (!isset($this[$name]) && $this->getOption('modifiable')) {
$this->add($this->newField($name, $name));
}
}
return parent::bind($taintedData);
}
foreach ($taintedData as $name => $value)
protected function newField($key, $propertyPath)
{
if (!isset($this[$name]) && $this->getOption('modifiable'))
{
$this->add($this->newField($name, $name));
}
$field = clone $this->prototype;
$field->setKey($key);
$field->setPropertyPath($propertyPath === null ? null : '['.$propertyPath.']');
return $field;
}
return parent::bind($taintedData);
}
protected function newField($key, $propertyPath)
{
$field = clone $this->prototype;
$field->setKey($key);
$field->setPropertyPath($propertyPath === null ? null : '['.$propertyPath.']');
return $field;
}
}

View File

@ -19,124 +19,119 @@ use Symfony\Components\Form\Exception\InvalidOptionsException;
*/
abstract class Configurable
{
/**
* The options and their values
* @var array
*/
private $options = array();
/**
* The options and their values
* @var array
*/
private $options = array();
/**
* The names of the valid options
* @var array
*/
private $knownOptions = array();
/**
* The names of the valid options
* @var array
*/
private $knownOptions = array();
/**
* The names of the required options
* @var array
*/
private $requiredOptions = array();
/**
* The names of the required options
* @var array
*/
private $requiredOptions = array();
/**
* The allowed values for each option
* @var array
*/
private $allowedValues = array();
/**
* The allowed values for each option
* @var array
*/
private $allowedValues = array();
/**
* Reads, validates and stores the given options
*
* @param array $options
*/
public function __construct(array $options = array())
{
$this->options = array_merge($this->options, $options);
$this->configure();
// check option names
if ($diff = array_diff_key($this->options, $this->knownOptions))
/**
* Reads, validates and stores the given options
*
* @param array $options
*/
public function __construct(array $options = array())
{
throw new InvalidOptionsException(sprintf('%s does not support the following options: "%s".', get_class($this), implode('", "', array_keys($diff))), array_keys($diff));
$this->options = array_merge($this->options, $options);
$this->configure();
// check option names
if ($diff = array_diff_key($this->options, $this->knownOptions)) {
throw new InvalidOptionsException(sprintf('%s does not support the following options: "%s".', get_class($this), implode('", "', array_keys($diff))), array_keys($diff));
}
// check required options
if ($diff = array_diff_key($this->requiredOptions, $this->options)) {
throw new MissingOptionsException(sprintf('%s requires the following options: \'%s\'.', get_class($this), implode('", "', array_keys($diff))), array_keys($diff));
}
}
// check required options
if ($diff = array_diff_key($this->requiredOptions, $this->options))
/**
* Configures the valid options
*
* This method should call addOption() or addRequiredOption() for every
* accepted option.
*/
protected function configure()
{
throw new MissingOptionsException(sprintf('%s requires the following options: \'%s\'.', get_class($this), implode('", "', array_keys($diff))), array_keys($diff));
}
}
/**
* Configures the valid options
*
* This method should call addOption() or addRequiredOption() for every
* accepted option.
*/
protected function configure()
{
}
/**
* Returns an option value.
*
* @param string $name The option name
*
* @return mixed The option value
*/
public function getOption($name)
{
return array_key_exists($name, $this->options) ? $this->options[$name] : null;
}
/**
* Adds a new option value with a default value.
*
* @param string $name The option name
* @param mixed $value The default value
*/
protected function addOption($name, $value = null, array $allowedValues = array())
{
$this->knownOptions[$name] = true;
if (!array_key_exists($name, $this->options))
{
$this->options[$name] = $value;
}
if (count($allowedValues) > 0 && !in_array($this->options[$name], $allowedValues))
/**
* Returns an option value.
*
* @param string $name The option name
*
* @return mixed The option value
*/
public function getOption($name)
{
throw new InvalidOptionsException(sprintf('The option "%s" is expected to be one of "%s", but is "%s"', $name, implode('", "', $allowedValues), $this->options[$name]), array($name));
return array_key_exists($name, $this->options) ? $this->options[$name] : null;
}
}
/**
* Adds a required option.
*
* @param string $name The option name
*/
protected function addRequiredOption($name, array $allowedValues = array())
{
$this->knownOptions[$name] = true;
$this->requiredOptions[$name] = true;
// only test if the option is set, otherwise an error will be thrown
// anyway
if (isset($this->options[$name]) && count($allowedValues) > 0 && !in_array($this->options[$name], $allowedValues))
/**
* Adds a new option value with a default value.
*
* @param string $name The option name
* @param mixed $value The default value
*/
protected function addOption($name, $value = null, array $allowedValues = array())
{
throw new InvalidOptionsException(sprintf('The option "%s" is expected to be one of "%s", but is "%s"', $name, implode('", "', $allowedValues), $this->options[$name]), array($name));
}
}
$this->knownOptions[$name] = true;
/**
* Returns true if the option exists.
*
* @param string $name The option name
*
* @return bool true if the option is set, false otherwise
*/
public function hasOption($name)
{
return isset($this->options[$name]);
}
if (!array_key_exists($name, $this->options)) {
$this->options[$name] = $value;
}
if (count($allowedValues) > 0 && !in_array($this->options[$name], $allowedValues)) {
throw new InvalidOptionsException(sprintf('The option "%s" is expected to be one of "%s", but is "%s"', $name, implode('", "', $allowedValues), $this->options[$name]), array($name));
}
}
/**
* Adds a required option.
*
* @param string $name The option name
*/
protected function addRequiredOption($name, array $allowedValues = array())
{
$this->knownOptions[$name] = true;
$this->requiredOptions[$name] = true;
// only test if the option is set, otherwise an error will be thrown
// anyway
if (isset($this->options[$name]) && count($allowedValues) > 0 && !in_array($this->options[$name], $allowedValues)) {
throw new InvalidOptionsException(sprintf('The option "%s" is expected to be one of "%s", but is "%s"', $name, implode('", "', $allowedValues), $this->options[$name]), array($name));
}
}
/**
* Returns true if the option exists.
*
* @param string $name The option name
*
* @return bool true if the option is set, false otherwise
*/
public function hasOption($name)
{
return isset($this->options[$name]);
}
}

View File

@ -4,11 +4,11 @@ namespace Symfony\Components\Form\Configurator;
interface ConfiguratorInterface
{
public function initialize($object);
public function initialize($object);
public function getClass($fieldName);
public function getClass($fieldName);
public function getOptions($fieldName);
public function getOptions($fieldName);
public function isRequired($fieldName);
public function isRequired($fieldName);
}

View File

@ -4,32 +4,32 @@ namespace Symfony\Components\Form\Configurator;
class ValidatorConfigurator implements ConfiguratorInterface
{
protected $metaData = null;
protected $classMetaData = null;
protected $metaData = null;
protected $classMetaData = null;
public function __construct(MetaDataInterface $metaData)
{
$this->metaData = $metaData;
}
public function __construct(MetaDataInterface $metaData)
{
$this->metaData = $metaData;
}
public function initialize($object)
{
$this->classMetaData = $this->metaData->getClassMetaData(get_class($object));
}
public function initialize($object)
{
$this->classMetaData = $this->metaData->getClassMetaData(get_class($object));
}
public function getClass($fieldName)
{
public function getClass($fieldName)
{
}
}
public function getOptions($fieldName)
{
public function getOptions($fieldName)
{
}
}
public function isRequired($fieldName)
{
return $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotNull')
|| $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotEmpty');
}
public function isRequired($fieldName)
{
return $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotNull')
|| $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotEmpty');
}
}

View File

@ -19,244 +19,224 @@ use Symfony\Components\Form\ValueTransformer\DateTimeToArrayTransformer;
class DateField extends HybridField
{
const FULL = 'full';
const LONG = 'long';
const MEDIUM = 'medium';
const SHORT = 'short';
const FULL = 'full';
const LONG = 'long';
const MEDIUM = 'medium';
const SHORT = 'short';
const DATETIME = 'datetime';
const STRING = 'string';
const TIMESTAMP = 'timestamp';
const RAW = 'raw';
const DATETIME = 'datetime';
const STRING = 'string';
const TIMESTAMP = 'timestamp';
const RAW = 'raw';
const INPUT = 'input';
const CHOICE = 'choice';
const INPUT = 'input';
const CHOICE = 'choice';
protected static $formats = array(
self::FULL,
self::LONG,
self::MEDIUM,
self::SHORT,
);
protected static $intlFormats = array(
self::FULL => \IntlDateFormatter::FULL,
self::LONG => \IntlDateFormatter::LONG,
self::MEDIUM => \IntlDateFormatter::MEDIUM,
self::SHORT => \IntlDateFormatter::SHORT,
);
protected static $widgets = array(
self::INPUT,
self::CHOICE,
);
protected static $types = array(
self::DATETIME,
self::STRING,
self::TIMESTAMP,
self::RAW,
);
/**
* The ICU formatter instance
* @var \IntlDateFormatter
*/
protected $formatter;
/**
* Configures the text field.
*
* Available options:
*
* * widget: How to render the field ("input" or "select"). Default: "input"
* * years: An array of years for the year select tag (optional)
* * months: An array of months for the month select tag (optional)
* * days: An array of days for the day select tag (optional)
* * format: See DateValueTransformer. Default: medium
* * type: The type of the date ("date", "datetime" or "timestamp"). Default: "date"
* * data_timezone: The timezone of the data
* * user_timezone: The timezone of the user entering a new value
* * pattern: The pattern for the select boxes when "widget" is "select".
* You can use the placeholders "%year%", "%month%" and "%day%".
* Default: locale dependent
*
* @param array $options Options for this field
* @throws \InvalidArgumentException Thrown if you want to show a timestamp with the select widget.
*/
protected function configure()
{
$this->addOption('years', range(date('Y') - 5, date('Y') + 5));
$this->addOption('months', range(1, 12));
$this->addOption('days', range(1, 31));
$this->addOption('format', self::MEDIUM, self::$formats);
$this->addOption('type', self::DATETIME, self::$types);
$this->addOption('data_timezone', 'UTC');
$this->addOption('user_timezone', 'UTC');
$this->addOption('widget', self::CHOICE, self::$widgets);
$this->addOption('pattern');
$this->formatter = new \IntlDateFormatter(
$this->locale,
self::$intlFormats[$this->getOption('format')],
\IntlDateFormatter::NONE
protected static $formats = array(
self::FULL,
self::LONG,
self::MEDIUM,
self::SHORT,
);
$transformers = array();
protected static $intlFormats = array(
self::FULL => \IntlDateFormatter::FULL,
self::LONG => \IntlDateFormatter::LONG,
self::MEDIUM => \IntlDateFormatter::MEDIUM,
self::SHORT => \IntlDateFormatter::SHORT,
);
if ($this->getOption('type') === self::STRING)
protected static $widgets = array(
self::INPUT,
self::CHOICE,
);
protected static $types = array(
self::DATETIME,
self::STRING,
self::TIMESTAMP,
self::RAW,
);
/**
* The ICU formatter instance
* @var \IntlDateFormatter
*/
protected $formatter;
/**
* Configures the text field.
*
* Available options:
*
* * widget: How to render the field ("input" or "select"). Default: "input"
* * years: An array of years for the year select tag (optional)
* * months: An array of months for the month select tag (optional)
* * days: An array of days for the day select tag (optional)
* * format: See DateValueTransformer. Default: medium
* * type: The type of the date ("date", "datetime" or "timestamp"). Default: "date"
* * data_timezone: The timezone of the data
* * user_timezone: The timezone of the user entering a new value
* * pattern: The pattern for the select boxes when "widget" is "select".
* You can use the placeholders "%year%", "%month%" and "%day%".
* Default: locale dependent
*
* @param array $options Options for this field
* @throws \InvalidArgumentException Thrown if you want to show a timestamp with the select widget.
*/
protected function configure()
{
$transformers[] = new StringToDateTimeTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
'format' => 'Y-m-d',
));
}
else if ($this->getOption('type') === self::TIMESTAMP)
{
$transformers[] = new TimestampToDateTimeTransformer(array(
'output_timezone' => $this->getOption('data_timezone'),
'input_timezone' => $this->getOption('data_timezone'),
));
}
else if ($this->getOption('type') === self::RAW)
{
$transformers[] = new ReversedTransformer(new DateTimeToArrayTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
'fields' => array('year', 'month', 'day'),
)));
$this->addOption('years', range(date('Y') - 5, date('Y') + 5));
$this->addOption('months', range(1, 12));
$this->addOption('days', range(1, 31));
$this->addOption('format', self::MEDIUM, self::$formats);
$this->addOption('type', self::DATETIME, self::$types);
$this->addOption('data_timezone', 'UTC');
$this->addOption('user_timezone', 'UTC');
$this->addOption('widget', self::CHOICE, self::$widgets);
$this->addOption('pattern');
$this->formatter = new \IntlDateFormatter(
$this->locale,
self::$intlFormats[$this->getOption('format')],
\IntlDateFormatter::NONE
);
$transformers = array();
if ($this->getOption('type') === self::STRING) {
$transformers[] = new StringToDateTimeTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
'format' => 'Y-m-d',
));
} else if ($this->getOption('type') === self::TIMESTAMP) {
$transformers[] = new TimestampToDateTimeTransformer(array(
'output_timezone' => $this->getOption('data_timezone'),
'input_timezone' => $this->getOption('data_timezone'),
));
} else if ($this->getOption('type') === self::RAW) {
$transformers[] = new ReversedTransformer(new DateTimeToArrayTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
'fields' => array('year', 'month', 'day'),
)));
}
if ($this->getOption('widget') === self::INPUT) {
$transformers[] = new DateTimeToLocalizedStringTransformer(array(
'date_format' => $this->getOption('format'),
'time_format' => DateTimeToLocalizedStringTransformer::NONE,
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('user_timezone'),
));
$this->setFieldMode(self::FIELD);
} else {
$transformers[] = new DateTimeToArrayTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('user_timezone'),
));
$this->setFieldMode(self::GROUP);
$this->add(new ChoiceField('year', array(
'choices' => $this->generatePaddedChoices($this->getOption('years'), 4),
)));
$this->add(new ChoiceField('month', array(
'choices' => $this->generateMonthChoices($this->getOption('months')),
)));
$this->add(new ChoiceField('day', array(
'choices' => $this->generatePaddedChoices($this->getOption('days'), 2),
)));
}
if (count($transformers) > 0) {
$this->setValueTransformer(new ValueTransformerChain($transformers));
}
}
if ($this->getOption('widget') === self::INPUT)
/**
* Generates an array of choices for the given values
*
* If the values are shorter than $padLength characters, they are padded with
* zeros on the left side.
*
* @param array $values The available choices
* @param integer $padLength The length to pad the choices
* @return array An array with the input values as keys and the
* padded values as values
*/
protected function generatePaddedChoices(array $values, $padLength)
{
$transformers[] = new DateTimeToLocalizedStringTransformer(array(
'date_format' => $this->getOption('format'),
'time_format' => DateTimeToLocalizedStringTransformer::NONE,
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('user_timezone'),
));
$choices = array();
$this->setFieldMode(self::FIELD);
}
else
{
$transformers[] = new DateTimeToArrayTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('user_timezone'),
));
foreach ($values as $value) {
$choices[$value] = str_pad($value, $padLength, '0', STR_PAD_LEFT);
}
$this->setFieldMode(self::GROUP);
$this->add(new ChoiceField('year', array(
'choices' => $this->generatePaddedChoices($this->getOption('years'), 4),
)));
$this->add(new ChoiceField('month', array(
'choices' => $this->generateMonthChoices($this->getOption('months')),
)));
$this->add(new ChoiceField('day', array(
'choices' => $this->generatePaddedChoices($this->getOption('days'), 2),
)));
return $choices;
}
if (count($transformers) > 0)
/**
* Generates an array of localized month choices
*
* @param array $months The month numbers to generate
* @return array The localized months respecting the configured
* locale and date format
*/
protected function generateMonthChoices(array $months)
{
$this->setValueTransformer(new ValueTransformerChain($transformers));
}
}
$pattern = $this->formatter->getPattern();
/**
* Generates an array of choices for the given values
*
* If the values are shorter than $padLength characters, they are padded with
* zeros on the left side.
*
* @param array $values The available choices
* @param integer $padLength The length to pad the choices
* @return array An array with the input values as keys and the
* padded values as values
*/
protected function generatePaddedChoices(array $values, $padLength)
{
$choices = array();
if (preg_match('/M+/', $pattern, $matches)) {
$this->formatter->setPattern($matches[0]);
$choices = array();
foreach ($values as $value)
{
$choices[$value] = str_pad($value, $padLength, '0', STR_PAD_LEFT);
foreach ($months as $month) {
$choices[$month] = $this->formatter->format(gmmktime(0, 0, 0, $month));
}
$this->formatter->setPattern($pattern);
} else {
$choices = $this->generatePaddedChoices($months, 2);
}
return $choices;
}
return $choices;
}
/**
* Generates an array of localized month choices
*
* @param array $months The month numbers to generate
* @return array The localized months respecting the configured
* locale and date format
*/
protected function generateMonthChoices(array $months)
{
$pattern = $this->formatter->getPattern();
if (preg_match('/M+/', $pattern, $matches))
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
$this->formatter->setPattern($matches[0]);
$choices = array();
if ($this->getOption('widget') === self::INPUT) {
return $this->generator->tag('input', array_merge(array(
'id' => $this->getId(),
'name' => $this->getName(),
'value' => $this->getDisplayedData(),
'type' => 'text',
), $attributes));
} else {
// set order as specified in the pattern
if ($this->getOption('pattern')) {
$pattern = $this->getOption('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
else if (preg_match('/^([yMd]+).+([yMd]+).+([yMd]+)$/', $this->formatter->getPattern())) {
$pattern = preg_replace(array('/y+/', '/M+/', '/d+/'), array('%year%', '%month%', '%day%'), $this->formatter->getPattern());
}
// default fallback
else {
$pattern = '%year%-%month%-%day%';
}
foreach ($months as $month)
{
$choices[$month] = $this->formatter->format(gmmktime(0, 0, 0, $month));
}
$this->formatter->setPattern($pattern);
return str_replace(array('%year%', '%month%', '%day%'), array(
$this->get('year')->render($attributes),
$this->get('month')->render($attributes),
$this->get('day')->render($attributes),
), $pattern);
}
}
else
{
$choices = $this->generatePaddedChoices($months, 2);
}
return $choices;
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
if ($this->getOption('widget') === self::INPUT)
{
return $this->generator->tag('input', array_merge(array(
'id' => $this->getId(),
'name' => $this->getName(),
'value' => $this->getDisplayedData(),
'type' => 'text',
), $attributes));
}
else
{
// set order as specified in the pattern
if ($this->getOption('pattern'))
{
$pattern = $this->getOption('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
else if (preg_match('/^([yMd]+).+([yMd]+).+([yMd]+)$/', $this->formatter->getPattern()))
{
$pattern = preg_replace(array('/y+/', '/M+/', '/d+/'), array('%year%', '%month%', '%day%'), $this->formatter->getPattern());
}
// default fallback
else
{
$pattern = '%year%-%month%-%day%';
}
return str_replace(array('%year%', '%month%', '%day%'), array(
$this->get('year')->render($attributes),
$this->get('month')->render($attributes),
$this->get('day')->render($attributes),
), $pattern);
}
}
}

View File

@ -14,115 +14,112 @@ use Symfony\Components\Form\ValueTransformer\ValueTransformerChain;
*/
class DateTimeField extends FieldGroup
{
const DATETIME = 'datetime';
const STRING = 'string';
const TIMESTAMP = 'timestamp';
const DATETIME = 'datetime';
const STRING = 'string';
const TIMESTAMP = 'timestamp';
protected static $types = array(
self::DATETIME,
self::STRING,
self::TIMESTAMP,
);
protected static $types = array(
self::DATETIME,
self::STRING,
self::TIMESTAMP,
);
protected static $dateWidgets = array(
DateField::CHOICE,
DateField::INPUT,
);
protected static $dateWidgets = array(
DateField::CHOICE,
DateField::INPUT,
);
protected static $timeWidgets = array(
TimeField::CHOICE,
TimeField::INPUT,
);
protected static $timeWidgets = array(
TimeField::CHOICE,
TimeField::INPUT,
);
/**
* {@inheritDoc}
*/
public function configure()
{
$this->addOption('years', range(date('Y') - 5, date('Y') + 5));
$this->addOption('months', range(1, 12));
$this->addOption('days', range(1, 31));
$this->addOption('hours', range(0, 23));
$this->addOption('minutes', range(0, 59));
$this->addOption('seconds', range(0, 59));
$this->addOption('data_timezone', 'UTC');
$this->addOption('user_timezone', 'UTC');
$this->addOption('date_widget', DateField::INPUT, self::$dateWidgets);
$this->addOption('time_widget', TimeField::CHOICE, self::$timeWidgets);
$this->addOption('type', self::DATETIME, self::$types);
$this->addOption('with_seconds', false);
$this->add(new DateField('date', array(
'type' => DateField::RAW,
'widget' => $this->getOption('date_widget'),
'data_timezone' => $this->getOption('user_timezone'),
'user_timezone' => $this->getOption('user_timezone'),
'years' => $this->getOption('years'),
'months' => $this->getOption('months'),
'days' => $this->getOption('days'),
)));
$this->add(new TimeField('time', array(
'type' => TimeField::RAW,
'widget' => $this->getOption('time_widget'),
'data_timezone' => $this->getOption('user_timezone'),
'user_timezone' => $this->getOption('user_timezone'),
'with_seconds' => $this->getOption('with_seconds'),
'hours' => $this->getOption('hours'),
'minutes' => $this->getOption('minutes'),
'seconds' => $this->getOption('seconds'),
)));
$transformers = array();
if ($this->getOption('type') == self::STRING)
/**
* {@inheritDoc}
*/
public function configure()
{
$transformers[] = new StringToDateTimeTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
));
}
else if ($this->getOption('type') == self::TIMESTAMP)
{
$transformers[] = new TimestampToDateTimeTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
));
$this->addOption('years', range(date('Y') - 5, date('Y') + 5));
$this->addOption('months', range(1, 12));
$this->addOption('days', range(1, 31));
$this->addOption('hours', range(0, 23));
$this->addOption('minutes', range(0, 59));
$this->addOption('seconds', range(0, 59));
$this->addOption('data_timezone', 'UTC');
$this->addOption('user_timezone', 'UTC');
$this->addOption('date_widget', DateField::INPUT, self::$dateWidgets);
$this->addOption('time_widget', TimeField::CHOICE, self::$timeWidgets);
$this->addOption('type', self::DATETIME, self::$types);
$this->addOption('with_seconds', false);
$this->add(new DateField('date', array(
'type' => DateField::RAW,
'widget' => $this->getOption('date_widget'),
'data_timezone' => $this->getOption('user_timezone'),
'user_timezone' => $this->getOption('user_timezone'),
'years' => $this->getOption('years'),
'months' => $this->getOption('months'),
'days' => $this->getOption('days'),
)));
$this->add(new TimeField('time', array(
'type' => TimeField::RAW,
'widget' => $this->getOption('time_widget'),
'data_timezone' => $this->getOption('user_timezone'),
'user_timezone' => $this->getOption('user_timezone'),
'with_seconds' => $this->getOption('with_seconds'),
'hours' => $this->getOption('hours'),
'minutes' => $this->getOption('minutes'),
'seconds' => $this->getOption('seconds'),
)));
$transformers = array();
if ($this->getOption('type') == self::STRING) {
$transformers[] = new StringToDateTimeTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
));
} else if ($this->getOption('type') == self::TIMESTAMP) {
$transformers[] = new TimestampToDateTimeTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
));
}
$transformers[] = new DateTimeToArrayTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('user_timezone'),
));
$this->setValueTransformer(new ValueTransformerChain($transformers));
}
$transformers[] = new DateTimeToArrayTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('user_timezone'),
));
/**
* {@inheritDoc}
*/
protected function transform($value)
{
$value = parent::transform($value);
$this->setValueTransformer(new ValueTransformerChain($transformers));
}
return array('date' => $value, 'time' => $value);
}
/**
* {@inheritDoc}
*/
protected function transform($value)
{
$value = parent::transform($value);
/**
* {@inheritDoc}
*/
protected function reverseTransform($value)
{
return parent::reverseTransform(array_merge($value['date'], $value['time']));
}
return array('date' => $value, 'time' => $value);
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
$html = $this->get('date')->render($attributes)."\n";
$html .= $this->get('time')->render($attributes);
/**
* {@inheritDoc}
*/
protected function reverseTransform($value)
{
return parent::reverseTransform(array_merge($value['date'], $value['time']));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
$html = $this->get('date')->render($attributes)."\n";
$html .= $this->get('time')->render($attributes);
return $html;
}
return $html;
}
}

View File

@ -4,17 +4,17 @@ namespace Symfony\Components\Form\Exception;
class InvalidOptionsException extends FormException
{
private $options;
private $options;
public function __construct($message, array $options)
{
parent::__construct($message);
public function __construct($message, array $options)
{
parent::__construct($message);
$this->options = $options;
}
$this->options = $options;
}
public function getOptions()
{
return $this->options;
}
public function getOptions()
{
return $this->options;
}
}

View File

@ -4,17 +4,17 @@ namespace Symfony\Components\Form\Exception;
class MissingOptionsException extends FormException
{
private $options;
private $options;
public function __construct($message, array $options)
{
parent::__construct($message);
public function __construct($message, array $options)
{
parent::__construct($message);
$this->options = $options;
}
$this->options = $options;
}
public function getOptions()
{
return $this->options;
}
public function getOptions()
{
return $this->options;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -12,248 +12,248 @@ use Symfony\Components\I18N\TranslatorInterface;
*/
interface FieldInterface extends Localizable, Translatable
{
/**
* Marks a constraint violation in a form field
* @var integer
*/
const FIELD_ERROR = 0;
/**
* Marks a constraint violation in a form field
* @var integer
*/
const FIELD_ERROR = 0;
/**
* Marks a constraint violation in the data of a form field
* @var integer
*/
const DATA_ERROR = 1;
/**
* Marks a constraint violation in the data of a form field
* @var integer
*/
const DATA_ERROR = 1;
/**
* Clones this field.
*/
public function __clone();
/**
* Clones this field.
*/
public function __clone();
/**
* Sets the parent field.
*
* @param FieldInterface $parent The parent field
*/
public function setParent(FieldInterface $parent = null);
/**
* Sets the parent field.
*
* @param FieldInterface $parent The parent field
*/
public function setParent(FieldInterface $parent = null);
/**
* Sets the key by which the field is identified in field groups.
*
* Once this field is nested in a field group, i.e. after setParent() was
* called for the first time, this method should throw an exception.
*
* @param string $key The key of the field
* @throws BadMethodCallException When the field already has a parent
*/
public function setKey($key);
/**
* Sets the key by which the field is identified in field groups.
*
* Once this field is nested in a field group, i.e. after setParent() was
* called for the first time, this method should throw an exception.
*
* @param string $key The key of the field
* @throws BadMethodCallException When the field already has a parent
*/
public function setKey($key);
/**
* Returns the key by which the field is identified in field groups.
*
* @return string The key of the field.
*/
public function getKey();
/**
* Returns the key by which the field is identified in field groups.
*
* @return string The key of the field.
*/
public function getKey();
/**
* Returns the name of the field.
*
* @return string When the field has no parent, the name is equal to its
* key. If the field has a parent, the name is composed of
* the parent's name and the field's key, where the field's
* key is wrapped in squared brackets
* (e.g. "parent_name[field_key]")
*/
public function getName();
/**
* Returns the name of the field.
*
* @return string When the field has no parent, the name is equal to its
* key. If the field has a parent, the name is composed of
* the parent's name and the field's key, where the field's
* key is wrapped in squared brackets
* (e.g. "parent_name[field_key]")
*/
public function getName();
/**
* Returns the ID of the field.
*
* @return string The ID of a field is equal to its name, where all
* sequences of squared brackets are replaced by a single
* underscore (e.g. if the name is "parent_name[field_key]",
* the ID is "parent_name_field_key").
*/
public function getId();
/**
* Returns the ID of the field.
*
* @return string The ID of a field is equal to its name, where all
* sequences of squared brackets are replaced by a single
* underscore (e.g. if the name is "parent_name[field_key]",
* the ID is "parent_name_field_key").
*/
public function getId();
/**
* Sets the property path
*
* The property path determines the property or a sequence of properties
* that a field updates in the data of the field group.
*
* @param string $propertyPath
*/
public function setPropertyPath($propertyPath);
/**
* Sets the property path
*
* The property path determines the property or a sequence of properties
* that a field updates in the data of the field group.
*
* @param string $propertyPath
*/
public function setPropertyPath($propertyPath);
/**
* Returns the property path of the field
*
* @return PropertyPath
*/
public function getPropertyPath();
/**
* Returns the property path of the field
*
* @return PropertyPath
*/
public function getPropertyPath();
/**
* Writes a property value of the object into the field
*
* The chosen property is determined by the field's property path.
*
* @param array|object $objectOrArray
*/
public function updateFromObject(&$objectOrArray);
/**
* Writes a property value of the object into the field
*
* The chosen property is determined by the field's property path.
*
* @param array|object $objectOrArray
*/
public function updateFromObject(&$objectOrArray);
/**
* Writes a the field value into a property of the object
*
* The chosen property is determined by the field's property path.
*
* @param array|object $objectOrArray
*/
public function updateObject(&$objectOrArray);
/**
* Writes a the field value into a property of the object
*
* The chosen property is determined by the field's property path.
*
* @param array|object $objectOrArray
*/
public function updateObject(&$objectOrArray);
/**
* Returns the normalized data of the field.
*
* @return mixed When the field is not bound, the default data is returned.
* When the field is bound, the normalized bound data is
* returned if the field is valid, null otherwise.
*/
public function getData();
/**
* Returns the normalized data of the field.
*
* @return mixed When the field is not bound, the default data is returned.
* When the field is bound, the normalized bound data is
* returned if the field is valid, null otherwise.
*/
public function getData();
/**
* Returns the data of the field as it is displayed to the user.
*
* @return string|array When the field is not bound, the transformed
* default data is returned. When the field is bound,
* the bound data is returned.
*/
public function getDisplayedData();
/**
* Returns the data of the field as it is displayed to the user.
*
* @return string|array When the field is not bound, the transformed
* default data is returned. When the field is bound,
* the bound data is returned.
*/
public function getDisplayedData();
/**
* Sets the default data
*
* @param mixed $default The default data
* @throws UnexpectedTypeException If the default data is invalid
*/
public function setData($default);
/**
* Sets the default data
*
* @param mixed $default The default data
* @throws UnexpectedTypeException If the default data is invalid
*/
public function setData($default);
/**
* Binds POST data to the field, transforms and validates it.
*
* @param string|array $taintedData The POST data
* @return boolean Whether the form is valid
* @throws InvalidConfigurationException when the field is not configured
* correctly
*/
public function bind($taintedData);
/**
* Binds POST data to the field, transforms and validates it.
*
* @param string|array $taintedData The POST data
* @return boolean Whether the form is valid
* @throws InvalidConfigurationException when the field is not configured
* correctly
*/
public function bind($taintedData);
/**
* Recursively adds constraint violations to the fields
*
* Violations in the form fields usually have property paths like:
*
* <code>
* iterator[firstName].data
* iterator[firstName].displayedData
* iterator[Address].iterator[street].displayedData
* ...
* </code>
*
* Violations in the form data usually have property paths like:
*
* <code>
* data.firstName
* data.Address.street
* ...
* </code>
*
* @param FieldInterface $field
* @param PropertyPath $path
* @param ConstraintViolation$violation
*/
public function addError($message, PropertyPath $path = null, $type = null);
/**
* Recursively adds constraint violations to the fields
*
* Violations in the form fields usually have property paths like:
*
* <code>
* iterator[firstName].data
* iterator[firstName].displayedData
* iterator[Address].iterator[street].displayedData
* ...
* </code>
*
* Violations in the form data usually have property paths like:
*
* <code>
* data.firstName
* data.Address.street
* ...
* </code>
*
* @param FieldInterface $field
* @param PropertyPath $path
* @param ConstraintViolation$violation
*/
public function addError($message, PropertyPath $path = null, $type = null);
/**
* Renders this field.
*
* @param array $attributes The attributes to include in the rendered
* output
* @return string The rendered output of this field
*/
public function render(array $attributes = array());
/**
* Renders this field.
*
* @param array $attributes The attributes to include in the rendered
* output
* @return string The rendered output of this field
*/
public function render(array $attributes = array());
/**
* Renders the errors of this field.
*
* @return string The rendered output of the field errors
*/
public function renderErrors();
/**
* Renders the errors of this field.
*
* @return string The rendered output of the field errors
*/
public function renderErrors();
/**
* Returns whether the field is bound.
*
* @return boolean
*/
public function isBound();
/**
* Returns whether the field is bound.
*
* @return boolean
*/
public function isBound();
/**
* Returns whether the field is valid.
*
* @return boolean
*/
public function isValid();
/**
* Returns whether the field is valid.
*
* @return boolean
*/
public function isValid();
/**
* Returns whether the field requires a multipart form.
*
* @return boolean
*/
public function isMultipart();
/**
* Returns whether the field requires a multipart form.
*
* @return boolean
*/
public function isMultipart();
/**
* Returns whether the field is required to be filled out.
*
* If the field has a parent and the parent is not required, this method
* will always return false. Otherwise the value set with setRequired()
* is returned.
*
* @return boolean
*/
public function isRequired();
/**
* Returns whether the field is required to be filled out.
*
* If the field has a parent and the parent is not required, this method
* will always return false. Otherwise the value set with setRequired()
* is returned.
*
* @return boolean
*/
public function isRequired();
/**
* Returns whether this field is disabled
*
* The content of a disabled field is displayed, but not allowed to be
* modified. The validation of modified, disabled fields should fail.
*
* Fields whose parents are disabled are considered disabled regardless of
* their own state.
*
* @return boolean
*/
public function isDisabled();
/**
* Returns whether this field is disabled
*
* The content of a disabled field is displayed, but not allowed to be
* modified. The validation of modified, disabled fields should fail.
*
* Fields whose parents are disabled are considered disabled regardless of
* their own state.
*
* @return boolean
*/
public function isDisabled();
/**
* Returns whether the field is hidden
*
* @return boolean
*/
public function isHidden();
/**
* Returns whether the field is hidden
*
* @return boolean
*/
public function isHidden();
/**
* Sets whether this field is required to be filled out when submitted.
*
* @param boolean $required
*/
public function setRequired($required);
/**
* Sets whether this field is required to be filled out when submitted.
*
* @param boolean $required
*/
public function setRequired($required);
/**
* Sets the generator used for rendering HTML.
*
* Usually there is one generator instance shared between all fields of a
* form.
*
* @param string $charset
*/
public function setGenerator(HtmlGeneratorInterface $generator);
/**
* Sets the generator used for rendering HTML.
*
* Usually there is one generator instance shared between all fields of a
* form.
*
* @param string $charset
*/
public function setGenerator(HtmlGeneratorInterface $generator);
}

View File

@ -34,528 +34,491 @@ use Symfony\Components\File\UploadedFile;
*/
class Form extends FieldGroup
{
protected static $defaultCsrfSecret = null;
protected static $defaultCsrfProtection = false;
protected static $defaultCsrfFieldName = '_csrf_token';
protected static $defaultLocale = null;
protected static $defaultTranslator = null;
protected static $defaultCsrfSecret = null;
protected static $defaultCsrfProtection = false;
protected static $defaultCsrfFieldName = '_csrf_token';
protected static $defaultLocale = null;
protected static $defaultTranslator = null;
protected $validator = null;
protected $validationGroups = null;
protected $validator = null;
protected $validationGroups = null;
private $csrfSecret = null;
private $csrfFieldName = null;
private $csrfSecret = null;
private $csrfFieldName = null;
/**
* Constructor.
*
* @param array $defaults An array of field default values
* @param array $options An array of options
* @param string $defaultCsrfSecret A Csrf secret
*/
public function __construct($name, $object, ValidatorInterface $validator, array $options = array())
{
$this->generator = new HtmlGenerator();
$this->validator = $validator;
parent::__construct($name, $options);
$this->setData($object);
$this->setCsrfFieldName(self::$defaultCsrfFieldName);
if (self::$defaultCsrfSecret !== null)
/**
* Constructor.
*
* @param array $defaults An array of field default values
* @param array $options An array of options
* @param string $defaultCsrfSecret A Csrf secret
*/
public function __construct($name, $object, ValidatorInterface $validator, array $options = array())
{
$this->setCsrfSecret(self::$defaultCsrfSecret);
}
else
{
$this->setCsrfSecret(md5(__FILE__.php_uname()));
}
$this->generator = new HtmlGenerator();
$this->validator = $validator;
if (self::$defaultCsrfProtection !== false)
{
$this->enableCsrfProtection();
}
parent::__construct($name, $options);
if (self::$defaultLocale !== null)
{
$this->setLocale(self::$defaultLocale);
}
$this->setData($object);
$this->setCsrfFieldName(self::$defaultCsrfFieldName);
if (self::$defaultTranslator !== null)
{
$this->setTranslator(self::$defaultTranslator);
}
}
/**
* Sets the charset used for rendering HTML
*
* This method overrides the internal HTML generator! If you want to use
* your own generator, use setGenerator() instead.
*
* @param string $charset
*/
public function setCharset($charset)
{
$this->setGenerator(new HtmlGenerator($charset));
}
/**
* Sets the validation groups for this form.
*
* @param array|string $validationGroups
*/
public function setValidationGroups($validationGroups)
{
$this->validationGroups = $validationGroups === null ? $validationGroups : (array) $validationGroups;
}
/**
* Returns the validation groups for this form.
*
* @return array
*/
public function getValidationGroups()
{
return $this->validationGroups;
}
/**
* Sets the default locale for newly created forms.
*
* @param string $defaultLocale
*/
static public function setDefaultLocale($defaultLocale)
{
self::$defaultLocale = $defaultLocale;
}
/**
* Returns the default locale for newly created forms.
*
* @return string
*/
static public function getDefaultLocale()
{
return self::$defaultLocale;
}
/**
* Sets the default translator for newly created forms.
*
* @param TranslatorInterface $defaultTranslator
*/
static public function setDefaultTranslator(TranslatorInterface $defaultTranslator)
{
self::$defaultTranslator = $defaultTranslator;
}
/**
* Returns the default translator for newly created forms.
*
* @return TranslatorInterface
*/
static public function getDefaultTranslator()
{
return self::$defaultTranslator;
}
/**
* Binds the form with values and files.
*
* This method is final because it is very easy to break a form when
* overriding this method and adding logic that depends on $taintedFiles.
* You should override doBind() instead where the uploaded files are
* already merged into the data array.
*
* @param array $taintedValues The form data of the $_POST array
* @param array $taintedFiles The form data of the $_FILES array
* @return boolean Whether the form is valid
*/
final public function bind($taintedValues, array $taintedFiles = null)
{
if ($taintedFiles === null)
{
if ($this->isMultipart() && $this->getParent() === null)
{
throw new \InvalidArgumentException('You must provide a files array for multipart forms');
}
$taintedFiles = array();
}
else
{
$taintedFiles = self::convertFileInformation(self::fixPhpFilesArray($taintedFiles));
}
$this->doBind(self::deepArrayUnion($taintedValues, $taintedFiles));
if ($this->getParent() === null)
{
if ($violations = $this->validator->validate($this, $this->getValidationGroups()))
{
foreach ($violations as $violation)
{
$propertyPath = new PropertyPath($violation->getPropertyPath());
if ($propertyPath->getCurrent() == 'data')
{
$type = self::DATA_ERROR;
$propertyPath->next(); // point at the first data element
}
else
{
$type = self::FIELD_ERROR;
}
$this->addError($violation->getMessage(), $propertyPath, $type);
if (self::$defaultCsrfSecret !== null) {
$this->setCsrfSecret(self::$defaultCsrfSecret);
} else {
$this->setCsrfSecret(md5(__FILE__.php_uname()));
}
}
}
}
/**
* Binds the form with the given data.
*
* @param array $taintedData The data to bind to the form
* @return boolean Whether the form is valid
*/
protected function doBind(array $taintedData)
{
parent::bind($taintedData);
}
if (self::$defaultCsrfProtection !== false) {
$this->enableCsrfProtection();
}
/**
* Gets the stylesheet paths associated with the form.
*
* @return array An array of stylesheet paths
*/
public function getStylesheets()
{
return $this->getWidget()->getStylesheets();
}
if (self::$defaultLocale !== null) {
$this->setLocale(self::$defaultLocale);
}
/**
* Gets the JavaScript paths associated with the form.
*
* @return array An array of JavaScript paths
*/
public function getJavaScripts()
{
return $this->getWidget()->getJavaScripts();
}
/**
* Returns a CSRF token for the set CSRF secret
*
* If you want to change the algorithm used to compute the token, you
* can override this method.
*
* @param string $secret The secret string to use (null to use the current secret)
*
* @return string A token string
*/
protected function getCsrfToken()
{
return md5($this->csrfSecret.session_id().get_class($this));
}
/**
* @return true if this form is CSRF protected
*/
public function isCsrfProtected()
{
return $this->has($this->getCsrfFieldName());
}
/**
* Enables CSRF protection for this form.
*/
public function enableCsrfProtection()
{
if (!$this->isCsrfProtected())
{
$field = new HiddenField($this->getCsrfFieldName(), array(
'property_path' => null,
));
$field->setData($this->getCsrfToken());
$this->add($field);
}
}
/**
* Disables CSRF protection for this form.
*/
public function disableCsrfProtection()
{
if ($this->isCsrfProtected())
{
$this->remove($this->getCsrfFieldName());
}
}
/**
* Sets the CSRF field name used in this form
*
* @param string $name The CSRF field name
*/
public function setCsrfFieldName($name)
{
$this->csrfFieldName = $name;
}
/**
* Returns the CSRF field name used in this form
*
* @return string The CSRF field name
*/
public function getCsrfFieldName()
{
return $this->csrfFieldName;
}
/**
* Sets the CSRF secret used in this form
*
* @param string $secret
*/
public function setCsrfSecret($secret)
{
$this->csrfSecret = $secret;
}
/**
* Returns the CSRF secret used in this form
*
* @return string
*/
public function getCsrfSecret()
{
return $this->csrfSecret;
}
/**
* Returns whether the CSRF token is valid
*
* @return boolean
*/
public function isCsrfTokenValid()
{
if (!$this->isCsrfProtected())
{
return true;
}
else
{
return $this->get($this->getCsrfFieldName())->getDisplayedData() === $this->getCsrfToken();
}
}
/**
* Enables CSRF protection for all new forms
*/
static public function enableDefaultCsrfProtection()
{
self::$defaultCsrfProtection = true;
}
/**
* Disables Csrf protection for all forms.
*/
static public function disableDefaultCsrfProtection()
{
self::$defaultCsrfProtection = false;
}
/**
* Sets the CSRF field name used in all new CSRF protected forms
*
* @param string $name The CSRF field name
*/
static public function setDefaultCsrfFieldName($name)
{
self::$defaultCsrfFieldName = $name;
}
/**
* Returns the default CSRF field name
*
* @return string The CSRF field name
*/
static public function getDefaultCsrfFieldName()
{
return self::$defaultCsrfFieldName;
}
/**
* Sets the CSRF secret used in all new CSRF protected forms
*
* @param string $secret
*/
static public function setDefaultCsrfSecret($secret)
{
self::$defaultCsrfSecret = $secret;
}
/**
* Returns the default CSRF secret
*
* @return string
*/
static public function getDefaultCsrfSecret()
{
return self::$defaultCsrfSecret;
}
/**
* Renders the form tag.
*
* This method only renders the opening form tag.
* You need to close it after the form rendering.
*
* This method takes into account the multipart widgets.
*
* @param string $url The URL for the action
* @param array $attributes An array of HTML attributes
*
* @return string An HTML representation of the opening form tag
*/
public function renderFormTag($url, array $attributes = array())
{
return sprintf('<form%s>', $this->generator->attributes(array_merge(array(
'action' => $url,
'method' => isset($attributes['method']) ? strtolower($attributes['method']) : 'post',
'enctype' => $this->isMultipart() ? 'multipart/form-data' : null,
), $attributes)));
}
/**
* Returns whether the maximum POST size was reached in this request.
*
* @return boolean
*/
public function isPostMaxSizeReached()
{
if (isset($_SERVER['CONTENT_LENGTH']))
{
$length = (int) $_SERVER['CONTENT_LENGTH'];
$max = trim(ini_get('post_max_size'));
switch (strtolower(substr($max, -1)))
{
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$max *= 1024;
case 'm':
$max *= 1024;
case 'k':
$max *= 1024;
}
return $length > $max;
}
else
{
return false;
}
}
/**
* Merges two arrays without reindexing numeric keys.
*
* @param array $array1 An array to merge
* @param array $array2 An array to merge
*
* @return array The merged array
*/
static protected function deepArrayUnion($array1, $array2)
{
foreach ($array2 as $key => $value)
{
if (is_array($value) && isset($array1[$key]) && is_array($array1[$key]))
{
$array1[$key] = self::deepArrayUnion($array1[$key], $value);
}
else
{
$array1[$key] = $value;
}
if (self::$defaultTranslator !== null) {
$this->setTranslator(self::$defaultTranslator);
}
}
return $array1;
}
/**
* Fixes a malformed PHP $_FILES array.
*
* PHP has a bug that the format of the $_FILES array differs, depending on
* whether the uploaded file fields had normal field names or array-like
* field names ("normal" vs. "parent[child]").
*
* This method fixes the array to look like the "normal" $_FILES array.
*
* @param array $data
* @return array
*/
static protected function fixPhpFilesArray(array $data)
{
$fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
$keys = array_keys($data);
sort($keys);
$files = $data;
if ($fileKeys == $keys && isset($data['name']) && is_array($data['name']))
/**
* Sets the charset used for rendering HTML
*
* This method overrides the internal HTML generator! If you want to use
* your own generator, use setGenerator() instead.
*
* @param string $charset
*/
public function setCharset($charset)
{
foreach ($fileKeys as $k)
{
unset($files[$k]);
}
foreach (array_keys($data['name']) as $key)
{
$files[$key] = self::fixPhpFilesArray(array(
'error' => $data['error'][$key],
'name' => $data['name'][$key],
'type' => $data['type'][$key],
'tmp_name' => $data['tmp_name'][$key],
'size' => $data['size'][$key],
));
}
$this->setGenerator(new HtmlGenerator($charset));
}
return $files;
}
/**
* Converts uploaded files to instances of clsas UploadedFile.
*
* @param array $files A (multi-dimensional) array of uploaded file information
* @return array A (multi-dimensional) array of UploadedFile instances
*/
static protected function convertFileInformation(array $files)
{
$fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
foreach ($files as $key => $data)
/**
* Sets the validation groups for this form.
*
* @param array|string $validationGroups
*/
public function setValidationGroups($validationGroups)
{
if (is_array($data))
{
$this->validationGroups = $validationGroups === null ? $validationGroups : (array) $validationGroups;
}
/**
* Returns the validation groups for this form.
*
* @return array
*/
public function getValidationGroups()
{
return $this->validationGroups;
}
/**
* Sets the default locale for newly created forms.
*
* @param string $defaultLocale
*/
static public function setDefaultLocale($defaultLocale)
{
self::$defaultLocale = $defaultLocale;
}
/**
* Returns the default locale for newly created forms.
*
* @return string
*/
static public function getDefaultLocale()
{
return self::$defaultLocale;
}
/**
* Sets the default translator for newly created forms.
*
* @param TranslatorInterface $defaultTranslator
*/
static public function setDefaultTranslator(TranslatorInterface $defaultTranslator)
{
self::$defaultTranslator = $defaultTranslator;
}
/**
* Returns the default translator for newly created forms.
*
* @return TranslatorInterface
*/
static public function getDefaultTranslator()
{
return self::$defaultTranslator;
}
/**
* Binds the form with values and files.
*
* This method is final because it is very easy to break a form when
* overriding this method and adding logic that depends on $taintedFiles.
* You should override doBind() instead where the uploaded files are
* already merged into the data array.
*
* @param array $taintedValues The form data of the $_POST array
* @param array $taintedFiles The form data of the $_FILES array
* @return boolean Whether the form is valid
*/
final public function bind($taintedValues, array $taintedFiles = null)
{
if ($taintedFiles === null) {
if ($this->isMultipart() && $this->getParent() === null) {
throw new \InvalidArgumentException('You must provide a files array for multipart forms');
}
$taintedFiles = array();
} else {
$taintedFiles = self::convertFileInformation(self::fixPhpFilesArray($taintedFiles));
}
$this->doBind(self::deepArrayUnion($taintedValues, $taintedFiles));
if ($this->getParent() === null) {
if ($violations = $this->validator->validate($this, $this->getValidationGroups())) {
foreach ($violations as $violation) {
$propertyPath = new PropertyPath($violation->getPropertyPath());
if ($propertyPath->getCurrent() == 'data') {
$type = self::DATA_ERROR;
$propertyPath->next(); // point at the first data element
} else {
$type = self::FIELD_ERROR;
}
$this->addError($violation->getMessage(), $propertyPath, $type);
}
}
}
}
/**
* Binds the form with the given data.
*
* @param array $taintedData The data to bind to the form
* @return boolean Whether the form is valid
*/
protected function doBind(array $taintedData)
{
parent::bind($taintedData);
}
/**
* Gets the stylesheet paths associated with the form.
*
* @return array An array of stylesheet paths
*/
public function getStylesheets()
{
return $this->getWidget()->getStylesheets();
}
/**
* Gets the JavaScript paths associated with the form.
*
* @return array An array of JavaScript paths
*/
public function getJavaScripts()
{
return $this->getWidget()->getJavaScripts();
}
/**
* Returns a CSRF token for the set CSRF secret
*
* If you want to change the algorithm used to compute the token, you
* can override this method.
*
* @param string $secret The secret string to use (null to use the current secret)
*
* @return string A token string
*/
protected function getCsrfToken()
{
return md5($this->csrfSecret.session_id().get_class($this));
}
/**
* @return true if this form is CSRF protected
*/
public function isCsrfProtected()
{
return $this->has($this->getCsrfFieldName());
}
/**
* Enables CSRF protection for this form.
*/
public function enableCsrfProtection()
{
if (!$this->isCsrfProtected()) {
$field = new HiddenField($this->getCsrfFieldName(), array(
'property_path' => null,
));
$field->setData($this->getCsrfToken());
$this->add($field);
}
}
/**
* Disables CSRF protection for this form.
*/
public function disableCsrfProtection()
{
if ($this->isCsrfProtected()) {
$this->remove($this->getCsrfFieldName());
}
}
/**
* Sets the CSRF field name used in this form
*
* @param string $name The CSRF field name
*/
public function setCsrfFieldName($name)
{
$this->csrfFieldName = $name;
}
/**
* Returns the CSRF field name used in this form
*
* @return string The CSRF field name
*/
public function getCsrfFieldName()
{
return $this->csrfFieldName;
}
/**
* Sets the CSRF secret used in this form
*
* @param string $secret
*/
public function setCsrfSecret($secret)
{
$this->csrfSecret = $secret;
}
/**
* Returns the CSRF secret used in this form
*
* @return string
*/
public function getCsrfSecret()
{
return $this->csrfSecret;
}
/**
* Returns whether the CSRF token is valid
*
* @return boolean
*/
public function isCsrfTokenValid()
{
if (!$this->isCsrfProtected()) {
return true;
} else {
return $this->get($this->getCsrfFieldName())->getDisplayedData() === $this->getCsrfToken();
}
}
/**
* Enables CSRF protection for all new forms
*/
static public function enableDefaultCsrfProtection()
{
self::$defaultCsrfProtection = true;
}
/**
* Disables Csrf protection for all forms.
*/
static public function disableDefaultCsrfProtection()
{
self::$defaultCsrfProtection = false;
}
/**
* Sets the CSRF field name used in all new CSRF protected forms
*
* @param string $name The CSRF field name
*/
static public function setDefaultCsrfFieldName($name)
{
self::$defaultCsrfFieldName = $name;
}
/**
* Returns the default CSRF field name
*
* @return string The CSRF field name
*/
static public function getDefaultCsrfFieldName()
{
return self::$defaultCsrfFieldName;
}
/**
* Sets the CSRF secret used in all new CSRF protected forms
*
* @param string $secret
*/
static public function setDefaultCsrfSecret($secret)
{
self::$defaultCsrfSecret = $secret;
}
/**
* Returns the default CSRF secret
*
* @return string
*/
static public function getDefaultCsrfSecret()
{
return self::$defaultCsrfSecret;
}
/**
* Renders the form tag.
*
* This method only renders the opening form tag.
* You need to close it after the form rendering.
*
* This method takes into account the multipart widgets.
*
* @param string $url The URL for the action
* @param array $attributes An array of HTML attributes
*
* @return string An HTML representation of the opening form tag
*/
public function renderFormTag($url, array $attributes = array())
{
return sprintf('<form%s>', $this->generator->attributes(array_merge(array(
'action' => $url,
'method' => isset($attributes['method']) ? strtolower($attributes['method']) : 'post',
'enctype' => $this->isMultipart() ? 'multipart/form-data' : null,
), $attributes)));
}
/**
* Returns whether the maximum POST size was reached in this request.
*
* @return boolean
*/
public function isPostMaxSizeReached()
{
if (isset($_SERVER['CONTENT_LENGTH'])) {
$length = (int) $_SERVER['CONTENT_LENGTH'];
$max = trim(ini_get('post_max_size'));
switch (strtolower(substr($max, -1))) {
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$max *= 1024;
case 'm':
$max *= 1024;
case 'k':
$max *= 1024;
}
return $length > $max;
} else {
return false;
}
}
/**
* Merges two arrays without reindexing numeric keys.
*
* @param array $array1 An array to merge
* @param array $array2 An array to merge
*
* @return array The merged array
*/
static protected function deepArrayUnion($array1, $array2)
{
foreach ($array2 as $key => $value) {
if (is_array($value) && isset($array1[$key]) && is_array($array1[$key])) {
$array1[$key] = self::deepArrayUnion($array1[$key], $value);
} else {
$array1[$key] = $value;
}
}
return $array1;
}
/**
* Fixes a malformed PHP $_FILES array.
*
* PHP has a bug that the format of the $_FILES array differs, depending on
* whether the uploaded file fields had normal field names or array-like
* field names ("normal" vs. "parent[child]").
*
* This method fixes the array to look like the "normal" $_FILES array.
*
* @param array $data
* @return array
*/
static protected function fixPhpFilesArray(array $data)
{
$fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
$keys = array_keys($data);
sort($keys);
if ($keys == $fileKeys)
{
$files[$key] = new UploadedFile($data['tmp_name'], $data['name'], $data['type'], $data['size'], $data['error']);
$files = $data;
if ($fileKeys == $keys && isset($data['name']) && is_array($data['name'])) {
foreach ($fileKeys as $k) {
unset($files[$k]);
}
foreach (array_keys($data['name']) as $key) {
$files[$key] = self::fixPhpFilesArray(array(
'error' => $data['error'][$key],
'name' => $data['name'][$key],
'type' => $data['type'][$key],
'tmp_name' => $data['tmp_name'][$key],
'size' => $data['size'][$key],
));
}
}
else
{
$files[$key] = self::convertFileInformation($data);
}
}
return $files;
}
return $files;
}
/**
* Converts uploaded files to instances of clsas UploadedFile.
*
* @param array $files A (multi-dimensional) array of uploaded file information
* @return array A (multi-dimensional) array of UploadedFile instances
*/
static protected function convertFileInformation(array $files)
{
$fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
foreach ($files as $key => $data) {
if (is_array($data)) {
$keys = array_keys($data);
sort($keys);
if ($keys == $fileKeys) {
$files[$key] = new UploadedFile($data['tmp_name'], $data['name'], $data['type'], $data['size'], $data['error']);
} else {
$files[$key] = self::convertFileInformation($data);
}
}
}
return $files;
}
}

View File

@ -19,21 +19,21 @@ use Symfony\Components\Form\Renderer\InputHiddenRenderer;
*/
class HiddenField extends InputField
{
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'type' => 'hidden',
), $attributes));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'type' => 'hidden',
), $attributes));
}
/**
* {@inheritDoc}
*/
public function isHidden()
{
return true;
}
/**
* {@inheritDoc}
*/
public function isHidden()
{
return true;
}
}

View File

@ -10,136 +10,128 @@ namespace Symfony\Components\Form;
*/
class HtmlGenerator implements HtmlGeneratorInterface
{
/**
* Whether to produce XHTML compliant code
* @var boolean
*/
protected static $xhtml = true;
/**
* Whether to produce XHTML compliant code
* @var boolean
*/
protected static $xhtml = true;
/**
* The charset used during generating
* @var string
*/
protected $charset;
/**
* The charset used during generating
* @var string
*/
protected $charset;
/**
* Sets the charset used for rendering
*
* @param string $charset
*/
public function __construct($charset = 'UTF-8')
{
$this->charset = $charset;
}
/**
* Sets the XHTML generation flag.
*
* @param bool $boolean true if renderers must be generated as XHTML, false otherwise
*/
static public function setXhtml($boolean)
{
self::$xhtml = (boolean) $boolean;
}
/**
* Returns whether to generate XHTML tags or not.
*
* @return bool true if renderers must be generated as XHTML, false otherwise
*/
static public function isXhtml()
{
return self::$xhtml;
}
/**
* {@inheritDoc}
*/
public function tag($tag, $attributes = array())
{
if (empty($tag))
/**
* Sets the charset used for rendering
*
* @param string $charset
*/
public function __construct($charset = 'UTF-8')
{
return '';
$this->charset = $charset;
}
return sprintf('<%s%s%s', $tag, $this->attributes($attributes), self::$xhtml ? ' />' : (strtolower($tag) == 'input' ? '>' : sprintf('></%s>', $tag)));
}
/**
* {@inheritDoc}
*/
public function contentTag($tag, $content = null, $attributes = array())
{
if (empty($tag))
/**
* Sets the XHTML generation flag.
*
* @param bool $boolean true if renderers must be generated as XHTML, false otherwise
*/
static public function setXhtml($boolean)
{
return '';
self::$xhtml = (boolean) $boolean;
}
return sprintf('<%s%s>%s</%s>', $tag, $this->attributes($attributes), $content, $tag);
}
/**
* {@inheritDoc}
*/
public function attribute($name, $value)
{
if (true === $value)
/**
* Returns whether to generate XHTML tags or not.
*
* @return bool true if renderers must be generated as XHTML, false otherwise
*/
static public function isXhtml()
{
return self::$xhtml ? sprintf('%s="%s"', $name, $this->escape($name)) : $this->escape($name);
return self::$xhtml;
}
else
/**
* {@inheritDoc}
*/
public function tag($tag, $attributes = array())
{
return sprintf('%s="%s"', $name, $this->escape($value));
if (empty($tag)) {
return '';
}
return sprintf('<%s%s%s', $tag, $this->attributes($attributes), self::$xhtml ? ' />' : (strtolower($tag) == 'input' ? '>' : sprintf('></%s>', $tag)));
}
}
/**
* {@inheritDoc}
*/
public function attributes(array $attributes)
{
return implode('', array_map(array($this, 'attributesCallback'), array_keys($attributes), array_values($attributes)));
}
/**
* Prepares an attribute key and value for HTML representation.
*
* It removes empty attributes, except for the value one.
*
* @param string $name The attribute name
* @param string $value The attribute value
*
* @return string The HTML representation of the HTML key attribute pair.
*/
private function attributesCallback($name, $value)
{
if (false === $value || null === $value || ('' === $value && 'value' != $name))
/**
* {@inheritDoc}
*/
public function contentTag($tag, $content = null, $attributes = array())
{
return '';
if (empty($tag)) {
return '';
}
return sprintf('<%s%s>%s</%s>', $tag, $this->attributes($attributes), $content, $tag);
}
else
/**
* {@inheritDoc}
*/
public function attribute($name, $value)
{
return ' '.$this->attribute($name, $value);
if (true === $value) {
return self::$xhtml ? sprintf('%s="%s"', $name, $this->escape($name)) : $this->escape($name);
} else {
return sprintf('%s="%s"', $name, $this->escape($value));
}
}
}
/**
* {@inheritDoc}
*/
public function escape($value)
{
return $this->fixDoubleEscape(htmlspecialchars((string) $value, ENT_QUOTES, $this->charset));
}
/**
* {@inheritDoc}
*/
public function attributes(array $attributes)
{
return implode('', array_map(array($this, 'attributesCallback'), array_keys($attributes), array_values($attributes)));
}
/**
* Fixes double escaped strings.
*
* @param string $escaped string to fix
*
* @return string A single escaped string
*/
protected function fixDoubleEscape($escaped)
{
return preg_replace('/&amp;([a-z]+|(#\d+)|(#x[\da-f]+));/i', '&$1;', $escaped);
}
/**
* Prepares an attribute key and value for HTML representation.
*
* It removes empty attributes, except for the value one.
*
* @param string $name The attribute name
* @param string $value The attribute value
*
* @return string The HTML representation of the HTML key attribute pair.
*/
private function attributesCallback($name, $value)
{
if (false === $value || null === $value || ('' === $value && 'value' != $name)) {
return '';
} else {
return ' '.$this->attribute($name, $value);
}
}
/**
* {@inheritDoc}
*/
public function escape($value)
{
return $this->fixDoubleEscape(htmlspecialchars((string) $value, ENT_QUOTES, $this->charset));
}
/**
* Fixes double escaped strings.
*
* @param string $escaped string to fix
*
* @return string A single escaped string
*/
protected function fixDoubleEscape($escaped)
{
return preg_replace('/&amp;([a-z]+|(#\d+)|(#x[\da-f]+));/i', '&$1;', $escaped);
}
}

View File

@ -9,55 +9,55 @@ namespace Symfony\Components\Form;
*/
interface HtmlGeneratorInterface
{
/**
* Escapes a value for safe output in HTML
*
* Double escaping of already-escaped sequences is avoided by this method.
*
* @param string $value The unescaped or partially escaped value
*
* @return string The fully escaped value
*/
public function escape($value);
/**
* Escapes a value for safe output in HTML
*
* Double escaping of already-escaped sequences is avoided by this method.
*
* @param string $value The unescaped or partially escaped value
*
* @return string The fully escaped value
*/
public function escape($value);
/**
* Generates the HTML code for a tag attribute
*
* @param string $name The attribute name
* @param string $value The attribute value
*
* @return string The HTML code of the attribute
*/
public function attribute($name, $value);
/**
* Generates the HTML code for a tag attribute
*
* @param string $name The attribute name
* @param string $value The attribute value
*
* @return string The HTML code of the attribute
*/
public function attribute($name, $value);
/**
* Generates the HTML code for multiple tag attributes
*
* @param array $attributes An array with attribute names as keys and
* attribute values as elements
*
* @return string The HTML code of the attribute list
*/
public function attributes(array $attributes);
/**
* Generates the HTML code for multiple tag attributes
*
* @param array $attributes An array with attribute names as keys and
* attribute values as elements
*
* @return string The HTML code of the attribute list
*/
public function attributes(array $attributes);
/**
* Generates the HTML code for a tag without content
*
* @param string $tag The name of the tag
* @param array $attributes The attributes for the tag
*
* @return string The HTML code for the tag
*/
public function tag($tag, $attributes = array());
/**
* Generates the HTML code for a tag without content
*
* @param string $tag The name of the tag
* @param array $attributes The attributes for the tag
*
* @return string The HTML code for the tag
*/
public function tag($tag, $attributes = array());
/**
* Generates the HTML code for a tag with content
*
* @param string $tag The name of the tag
* @param string $content The content of the tag
* @param array $attributes The attributes for the tag
*
* @return string The HTML code for the tag
*/
public function contentTag($tag, $content, $attributes = array());
/**
* Generates the HTML code for a tag with content
*
* @param string $tag The name of the tag
* @param string $content The content of the tag
* @param array $attributes The attributes for the tag
*
* @return string The HTML code for the tag
*/
public function contentTag($tag, $content, $attributes = array());
}

View File

@ -14,88 +14,77 @@ namespace Symfony\Components\Form;
*/
class HybridField extends FieldGroup
{
const FIELD = 0;
const GROUP = 1;
const FIELD = 0;
const GROUP = 1;
protected $mode = self::FIELD;
protected $mode = self::FIELD;
/**
* Sets the current mode of the field
*
* Note that you can't switch modes anymore once you have added children to
* this field.
*
* @param integer $mode One of the constants HybridField::FIELD and
* HybridField::GROUP.
*/
public function setFieldMode($mode)
{
if (count($this) > 0 && $mode === self::FIELD)
/**
* Sets the current mode of the field
*
* Note that you can't switch modes anymore once you have added children to
* this field.
*
* @param integer $mode One of the constants HybridField::FIELD and
* HybridField::GROUP.
*/
public function setFieldMode($mode)
{
throw new FormException('Switching to mode FIELD is not allowed after adding nested fields');
if (count($this) > 0 && $mode === self::FIELD) {
throw new FormException('Switching to mode FIELD is not allowed after adding nested fields');
}
$this->mode = $mode;
}
$this->mode = $mode;
}
/**
* {@inheritDoc}
*
* @throws FormException When the field is in mode HybridField::FIELD adding
* subfields is not allowed
*/
public function add(FieldInterface $field)
{
if ($this->mode === self::FIELD)
/**
* {@inheritDoc}
*
* @throws FormException When the field is in mode HybridField::FIELD adding
* subfields is not allowed
*/
public function add(FieldInterface $field)
{
throw new FormException('You cannot add nested fields while in mode FIELD');
if ($this->mode === self::FIELD) {
throw new FormException('You cannot add nested fields while in mode FIELD');
}
return parent::add($field);
}
return parent::add($field);
}
/**
* {@inheritDoc}
*/
public function getDisplayedData()
{
if ($this->mode === self::GROUP) {
return parent::getDisplayedData();
} else {
return Field::getDisplayedData();
}
}
/**
* {@inheritDoc}
*/
public function getDisplayedData()
{
if ($this->mode === self::GROUP)
/**
* {@inheritDoc}
*/
public function setData($data)
{
return parent::getDisplayedData();
if ($this->mode === self::GROUP) {
parent::setData($data);
} else {
Field::setData($data);
}
}
else
{
return Field::getDisplayedData();
}
}
/**
* {@inheritDoc}
*/
public function setData($data)
{
if ($this->mode === self::GROUP)
/**
* {@inheritDoc}
*/
public function bind($data)
{
parent::setData($data);
if ($this->mode === self::GROUP) {
parent::bind($data);
} else {
Field::bind($data);
}
}
else
{
Field::setData($data);
}
}
/**
* {@inheritDoc}
*/
public function bind($data)
{
if ($this->mode === self::GROUP)
{
parent::bind($data);
}
else
{
Field::bind($data);
}
}
}

View File

@ -9,16 +9,16 @@ namespace Symfony\Components\Form;
*/
abstract class InputField extends Field
{
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return $this->generator->tag('input', array_merge(array(
'id' => $this->getId(),
'name' => $this->getName(),
'value' => $this->getDisplayedData(),
'disabled' => $this->isDisabled(),
), $attributes));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return $this->generator->tag('input', array_merge(array(
'id' => $this->getId(),
'name' => $this->getName(),
'value' => $this->getDisplayedData(),
'disabled' => $this->isDisabled(),
), $attributes));
}
}

View File

@ -17,21 +17,21 @@ namespace Symfony\Components\Form;
*/
class IntegerField extends NumberField
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('precision', 0);
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('precision', 0);
parent::configure();
}
parent::configure();
}
/**
* {@inheritDoc}
*/
public function getData()
{
return (int)parent::getData();
}
/**
* {@inheritDoc}
*/
public function getData()
{
return (int)parent::getData();
}
}

View File

@ -6,18 +6,18 @@ use Symfony\Components\Form\FieldGroupInterface;
class RecursiveFieldsWithPropertyPathIterator extends \IteratorIterator implements \RecursiveIterator
{
public function __construct(FieldGroupInterface $group)
{
parent::__construct($group);
}
public function __construct(FieldGroupInterface $group)
{
parent::__construct($group);
}
public function getChildren()
{
return new self($this->current());
}
public function getChildren()
{
return new self($this->current());
}
public function hasChildren()
{
return $this->current() instanceof FieldGroupInterface && $this->current()->getPropertyPath() === null;
}
public function hasChildren()
{
return $this->current() instanceof FieldGroupInterface && $this->current()->getPropertyPath() === null;
}
}

View File

@ -9,10 +9,10 @@ namespace Symfony\Components\Form;
*/
interface Localizable
{
/**
* Sets the locale of the class.
*
* @param string $locale
*/
public function setLocale($locale);
/**
* Sets the locale of the class.
*
* @param string $locale
*/
public function setLocale($locale);
}

View File

@ -19,93 +19,83 @@ use Symfony\Components\Form\ValueTransformer\MoneyToLocalizedStringTransformer;
*/
class MoneyField extends NumberField
{
/**
* Stores patterns for different locales and cultures
*
* A pattern decides which currency symbol is displayed and where it is in
* relation to the number.
*
* @var array
*/
protected static $patterns = array();
/**
* Stores patterns for different locales and cultures
*
* A pattern decides which currency symbol is displayed and where it is in
* relation to the number.
*
* @var array
*/
protected static $patterns = array();
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('precision', 2);
$this->addOption('divisor', 1);
$this->addOption('currency');
parent::configure();
$this->setValueTransformer(new MoneyToLocalizedStringTransformer(array(
'precision' => $this->getOption('precision'),
'grouping' => $this->getOption('grouping'),
'divisor' => $this->getOption('divisor'),
)));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
$input = parent::render($attributes);
if ($this->getOption('currency'))
/**
* {@inheritDoc}
*/
protected function configure()
{
return str_replace('%widget%', $input, $this->getPattern($this->locale, $this->getOption('currency')));
}
else
{
return $input;
}
}
$this->addOption('precision', 2);
$this->addOption('divisor', 1);
$this->addOption('currency');
/**
* Returns the pattern for this locale
*
* The pattern contains the placeholder "%widget%" where the HTML tag should
* be inserted
*
* @param string $locale
*/
protected static function getPattern($locale, $currency)
{
if (!isset(self::$patterns[$locale]))
{
self::$patterns[$locale] = array();
parent::configure();
$this->setValueTransformer(new MoneyToLocalizedStringTransformer(array(
'precision' => $this->getOption('precision'),
'grouping' => $this->getOption('grouping'),
'divisor' => $this->getOption('divisor'),
)));
}
if (!isset(self::$patterns[$locale][$currency]))
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
$format = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
$pattern = $format->formatCurrency('123', $currency);
$input = parent::render($attributes);
// 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[,.]00[\s\xc2\xa0]*([^\s\xc2\xa0]*)$/', $pattern, $matches);
if (!empty($matches[1]))
{
self::$patterns[$locale] = $matches[1].' %widget%';
}
else if (!empty($matches[2]))
{
self::$patterns[$locale] = '%widget% '.$matches[2];
}
else
{
self::$patterns[$locale] = '%widget%';
}
if ($this->getOption('currency')) {
return str_replace('%widget%', $input, $this->getPattern($this->locale, $this->getOption('currency')));
} else {
return $input;
}
}
return self::$patterns[$locale];
}
/**
* Returns the pattern for this locale
*
* The pattern contains the placeholder "%widget%" where the HTML tag should
* be inserted
*
* @param string $locale
*/
protected static function getPattern($locale, $currency)
{
if (!isset(self::$patterns[$locale])) {
self::$patterns[$locale] = array();
}
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[,.]00[\s\xc2\xa0]*([^\s\xc2\xa0]*)$/', $pattern, $matches);
if (!empty($matches[1])) {
self::$patterns[$locale] = $matches[1].' %widget%';
} else if (!empty($matches[2])) {
self::$patterns[$locale] = '%widget% '.$matches[2];
} else {
self::$patterns[$locale] = '%widget%';
}
}
return self::$patterns[$locale];
}
}

View File

@ -20,28 +20,28 @@ use Symfony\Components\Form\ValueTransformer\NumberToLocalizedStringTransformer;
*/
class NumberField extends InputField
{
/**
* {@inheritDoc}
*/
protected function configure()
{
// default precision is locale specific (usually around 3)
$this->addOption('precision');
$this->addOption('grouping', false);
/**
* {@inheritDoc}
*/
protected function configure()
{
// default precision is locale specific (usually around 3)
$this->addOption('precision');
$this->addOption('grouping', false);
$this->setValueTransformer(new NumberToLocalizedStringTransformer(array(
'precision' => $this->getOption('precision'),
'grouping' => $this->getOption('grouping'),
)));
}
$this->setValueTransformer(new NumberToLocalizedStringTransformer(array(
'precision' => $this->getOption('precision'),
'grouping' => $this->getOption('grouping'),
)));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'type' => 'text',
), $attributes));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'type' => 'text',
), $attributes));
}
}

View File

@ -19,24 +19,24 @@ use Symfony\Components\Form\Renderer\InputPasswordRenderer;
*/
class PasswordField extends TextField
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this->addOption('always_empty', true);
}
$this->addOption('always_empty', true);
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'value' => $this->getOption('always_empty') && !$this->isBound() ? '' : $this->getDisplayedData(),
'type' => 'password',
), $attributes));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'value' => $this->getOption('always_empty') && !$this->isBound() ? '' : $this->getDisplayedData(),
'type' => 'password',
), $attributes));
}
}

View File

@ -19,28 +19,28 @@ use Symfony\Components\Form\ValueTransformer\PercentToLocalizedStringTransformer
*/
class PercentField extends NumberField
{
const FRACTIONAL = 'fractional';
const INTEGER = 'integer';
const FRACTIONAL = 'fractional';
const INTEGER = 'integer';
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('precision', 0);
$this->addOption('type', self::FRACTIONAL);
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('precision', 0);
$this->addOption('type', self::FRACTIONAL);
$this->setValueTransformer(new PercentToLocalizedStringTransformer(array(
'precision' => $this->getOption('precision'),
'type' => $this->getOption('type'),
)));
}
$this->setValueTransformer(new PercentToLocalizedStringTransformer(array(
'precision' => $this->getOption('precision'),
'type' => $this->getOption('type'),
)));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render($attributes).' %';
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render($attributes).' %';
}
}

View File

@ -11,152 +11,145 @@ use Symfony\Components\Form\Exception\InvalidPropertyPathException;
*/
class PropertyPath
{
/**
* The current index of the traversal
* @var integer
*/
protected $currentIndex = 0;
/**
* The current index of the traversal
* @var integer
*/
protected $currentIndex = 0;
/**
* The elements of the property path
* @var array
*/
protected $elements = array();
/**
* The elements of the property path
* @var array
*/
protected $elements = array();
/**
* Contains a boolean for each property in $elements denoting whether this
* element is a property. It is an index otherwise.
* @var array
*/
protected $isProperty = array();
/**
* Contains a boolean for each property in $elements denoting whether this
* element is a property. It is an index otherwise.
* @var array
*/
protected $isProperty = array();
/**
* String representation of the path
* @var string
*/
protected $string;
/**
* String representation of the path
* @var string
*/
protected $string;
/**
* Parses the given property path
*
* @param string $propertyPath
*/
public function __construct($propertyPath)
{
if (empty($propertyPath))
/**
* Parses the given property path
*
* @param string $propertyPath
*/
public function __construct($propertyPath)
{
throw new InvalidPropertyPathException('The property path must not be empty');
if (empty($propertyPath)) {
throw new InvalidPropertyPathException('The property path must not be empty');
}
$this->string = $propertyPath;
$position = 0;
$remaining = $propertyPath;
// first element is evaluated differently - no leading dot for properties
$pattern = '/^((\w+)|\[(\w+)\])(.*)/';
while (preg_match($pattern, $remaining, $matches)) {
if (!empty($matches[2])) {
$this->elements[] = $matches[2];
$this->isProperty[] = true;
} else {
$this->elements[] = $matches[3];
$this->isProperty[] = false;
}
$position += strlen($matches[1]);
$remaining = $matches[4];
$pattern = '/^(\.(\w+)|\[(\w+)\])(.*)/';
}
if (!empty($remaining)) {
throw new InvalidPropertyPathException(sprintf(
'Could not parse property path "%s". Unexpected token "%s" at position %d',
$propertyPath,
$remaining{0},
$position
));
}
}
$this->string = $propertyPath;
$position = 0;
$remaining = $propertyPath;
// first element is evaluated differently - no leading dot for properties
$pattern = '/^((\w+)|\[(\w+)\])(.*)/';
while (preg_match($pattern, $remaining, $matches))
/**
* Returns the string representation of the property path
*
* @return string
*/
public function __toString()
{
if (!empty($matches[2]))
{
$this->elements[] = $matches[2];
$this->isProperty[] = true;
}
else
{
$this->elements[] = $matches[3];
$this->isProperty[] = false;
}
$position += strlen($matches[1]);
$remaining = $matches[4];
$pattern = '/^(\.(\w+)|\[(\w+)\])(.*)/';
return $this->string;
}
if (!empty($remaining))
/**
* Returns the current element of the path
*
* @return string
*/
public function getCurrent()
{
throw new InvalidPropertyPathException(sprintf(
'Could not parse property path "%s". Unexpected token "%s" at position %d',
$propertyPath,
$remaining{0},
$position
));
}
}
/**
* Returns the string representation of the property path
*
* @return string
*/
public function __toString()
{
return $this->string;
}
/**
* Returns the current element of the path
*
* @return string
*/
public function getCurrent()
{
return $this->elements[$this->currentIndex];
}
/**
* Returns whether the current element is a property
*
* @return boolean
*/
public function isProperty()
{
return $this->isProperty[$this->currentIndex];
}
/**
* Returns whether the currente element is an array index
*
* @return boolean
*/
public function isIndex()
{
return !$this->isProperty();
}
/**
* Returns whether there is a next element in the path
*
* @return boolean
*/
public function hasNext()
{
return isset($this->elements[$this->currentIndex + 1]);
}
/**
* Sets the internal cursor to the next element in the path
*
* Use hasNext() to verify whether there is a next element before calling this
* method, otherwise an exception will be thrown.
*
* @throws OutOfBoundsException If there is no next element
*/
public function next()
{
if (!$this->hasNext())
{
throw new \OutOfBoundsException('There is no next element in the path');
return $this->elements[$this->currentIndex];
}
++$this->currentIndex;
}
/**
* Returns whether the current element is a property
*
* @return boolean
*/
public function isProperty()
{
return $this->isProperty[$this->currentIndex];
}
/**
* Sets the internal cursor to the first element in the path
*/
public function rewind()
{
$this->currentIndex = 0;
}
/**
* Returns whether the currente element is an array index
*
* @return boolean
*/
public function isIndex()
{
return !$this->isProperty();
}
/**
* Returns whether there is a next element in the path
*
* @return boolean
*/
public function hasNext()
{
return isset($this->elements[$this->currentIndex + 1]);
}
/**
* Sets the internal cursor to the next element in the path
*
* Use hasNext() to verify whether there is a next element before calling this
* method, otherwise an exception will be thrown.
*
* @throws OutOfBoundsException If there is no next element
*/
public function next()
{
if (!$this->hasNext()) {
throw new \OutOfBoundsException('There is no next element in the path');
}
++$this->currentIndex;
}
/**
* Sets the internal cursor to the first element in the path
*/
public function rewind()
{
$this->currentIndex = 0;
}
}

View File

@ -20,14 +20,14 @@ use Symfony\Components\Form\ValueTransformer\BooleanToStringTransformer;
*/
class RadioField extends ToggleField
{
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'type' => 'radio',
'name' => $this->getParent() ? $this->getParent()->getName() : $this->getName(),
), $attributes));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'type' => 'radio',
'name' => $this->getParent() ? $this->getParent()->getName() : $this->getName(),
), $attributes));
}
}

View File

@ -13,79 +13,77 @@ use Symfony\Components\Form\Configurable;
*/
abstract class Renderer extends Configurable implements RendererInterface
{
/**
* The generator used for rendering the HTML
* @var HtmlGeneratorInterface
*/
protected $generator;
/**
* The generator used for rendering the HTML
* @var HtmlGeneratorInterface
*/
protected $generator;
/**
* Gets the stylesheet paths associated with the renderer.
*
* The array keys are files and values are the media names (separated by a ,):
*
* array('/path/to/file.css' => 'all', '/another/file.css' => 'screen,print')
*
* @return array An array of stylesheet paths
*/
public function getStylesheets()
{
return array();
}
/**
* Gets the JavaScript paths associated with the renderer.
*
* @return array An array of JavaScript paths
*/
public function getJavaScripts()
{
return array();
}
/**
* {@inheritDoc}
*/
public function renderErrors(FieldInterface $field)
{
$html = '';
if ($field->hasErrors())
/**
* Gets the stylesheet paths associated with the renderer.
*
* The array keys are files and values are the media names (separated by a ,):
*
* array('/path/to/file.css' => 'all', '/another/file.css' => 'screen,print')
*
* @return array An array of stylesheet paths
*/
public function getStylesheets()
{
$html .= "<ul>\n";
foreach ($field->getErrors() as $error)
{
$html .= "<li>" . $error . "</li>\n";
}
$html .= "</ul>\n";
return array();
}
return $html;
}
/**
* Gets the JavaScript paths associated with the renderer.
*
* @return array An array of JavaScript paths
*/
public function getJavaScripts()
{
return array();
}
/**
* {@inheritDoc}
*/
public function setTranslator(TranslatorInterface $translator)
{
// TODO
}
/**
* {@inheritDoc}
*/
public function renderErrors(FieldInterface $field)
{
$html = '';
/**
* {@inheritDoc}
*/
public function setLocale($locale)
{
// TODO
}
if ($field->hasErrors()) {
$html .= "<ul>\n";
/**
* {@inheritDoc}
*/
public function setGenerator(HtmlGeneratorInterface $generator)
{
$this->generator = $generator;
}
foreach ($field->getErrors() as $error) {
$html .= "<li>" . $error . "</li>\n";
}
$html .= "</ul>\n";
}
return $html;
}
/**
* {@inheritDoc}
*/
public function setTranslator(TranslatorInterface $translator)
{
// TODO
}
/**
* {@inheritDoc}
*/
public function setLocale($locale)
{
// TODO
}
/**
* {@inheritDoc}
*/
public function setGenerator(HtmlGeneratorInterface $generator)
{
$this->generator = $generator;
}
}

View File

@ -14,32 +14,32 @@ use Symfony\Components\Form\Translatable;
*/
interface RendererInterface extends Localizable, Translatable
{
/**
* Sets the generator used for rendering the HTML
*
* @param HtmlGeneratorInterface $generator
*/
public function setGenerator(HtmlGeneratorInterface $generator);
/**
* Sets the generator used for rendering the HTML
*
* @param HtmlGeneratorInterface $generator
*/
public function setGenerator(HtmlGeneratorInterface $generator);
/**
* Returns the textual representation of the given field.
*
* @param FieldInterface $field The form field
* @param array $attributes The attributes to include in the
* rendered output
* @return string The rendered output
* @throws InvalidArgumentException If the $field is not instance of the
* expected class
*/
public function render(FieldInterface $field, array $attributes = array());
/**
* Returns the textual representation of the given field.
*
* @param FieldInterface $field The form field
* @param array $attributes The attributes to include in the
* rendered output
* @return string The rendered output
* @throws InvalidArgumentException If the $field is not instance of the
* expected class
*/
public function render(FieldInterface $field, array $attributes = array());
/**
* Returns the textual representation of the errors of the given field.
*
* @param FieldInterface $field The form field
* @return string The rendered output
* @throws InvalidArgumentException If the $field is not instance of the
* expected class
*/
public function renderErrors(FieldInterface $field);
/**
* Returns the textual representation of the errors of the given field.
*
* @param FieldInterface $field The form field
* @return string The rendered output
* @throws InvalidArgumentException If the $field is not instance of the
* expected class
*/
public function renderErrors(FieldInterface $field);
}

View File

@ -20,36 +20,34 @@ use Symfony\Components\Form\Field\ChoiceField;
*/
class TableRenderer extends Renderer
{
/**
* {@inheritDoc}
*/
public function render(FieldInterface $group, array $attributes = array())
{
$html = "<table>\n";
foreach ($group as $field)
/**
* {@inheritDoc}
*/
public function render(FieldInterface $group, array $attributes = array())
{
$label = self::humanize($field->getKey());
$html = "<table>\n";
$html .= "<tr>\n";
$html .= "<td><label for=\"{$field->getId()}\">$label</label></td>\n";
$html .= "<td>\n";
if ($field->hasErrors())
{
$html .= $field->renderErrors()."\n";
}
$html .= $field->render()."\n";
$html .= "</td>";
$html .= "</tr>\n";
foreach ($group as $field) {
$label = self::humanize($field->getKey());
$html .= "<tr>\n";
$html .= "<td><label for=\"{$field->getId()}\">$label</label></td>\n";
$html .= "<td>\n";
if ($field->hasErrors()) {
$html .= $field->renderErrors()."\n";
}
$html .= $field->render()."\n";
$html .= "</td>";
$html .= "</tr>\n";
}
$html .= "</table>\n";
return $html;
}
$html .= "</table>\n";
return $html;
}
protected static function humanize($text)
{
return ucfirst(strtolower(str_replace('_', ' ', $text)));
}
protected static function humanize($text)
{
return ucfirst(strtolower(str_replace('_', ' ', $text)));
}
}

View File

@ -17,72 +17,71 @@ namespace Symfony\Components\Form;
*/
class RepeatedField extends FieldGroup
{
/**
* The prototype for the inner fields
* @var FieldInterface
*/
protected $prototype;
/**
* The prototype for the inner fields
* @var FieldInterface
*/
protected $prototype;
/**
* Repeats the given field twice to verify the user's input
*
* @param FieldInterface $innerField
*/
public function __construct(FieldInterface $innerField, array $options = array())
{
$this->prototype = $innerField;
parent::__construct($innerField->getKey(), $options);
}
/**
* {@inheritDoc}
*/
protected function configure()
{
$field = clone $this->prototype;
$field->setKey('first');
$field->setPropertyPath('first');
$this->add($field);
$field = clone $this->prototype;
$field->setKey('second');
$field->setPropertyPath('second');
$this->add($field);
}
/**
* Returns whether both entered values are equal
*
* @return bool
*/
public function isFirstEqualToSecond()
{
return $this->get('first')->getData() === $this->get('second')->getData();
}
/**
* Sets the values of both fields to this value
*
* @param mixed $data
*/
public function setData($data)
{
parent::setData(array('first' => $data, 'second' => $data));
}
/**
* Return only value of first password field.
*
* @return string The password.
*/
public function getData()
{
if ($this->isBound() && $this->isFirstEqualToSecond())
/**
* Repeats the given field twice to verify the user's input
*
* @param FieldInterface $innerField
*/
public function __construct(FieldInterface $innerField, array $options = array())
{
return $this->get('first')->getData();
$this->prototype = $innerField;
parent::__construct($innerField->getKey(), $options);
}
return null;
}
/**
* {@inheritDoc}
*/
protected function configure()
{
$field = clone $this->prototype;
$field->setKey('first');
$field->setPropertyPath('first');
$this->add($field);
$field = clone $this->prototype;
$field->setKey('second');
$field->setPropertyPath('second');
$this->add($field);
}
/**
* Returns whether both entered values are equal
*
* @return bool
*/
public function isFirstEqualToSecond()
{
return $this->get('first')->getData() === $this->get('second')->getData();
}
/**
* Sets the values of both fields to this value
*
* @param mixed $data
*/
public function setData($data)
{
parent::setData(array('first' => $data, 'second' => $data));
}
/**
* Return only value of first password field.
*
* @return string The password.
*/
public function getData()
{
if ($this->isBound() && $this->isFirstEqualToSecond()) {
return $this->get('first')->getData();
}
return null;
}
}

View File

@ -19,24 +19,24 @@ use Symfony\Components\Form\Renderer\InputTextRenderer;
*/
class TextField extends InputField
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this->addOption('max_length');
}
$this->addOption('max_length');
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'type' => 'text',
'maxlength' => $this->getOption('max_length'),
), $attributes));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
return parent::render(array_merge(array(
'type' => 'text',
'maxlength' => $this->getOption('max_length'),
), $attributes));
}
}

View File

@ -19,18 +19,18 @@ use Symfony\Components\Form\Renderer\TextareaRenderer;
*/
class TextareaField extends Field
{
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
$content = $this->generator->escape($this->getDisplayedData());
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
$content = $this->generator->escape($this->getDisplayedData());
return $this->generator->contentTag('textarea', $content, array_merge(array(
'id' => $this->getId(),
'name' => $this->getName(),
'rows' => 30,
'cols' => 4,
), $attributes));
}
return $this->generator->contentTag('textarea', $content, array_merge(array(
'id' => $this->getId(),
'name' => $this->getName(),
'rows' => 30,
'cols' => 4,
), $attributes));
}
}

View File

@ -11,147 +11,134 @@ use Symfony\Components\Form\ValueTransformer\ValueTransformerChain;
class TimeField extends FieldGroup
{
const INPUT = 'input';
const CHOICE = 'choice';
const INPUT = 'input';
const CHOICE = 'choice';
const DATETIME = 'datetime';
const STRING = 'string';
const TIMESTAMP = 'timestamp';
const RAW = 'raw';
const DATETIME = 'datetime';
const STRING = 'string';
const TIMESTAMP = 'timestamp';
const RAW = 'raw';
protected static $widgets = array(
self::INPUT,
self::CHOICE,
);
protected static $widgets = array(
self::INPUT,
self::CHOICE,
);
protected static $types = array(
self::DATETIME,
self::STRING,
self::TIMESTAMP,
self::RAW,
);
protected static $types = array(
self::DATETIME,
self::STRING,
self::TIMESTAMP,
self::RAW,
);
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('hours', range(0, 23));
$this->addOption('minutes', range(0, 59));
$this->addOption('seconds', range(0, 59));
$this->addOption('widget', self::CHOICE, self::$widgets);
$this->addOption('type', self::DATETIME, self::$types);
$this->addOption('data_timezone', 'UTC');
$this->addOption('user_timezone', 'UTC');
$this->addOption('with_seconds', false);
if ($this->getOption('widget') == self::INPUT)
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->add(new TextField('hour', array('max_length' => 2)));
$this->add(new TextField('minute', array('max_length' => 2)));
$this->addOption('hours', range(0, 23));
$this->addOption('minutes', range(0, 59));
$this->addOption('seconds', range(0, 59));
$this->addOption('widget', self::CHOICE, self::$widgets);
$this->addOption('type', self::DATETIME, self::$types);
$this->addOption('data_timezone', 'UTC');
$this->addOption('user_timezone', 'UTC');
$this->addOption('with_seconds', false);
if ($this->getOption('with_seconds'))
{
$this->add(new TextField('second', array('max_length' => 2)));
}
}
else
{
$this->add(new ChoiceField('hour', array(
'choices' => $this->generatePaddedChoices($this->getOption('hours'), 2),
)));
$this->add(new ChoiceField('minute', array(
'choices' => $this->generatePaddedChoices($this->getOption('minutes'), 2),
)));
if ($this->getOption('widget') == self::INPUT) {
$this->add(new TextField('hour', array('max_length' => 2)));
$this->add(new TextField('minute', array('max_length' => 2)));
if ($this->getOption('with_seconds'))
{
$this->add(new ChoiceField('second', array(
'choices' => $this->generatePaddedChoices($this->getOption('seconds'), 2),
)));
}
if ($this->getOption('with_seconds')) {
$this->add(new TextField('second', array('max_length' => 2)));
}
} else {
$this->add(new ChoiceField('hour', array(
'choices' => $this->generatePaddedChoices($this->getOption('hours'), 2),
)));
$this->add(new ChoiceField('minute', array(
'choices' => $this->generatePaddedChoices($this->getOption('minutes'), 2),
)));
if ($this->getOption('with_seconds')) {
$this->add(new ChoiceField('second', array(
'choices' => $this->generatePaddedChoices($this->getOption('seconds'), 2),
)));
}
}
$transformers = array();
if ($this->getOption('type') == self::STRING) {
$transformers[] = new StringToDateTimeTransformer(array(
'format' => 'H:i:s',
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
));
} else if ($this->getOption('type') == self::TIMESTAMP) {
$transformers[] = new TimestampToDateTimeTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
));
} else if ($this->getOption('type') === self::RAW) {
$transformers[] = new ReversedTransformer(new DateTimeToArrayTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
'fields' => array('hour', 'minute', 'second'),
)));
}
$transformers[] = new DateTimeToArrayTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('user_timezone'),
// if the field is rendered as choice field, the values should be trimmed
// of trailing zeros to render the selected choices correctly
'pad' => $this->getOption('widget') == self::INPUT,
));
$this->setValueTransformer(new ValueTransformerChain($transformers));
}
$transformers = array();
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
if ($this->getOption('widget') == self::INPUT) {
$attributes = array_merge(array(
'size' => '1',
), $attributes);
}
if ($this->getOption('type') == self::STRING)
{
$transformers[] = new StringToDateTimeTransformer(array(
'format' => 'H:i:s',
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
));
}
else if ($this->getOption('type') == self::TIMESTAMP)
{
$transformers[] = new TimestampToDateTimeTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
));
}
else if ($this->getOption('type') === self::RAW)
{
$transformers[] = new ReversedTransformer(new DateTimeToArrayTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
'fields' => array('hour', 'minute', 'second'),
)));
$html = $this->get('hour')->render($attributes);
$html .= ':' . $this->get('minute')->render($attributes);
if ($this->getOption('with_seconds')) {
$html .= ':' . $this->get('second')->render($attributes);
}
return $html;
}
$transformers[] = new DateTimeToArrayTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('user_timezone'),
// if the field is rendered as choice field, the values should be trimmed
// of trailing zeros to render the selected choices correctly
'pad' => $this->getOption('widget') == self::INPUT,
));
$this->setValueTransformer(new ValueTransformerChain($transformers));
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
if ($this->getOption('widget') == self::INPUT)
/**
* Generates an array of choices for the given values
*
* If the values are shorter than $padLength characters, they are padded with
* zeros on the left side.
*
* @param array $values The available choices
* @param integer $padLength The length to pad the choices
* @return array An array with the input values as keys and the
* padded values as values
*/
protected function generatePaddedChoices(array $values, $padLength)
{
$attributes = array_merge(array(
'size' => '1',
), $attributes);
$choices = array();
foreach ($values as $value) {
$choices[$value] = str_pad($value, $padLength, '0', STR_PAD_LEFT);
}
return $choices;
}
$html = $this->get('hour')->render($attributes);
$html .= ':' . $this->get('minute')->render($attributes);
if ($this->getOption('with_seconds'))
{
$html .= ':' . $this->get('second')->render($attributes);
}
return $html;
}
/**
* Generates an array of choices for the given values
*
* If the values are shorter than $padLength characters, they are padded with
* zeros on the left side.
*
* @param array $values The available choices
* @param integer $padLength The length to pad the choices
* @return array An array with the input values as keys and the
* padded values as values
*/
protected function generatePaddedChoices(array $values, $padLength)
{
$choices = array();
foreach ($values as $value)
{
$choices[$value] = str_pad($value, $padLength, '0', STR_PAD_LEFT);
}
return $choices;
}
}

View File

@ -4,82 +4,73 @@ namespace Symfony\Components\Form;
class TimezoneField extends ChoiceField
{
/**
* Stores the available timezone choices
* @var array
*/
protected static $timezones = array();
/**
* Stores the available timezone choices
* @var array
*/
protected static $timezones = array();
/**
* {@inheritDoc}
*/
public function configure()
{
$this->addOption('choices', self::getTimezoneChoices());
parent::configure();
}
/**
* Preselects the server timezone if the field is empty and required
*
* {@inheritDoc}
*/
public function getDisplayedData()
{
$data = parent::getDisplayedData();
if ($data == null && $this->isRequired())
/**
* {@inheritDoc}
*/
public function configure()
{
$data = date_default_timezone_get();
$this->addOption('choices', self::getTimezoneChoices());
parent::configure();
}
return $data;
}
/**
* Returns the timezone choices
*
* The choices are generated from the ICU function
* \DateTimeZone::listIdentifiers(). They are cached during a single request,
* so multiple timezone fields on the same page don't lead to unnecessary
* overhead.
*
* @return array The timezone choices
*/
protected static function getTimezoneChoices()
{
if (count(self::$timezones) == 0)
/**
* Preselects the server timezone if the field is empty and required
*
* {@inheritDoc}
*/
public function getDisplayedData()
{
foreach (\DateTimeZone::listIdentifiers() as $timezone)
{
$parts = explode('/', $timezone);
$data = parent::getDisplayedData();
if (count($parts) > 2)
{
$region = $parts[0];
$name = $parts[1].' - '.$parts[2];
}
else if (count($parts) > 1)
{
$region = $parts[0];
$name = $parts[1];
}
else
{
$region = 'Other';
$name = $parts[0];
if ($data == null && $this->isRequired()) {
$data = date_default_timezone_get();
}
if (!isset(self::$timezones[$region]))
{
self::$timezones[$region] = array();
}
self::$timezones[$region][$timezone] = str_replace('_', ' ', $name);
}
return $data;
}
return self::$timezones;
}
/**
* Returns the timezone choices
*
* The choices are generated from the ICU function
* \DateTimeZone::listIdentifiers(). They are cached during a single request,
* so multiple timezone fields on the same page don't lead to unnecessary
* overhead.
*
* @return array The timezone choices
*/
protected static function getTimezoneChoices()
{
if (count(self::$timezones) == 0) {
foreach (\DateTimeZone::listIdentifiers() as $timezone) {
$parts = explode('/', $timezone);
if (count($parts) > 2) {
$region = $parts[0];
$name = $parts[1].' - '.$parts[2];
} else if (count($parts) > 1) {
$region = $parts[0];
$name = $parts[1];
} else {
$region = 'Other';
$name = $parts[0];
}
if (!isset(self::$timezones[$region])) {
self::$timezones[$region] = array();
}
self::$timezones[$region][$timezone] = str_replace('_', ' ', $name);
}
}
return self::$timezones;
}
}

View File

@ -20,40 +20,38 @@ use Symfony\Components\Form\ValueTransformer\BooleanToStringTransformer;
*/
abstract class ToggleField extends InputField
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('value');
$this->addOption('label');
$this->addOption('translate_label', false);
$this->setValueTransformer(new BooleanToStringTransformer());
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
$html = parent::render(array_merge(array(
'value' => $this->getOption('value'),
'checked' => ((string)$this->getDisplayedData() !== '' && $this->getDisplayedData() !== 0),
), $attributes));
if ($label = $this->getOption('label'))
/**
* {@inheritDoc}
*/
protected function configure()
{
if ($this->getOption('translate_label'))
{
$label = $this->translate($label);
}
$this->addOption('value');
$this->addOption('label');
$this->addOption('translate_label', false);
$html .= ' '.$this->generator->contentTag('label', $label, array(
'for' => $this->getId(),
));
$this->setValueTransformer(new BooleanToStringTransformer());
}
return $html;
}
/**
* {@inheritDoc}
*/
public function render(array $attributes = array())
{
$html = parent::render(array_merge(array(
'value' => $this->getOption('value'),
'checked' => ((string)$this->getDisplayedData() !== '' && $this->getDisplayedData() !== 0),
), $attributes));
if ($label = $this->getOption('label')) {
if ($this->getOption('translate_label')) {
$label = $this->translate($label);
}
$html .= ' '.$this->generator->contentTag('label', $label, array(
'for' => $this->getId(),
));
}
return $html;
}
}

View File

@ -11,10 +11,10 @@ use Symfony\Components\I18N\TranslatorInterface;
*/
interface Translatable
{
/**
* Sets the translator unit of the class.
*
* @param TranslatorInterface $translator
*/
public function setTranslator(TranslatorInterface $translator);
/**
* Sets the translator unit of the class.
*
* @param TranslatorInterface $translator
*/
public function setTranslator(TranslatorInterface $translator);
}

View File

@ -4,41 +4,40 @@ namespace Symfony\Components\Form\ValueTransformer;
abstract class BaseDateTimeTransformer extends BaseValueTransformer
{
const NONE = 'none';
const FULL = 'full';
const LONG = 'long';
const MEDIUM = 'medium';
const SHORT = 'short';
const NONE = 'none';
const FULL = 'full';
const LONG = 'long';
const MEDIUM = 'medium';
const SHORT = 'short';
protected static $formats = array(
self::NONE,
self::FULL,
self::LONG,
self::MEDIUM,
self::SHORT,
);
protected static $formats = array(
self::NONE,
self::FULL,
self::LONG,
self::MEDIUM,
self::SHORT,
);
/**
* Returns the appropriate IntLDateFormatter constant for the given format
*
* @param string $format One of "short", "medium", "long" and "full"
* @return integer
*/
protected function getIntlFormatConstant($format)
{
switch ($format)
/**
* Returns the appropriate IntLDateFormatter constant for the given format
*
* @param string $format One of "short", "medium", "long" and "full"
* @return integer
*/
protected function getIntlFormatConstant($format)
{
case self::FULL:
return \IntlDateFormatter::FULL;
case self::LONG:
return \IntlDateFormatter::LONG;
case self::SHORT:
return \IntldateFormatter::SHORT;
case self::MEDIUM:
return \IntlDateFormatter::MEDIUM;
case self::NONE:
default:
return \IntlDateFormatter::NONE;
switch ($format) {
case self::FULL:
return \IntlDateFormatter::FULL;
case self::LONG:
return \IntlDateFormatter::LONG;
case self::SHORT:
return \IntldateFormatter::SHORT;
case self::MEDIUM:
return \IntlDateFormatter::MEDIUM;
case self::NONE:
default:
return \IntlDateFormatter::NONE;
}
}
}
}

View File

@ -11,32 +11,32 @@ use Symfony\Components\Form\Configurable;
*/
abstract class BaseValueTransformer extends Configurable implements ValueTransformerInterface
{
/**
* The locale of this transformer as accepted by the class Locale
* @var string
*/
protected $locale;
/**
* The locale of this transformer as accepted by the class Locale
* @var string
*/
protected $locale;
/**
* Constructor.
*
* @param array $options An array of options
*
* @throws \InvalidArgumentException when a option is not supported
* @throws \RuntimeException when a required option is not given
*/
public function __construct(array $options = array())
{
$this->locale = \Locale::getDefault();
/**
* Constructor.
*
* @param array $options An array of options
*
* @throws \InvalidArgumentException when a option is not supported
* @throws \RuntimeException when a required option is not given
*/
public function __construct(array $options = array())
{
$this->locale = \Locale::getDefault();
parent::__construct($options);
}
parent::__construct($options);
}
/**
* {@inheritDoc}
*/
public function setLocale($locale)
{
$this->locale = $locale;
}
/**
* {@inheritDoc}
*/
public function setLocale($locale)
{
$this->locale = $locale;
}
}

View File

@ -10,36 +10,34 @@ namespace Symfony\Components\Form\ValueTransformer;
*/
class BooleanToStringTransformer extends BaseValueTransformer
{
/**
* Transforms a boolean into a string.
*
* @param boolean $value Boolean value.
* @return string String value.
*/
public function transform($value)
{
if (!is_bool($value))
/**
* Transforms a boolean into a string.
*
* @param boolean $value Boolean value.
* @return string String value.
*/
public function transform($value)
{
throw new \InvalidArgumentException(sprintf('Expected argument of type boolean but got %s.', gettype($value)));
if (!is_bool($value)) {
throw new \InvalidArgumentException(sprintf('Expected argument of type boolean but got %s.', gettype($value)));
}
return true === $value ? '1' : '';
}
return true === $value ? '1' : '';
}
/**
* Transforms a string into a boolean.
*
* @param string $value String value.
* @return boolean Boolean value.
*/
public function reverseTransform($value)
{
if (!is_string($value))
/**
* Transforms a string into a boolean.
*
* @param string $value String value.
* @return boolean Boolean value.
*/
public function reverseTransform($value)
{
throw new \InvalidArgumentException(sprintf('Expected argument of type string but got %s.', gettype($value)));
}
if (!is_string($value)) {
throw new \InvalidArgumentException(sprintf('Expected argument of type string but got %s.', gettype($value)));
}
return $value !== '';
}
return $value !== '';
}
}

View File

@ -19,92 +19,86 @@ use \Symfony\Components\Form\ValueTransformer\ValueTransformerException;
*/
class DateTimeToArrayTransformer extends BaseDateTimeTransformer
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this->addOption('input_timezone', 'UTC');
$this->addOption('output_timezone', 'UTC');
$this->addOption('pad', false);
$this->addOption('fields', array('year', 'month', 'day', 'hour', 'minute', 'second'));
}
/**
* Transforms a normalized date into a localized date string/array.
*
* @param DateTime $dateTime Normalized date.
* @return string|array Localized date array.
*/
public function transform($dateTime)
{
if (!$dateTime instanceof \DateTime)
/**
* {@inheritDoc}
*/
protected function configure()
{
throw new \InvalidArgumentException('Expected value of type \DateTime');
parent::configure();
$this->addOption('input_timezone', 'UTC');
$this->addOption('output_timezone', 'UTC');
$this->addOption('pad', false);
$this->addOption('fields', array('year', 'month', 'day', 'hour', 'minute', 'second'));
}
$inputTimezone = $this->getOption('input_timezone');
$outputTimezone = $this->getOption('output_timezone');
if ($inputTimezone != $outputTimezone)
/**
* Transforms a normalized date into a localized date string/array.
*
* @param DateTime $dateTime Normalized date.
* @return string|array Localized date array.
*/
public function transform($dateTime)
{
$dateTime->setTimezone(new \DateTimeZone($outputTimezone));
if (!$dateTime instanceof \DateTime) {
throw new \InvalidArgumentException('Expected value of type \DateTime');
}
$inputTimezone = $this->getOption('input_timezone');
$outputTimezone = $this->getOption('output_timezone');
if ($inputTimezone != $outputTimezone) {
$dateTime->setTimezone(new \DateTimeZone($outputTimezone));
}
$result = array_intersect_key(array(
'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->getOption('fields')));
if (!$this->getOption('pad')) {
foreach ($result as &$entry) {
$entry = (int)$entry;
}
}
return $result;
}
$result = array_intersect_key(array(
'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->getOption('fields')));
if (!$this->getOption('pad'))
/**
* Transforms a localized date string/array into a normalized date.
*
* @param array $value Localized date string/array
* @return DateTime Normalized date
*/
public function reverseTransform($value)
{
foreach ($result as &$entry)
{
$entry = (int)$entry;
}
$inputTimezone = $this->getOption('input_timezone');
$outputTimezone = $this->getOption('output_timezone');
if (!is_array($value)) {
throw new \InvalidArgumentException(sprintf('Expected argument of type array, %s given', gettype($value)));
}
$dateTime = new \DateTime(sprintf(
'%s-%s-%s %s:%s:%s %s',
isset($value['year']) ? $value['year'] : 1970,
isset($value['month']) ? $value['month'] : 1,
isset($value['day']) ? $value['day'] : 1,
isset($value['hour']) ? $value['hour'] : 0,
isset($value['minute']) ? $value['minute'] : 0,
isset($value['second']) ? $value['second'] : 0,
$outputTimezone
));
if ($inputTimezone != $outputTimezone) {
$dateTime->setTimezone(new \DateTimeZone($inputTimezone));
}
return $dateTime;
}
return $result;
}
/**
* Transforms a localized date string/array into a normalized date.
*
* @param array $value Localized date string/array
* @return DateTime Normalized date
*/
public function reverseTransform($value)
{
$inputTimezone = $this->getOption('input_timezone');
$outputTimezone = $this->getOption('output_timezone');
if (!is_array($value))
{
throw new \InvalidArgumentException(sprintf('Expected argument of type array, %s given', gettype($value)));
}
$dateTime = new \DateTime(sprintf(
'%s-%s-%s %s:%s:%s %s',
isset($value['year']) ? $value['year'] : 1970,
isset($value['month']) ? $value['month'] : 1,
isset($value['day']) ? $value['day'] : 1,
isset($value['hour']) ? $value['hour'] : 0,
isset($value['minute']) ? $value['minute'] : 0,
isset($value['second']) ? $value['second'] : 0,
$outputTimezone
));
if ($inputTimezone != $outputTimezone)
{
$dateTime->setTimezone(new \DateTimeZone($inputTimezone));
}
return $dateTime;
}
}

View File

@ -19,104 +19,96 @@ use \Symfony\Components\Form\ValueTransformer\ValueTransformerException;
*/
class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
{
/**
* {@inheritDoc}
*/
protected function configure()
{
parent::configure();
$this->addOption('date_format', self::MEDIUM);
$this->addOption('time_format', self::SHORT);
$this->addOption('input_timezone', 'UTC');
$this->addOption('output_timezone', 'UTC');
if (!in_array($this->getOption('date_format'), self::$formats, true))
/**
* {@inheritDoc}
*/
protected function configure()
{
throw new \InvalidArgumentException(sprintf('The option "date_format" is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $this->getOption('time_format')));
parent::configure();
$this->addOption('date_format', self::MEDIUM);
$this->addOption('time_format', self::SHORT);
$this->addOption('input_timezone', 'UTC');
$this->addOption('output_timezone', 'UTC');
if (!in_array($this->getOption('date_format'), self::$formats, true)) {
throw new \InvalidArgumentException(sprintf('The option "date_format" is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $this->getOption('time_format')));
}
if (!in_array($this->getOption('time_format'), self::$formats, true)) {
throw new \InvalidArgumentException(sprintf('The option "time_format" is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $this->getOption('time_format')));
}
}
if (!in_array($this->getOption('time_format'), self::$formats, true))
/**
* Transforms a normalized date into a localized date string/array.
*
* @param DateTime $dateTime Normalized date.
* @return string|array Localized date string/array.
*/
public function transform($dateTime)
{
throw new \InvalidArgumentException(sprintf('The option "time_format" is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $this->getOption('time_format')));
}
}
if (!$dateTime instanceof \DateTime) {
throw new \InvalidArgumentException('Expected value of type \DateTime');
}
/**
* Transforms a normalized date into a localized date string/array.
*
* @param DateTime $dateTime Normalized date.
* @return string|array Localized date string/array.
*/
public function transform($dateTime)
{
if (!$dateTime instanceof \DateTime)
{
throw new \InvalidArgumentException('Expected value of type \DateTime');
$inputTimezone = $this->getOption('input_timezone');
// convert time to UTC before passing it to the formatter
if ($inputTimezone != 'UTC') {
$dateTime->setTimezone(new \DateTimeZone('UTC'));
}
$value = $this->getIntlDateFormatter()->format((int)$dateTime->format('U'));
if (intl_get_error_code() != 0) {
throw new TransformationFailedException(intl_get_error_message());
}
return $value;
}
$inputTimezone = $this->getOption('input_timezone');
// convert time to UTC before passing it to the formatter
if ($inputTimezone != 'UTC')
/**
* Transforms a localized date string/array into a normalized date.
*
* @param string|array $value Localized date string/array
* @return DateTime Normalized date
*/
public function reverseTransform($value)
{
$dateTime->setTimezone(new \DateTimeZone('UTC'));
$inputTimezone = $this->getOption('input_timezone');
if (!is_string($value)) {
throw new \InvalidArgumentException(sprintf('Expected argument of type string, %s given', gettype($value)));
}
$timestamp = $this->getIntlDateFormatter()->parse($value);
if (intl_get_error_code() != 0) {
throw new TransformationFailedException(intl_get_error_message());
}
// read timestamp into DateTime object - the formatter delivers in UTC
$dateTime = new \DateTime(sprintf('@%s UTC', $timestamp));
if ($inputTimezone != 'UTC') {
$dateTime->setTimezone(new \DateTimeZone($inputTimezone));
}
return $dateTime;
}
$value = $this->getIntlDateFormatter()->format((int)$dateTime->format('U'));
if (intl_get_error_code() != 0)
/**
* Returns a preconfigured IntlDateFormatter instance
*
* @return \IntlDateFormatter
*/
protected function getIntlDateFormatter()
{
throw new TransformationFailedException(intl_get_error_message());
$dateFormat = $this->getIntlFormatConstant($this->getOption('date_format'));
$timeFormat = $this->getIntlFormatConstant($this->getOption('time_format'));
$timezone = $this->getOption('output_timezone');
return new \IntlDateFormatter($this->locale, $dateFormat, $timeFormat, $timezone);
}
return $value;
}
/**
* Transforms a localized date string/array into a normalized date.
*
* @param string|array $value Localized date string/array
* @return DateTime Normalized date
*/
public function reverseTransform($value)
{
$inputTimezone = $this->getOption('input_timezone');
if (!is_string($value))
{
throw new \InvalidArgumentException(sprintf('Expected argument of type string, %s given', gettype($value)));
}
$timestamp = $this->getIntlDateFormatter()->parse($value);
if (intl_get_error_code() != 0)
{
throw new TransformationFailedException(intl_get_error_message());
}
// read timestamp into DateTime object - the formatter delivers in UTC
$dateTime = new \DateTime(sprintf('@%s UTC', $timestamp));
if ($inputTimezone != 'UTC')
{
$dateTime->setTimezone(new \DateTimeZone($inputTimezone));
}
return $dateTime;
}
/**
* Returns a preconfigured IntlDateFormatter instance
*
* @return \IntlDateFormatter
*/
protected function getIntlDateFormatter()
{
$dateFormat = $this->getIntlFormatConstant($this->getOption('date_format'));
$timeFormat = $this->getIntlFormatConstant($this->getOption('time_format'));
$timezone = $this->getOption('output_timezone');
return new \IntlDateFormatter($this->locale, $dateFormat, $timeFormat, $timezone);
}
}

View File

@ -12,43 +12,42 @@ use \Symfony\Components\Form\ValueTransformer\ValueTransformerException;
*/
class MoneyToLocalizedStringTransformer extends NumberToLocalizedStringTransformer
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('grouping', true);
$this->addOption('precision', 2);
$this->addOption('divisor', 1);
parent::configure();
}
/**
* Transforms a normalized format into a localized money string.
*
* @param number $value Normalized number
* @return string Localized money string.
*/
public function transform($value)
{
if (!is_numeric($value))
/**
* {@inheritDoc}
*/
protected function configure()
{
throw new \InvalidArgumentException(sprintf('Numeric argument expected, %s given', gettype($value)));
$this->addOption('grouping', true);
$this->addOption('precision', 2);
$this->addOption('divisor', 1);
parent::configure();
}
return parent::transform($value / $this->getOption('divisor'));
}
/**
* Transforms a normalized format into a localized money string.
*
* @param number $value Normalized number
* @return string Localized money string.
*/
public function transform($value)
{
if (!is_numeric($value)) {
throw new \InvalidArgumentException(sprintf('Numeric argument expected, %s given', gettype($value)));
}
/**
* Transforms a localized money string into a normalized format.
*
* @param string $value Localized money string
* @return number Normalized number
*/
public function reverseTransform($value)
{
return parent::reverseTransform($value) * $this->getOption('divisor');
}
return parent::transform($value / $this->getOption('divisor'));
}
/**
* Transforms a localized money string into a normalized format.
*
* @param string $value Localized money string
* @return number Normalized number
*/
public function reverseTransform($value)
{
return parent::reverseTransform($value) * $this->getOption('divisor');
}
}

View File

@ -13,80 +13,75 @@ use \Symfony\Components\Form\ValueTransformer\ValueTransformerException;
*/
class NumberToLocalizedStringTransformer extends BaseValueTransformer
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('precision', null);
$this->addOption('grouping', false);
parent::configure();
}
/**
* Transforms a number type into localized number.
*
* @param number $value Number value.
* @return string Localized value.
*/
public function transform($value)
{
if (!is_numeric($value))
/**
* {@inheritDoc}
*/
protected function configure()
{
throw new \InvalidArgumentException(sprintf('Numeric argument expected, %s given', gettype($value)));
$this->addOption('precision', null);
$this->addOption('grouping', false);
parent::configure();
}
$formatter = $this->getNumberFormatter();
$value = $formatter->format($value);
if (intl_is_failure($formatter->getErrorCode()))
/**
* Transforms a number type into localized number.
*
* @param number $value Number value.
* @return string Localized value.
*/
public function transform($value)
{
throw new TransformationFailedException($formatter->getErrorMessage());
if (!is_numeric($value)) {
throw new \InvalidArgumentException(sprintf('Numeric argument expected, %s given', gettype($value)));
}
$formatter = $this->getNumberFormatter();
$value = $formatter->format($value);
if (intl_is_failure($formatter->getErrorCode())) {
throw new TransformationFailedException($formatter->getErrorMessage());
}
return $value;
}
return $value;
}
/**
* Transforms a localized number into an integer or float
*
* @param string $value
*/
public function reverseTransform($value)
{
if (!is_string($value))
/**
* Transforms a localized number into an integer or float
*
* @param string $value
*/
public function reverseTransform($value)
{
throw new \InvalidArgumentException(sprintf('Expected argument of type string, %s given', gettype($value)));
if (!is_string($value)) {
throw new \InvalidArgumentException(sprintf('Expected argument of type string, %s given', gettype($value)));
}
$formatter = $this->getNumberFormatter();
$value = $formatter->parse($value);
if (intl_is_failure($formatter->getErrorCode())) {
throw new TransformationFailedException($formatter->getErrorMessage());
}
return $value;
}
$formatter = $this->getNumberFormatter();
$value = $formatter->parse($value);
if (intl_is_failure($formatter->getErrorCode()))
/**
* Returns a preconfigured \NumberFormatter instance
*
* @return \NumberFormatter
*/
protected function getNumberFormatter()
{
throw new TransformationFailedException($formatter->getErrorMessage());
$formatter = new \NumberFormatter($this->locale, \NumberFormatter::DECIMAL);
if ($this->getOption('precision') !== null) {
$formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->getOption('precision'));
}
$formatter->setAttribute(\NumberFormatter::GROUPING_USED, $this->getOption('grouping'));
return $formatter;
}
return $value;
}
/**
* Returns a preconfigured \NumberFormatter instance
*
* @return \NumberFormatter
*/
protected function getNumberFormatter()
{
$formatter = new \NumberFormatter($this->locale, \NumberFormatter::DECIMAL);
if ($this->getOption('precision') !== null)
{
$formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->getOption('precision'));
}
$formatter->setAttribute(\NumberFormatter::GROUPING_USED, $this->getOption('grouping'));
return $formatter;
}
}

View File

@ -12,101 +12,94 @@ use \Symfony\Components\Form\ValueTransformer\ValueTransformerException;
*/
class PercentToLocalizedStringTransformer extends BaseValueTransformer
{
const FRACTIONAL = 'fractional';
const INTEGER = 'integer';
const FRACTIONAL = 'fractional';
const INTEGER = 'integer';
protected static $types = array(
self::FRACTIONAL,
self::INTEGER,
);
protected static $types = array(
self::FRACTIONAL,
self::INTEGER,
);
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('type', self::FRACTIONAL);
$this->addOption('precision', 0);
if (!in_array($this->getOption('type'), self::$types, true))
/**
* {@inheritDoc}
*/
protected function configure()
{
throw new \InvalidArgumentException(sprintf('The option "type" is expected to be one of "%s"', implode('", "', self::$types)));
$this->addOption('type', self::FRACTIONAL);
$this->addOption('precision', 0);
if (!in_array($this->getOption('type'), self::$types, true)) {
throw new \InvalidArgumentException(sprintf('The option "type" is expected to be one of "%s"', implode('", "', self::$types)));
}
parent::configure();
}
parent::configure();
}
/**
* Transforms between a normalized format (integer or float) into a percentage value.
*
* @param number $value Normalized value.
* @return number Percentage value.
*/
public function transform($value)
{
if (!is_numeric($value))
/**
* Transforms between a normalized format (integer or float) into a percentage value.
*
* @param number $value Normalized value.
* @return number Percentage value.
*/
public function transform($value)
{
throw new \InvalidArgumentException(sprintf('Numeric argument expected, %s given', gettype($value)));
if (!is_numeric($value)) {
throw new \InvalidArgumentException(sprintf('Numeric argument expected, %s given', gettype($value)));
}
if (self::FRACTIONAL == $this->getOption('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;
}
if (self::FRACTIONAL == $this->getOption('type'))
/**
* Transforms between a percentage value into a normalized format (integer or float).
*
* @param number $value Percentage value.
* @return number Normalized value.
*/
public function reverseTransform($value)
{
$value *= 100;
if (!is_string($value)) {
throw new \InvalidArgumentException(sprintf('Expected argument of type string, %s given', gettype($value)));
}
$formatter = $this->getNumberFormatter();
// replace normal spaces so that the formatter can read them
$value = $formatter->parse(str_replace(' ', ' ', $value));
if (intl_is_failure($formatter->getErrorCode())) {
throw new TransformationFailedException($formatter->getErrorMessage());
}
if (self::FRACTIONAL == $this->getOption('type')) {
$value /= 100;
}
return $value;
}
$formatter = $this->getNumberFormatter();
$value = $formatter->format($value);
if (intl_is_failure($formatter->getErrorCode()))
/**
* Returns a preconfigured \NumberFormatter instance
*
* @return \NumberFormatter
*/
protected function getNumberFormatter()
{
throw new TransformationFailedException($formatter->getErrorMessage());
$formatter = new \NumberFormatter($this->locale, \NumberFormatter::DECIMAL);
$formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->getOption('precision'));
return $formatter;
}
// replace the UTF-8 non break spaces
return $value;
}
/**
* Transforms between a percentage value into a normalized format (integer or float).
*
* @param number $value Percentage value.
* @return number Normalized value.
*/
public function reverseTransform($value)
{
if (!is_string($value))
{
throw new \InvalidArgumentException(sprintf('Expected argument of type string, %s given', gettype($value)));
}
$formatter = $this->getNumberFormatter();
// replace normal spaces so that the formatter can read them
$value = $formatter->parse(str_replace(' ', ' ', $value));
if (intl_is_failure($formatter->getErrorCode()))
{
throw new TransformationFailedException($formatter->getErrorMessage());
}
if (self::FRACTIONAL == $this->getOption('type'))
{
$value /= 100;
}
return $value;
}
/**
* Returns a preconfigured \NumberFormatter instance
*
* @return \NumberFormatter
*/
protected function getNumberFormatter()
{
$formatter = new \NumberFormatter($this->locale, \NumberFormatter::DECIMAL);
$formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->getOption('precision'));
return $formatter;
}
}

View File

@ -12,43 +12,43 @@ namespace Symfony\Components\Form\ValueTransformer;
*/
class ReversedTransformer implements ValueTransformerInterface
{
/**
* The reversed transformer
* @var ValueTransformerInterface
*/
protected $reversedTransformer;
/**
* The reversed transformer
* @var ValueTransformerInterface
*/
protected $reversedTransformer;
/**
* Reverses this transformer
*
* @param ValueTransformerInterface $innerTransformer
*/
public function __construct(ValueTransformerInterface $reversedTransformer)
{
$this->reversedTransformer = $reversedTransformer;
}
/**
* Reverses this transformer
*
* @param ValueTransformerInterface $innerTransformer
*/
public function __construct(ValueTransformerInterface $reversedTransformer)
{
$this->reversedTransformer = $reversedTransformer;
}
/**
* {@inheritDoc}
*/
public function transform($value)
{
return $this->reversedTransformer->reverseTransform($value);
}
/**
* {@inheritDoc}
*/
public function transform($value)
{
return $this->reversedTransformer->reverseTransform($value);
}
/**
* {@inheritDoc}
*/
public function reverseTransform($value)
{
return $this->reversedTransformer->transform($value);
}
/**
* {@inheritDoc}
*/
public function reverseTransform($value)
{
return $this->reversedTransformer->transform($value);
}
/**
* {@inheritDoc}
*/
public function setLocale($locale)
{
$this->reversedTransformer->setLocale($locale);
}
/**
* {@inheritDoc}
*/
public function setLocale($locale)
{
$this->reversedTransformer->setLocale($locale);
}
}

View File

@ -12,60 +12,55 @@ use \Symfony\Components\Form\ValueTransformer\ValueTransformerException;
*/
class StringToDateTimeTransformer extends BaseValueTransformer
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('input_timezone', 'UTC');
$this->addOption('output_timezone', 'UTC');
$this->addOption('format', 'Y-m-d H:i:s');
}
/**
* 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 A DateTime object
*/
public function transform($value)
{
$inputTimezone = $this->getOption('input_timezone');
$outputTimezone = $this->getOption('output_timezone');
try
/**
* {@inheritDoc}
*/
protected function configure()
{
$dateTime = new \DateTime("$value $inputTimezone");
if ($inputTimezone != $outputTimezone)
{
$dateTime->setTimeZone(new \DateTimeZone($outputTimezone));
}
return $dateTime;
}
catch (\Exception $e)
{
throw new \InvalidArgumentException('Expected a valid date string. ' . $e->getMessage(), 0, $e);
}
}
/**
* Transforms a DateTime object into a date string with the configured format
* and timezone
*
* @param DateTime $value A DateTime object
* @return string A value as produced by PHP's date() function
*/
public function reverseTransform($value)
{
if (!$value instanceof \DateTime)
{
throw new \InvalidArgumentException('Expected value of type \DateTime');
$this->addOption('input_timezone', 'UTC');
$this->addOption('output_timezone', 'UTC');
$this->addOption('format', 'Y-m-d H:i:s');
}
$value->setTimezone(new \DateTimeZone($this->getOption('input_timezone')));
/**
* 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 A DateTime object
*/
public function transform($value)
{
$inputTimezone = $this->getOption('input_timezone');
$outputTimezone = $this->getOption('output_timezone');
return $value->format($this->getOption('format'));
}
try {
$dateTime = new \DateTime("$value $inputTimezone");
if ($inputTimezone != $outputTimezone) {
$dateTime->setTimeZone(new \DateTimeZone($outputTimezone));
}
return $dateTime;
} catch (\Exception $e) {
throw new \InvalidArgumentException('Expected a valid date string. ' . $e->getMessage(), 0, $e);
}
}
/**
* Transforms a DateTime object into a date string with the configured format
* and timezone
*
* @param DateTime $value A DateTime object
* @return string A value as produced by PHP's date() function
*/
public function reverseTransform($value)
{
if (!$value instanceof \DateTime) {
throw new \InvalidArgumentException('Expected value of type \DateTime');
}
$value->setTimezone(new \DateTimeZone($this->getOption('input_timezone')));
return $value->format($this->getOption('format'));
}
}

View File

@ -12,58 +12,53 @@ use \Symfony\Components\Form\ValueTransformer\ValueTransformerException;
*/
class TimestampToDateTimeTransformer extends BaseValueTransformer
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('input_timezone', 'UTC');
$this->addOption('output_timezone', 'UTC');
}
/**
* Transforms a timestamp in the configured timezone into a DateTime object
*
* @param string $value A value as produced by PHP's date() function
* @return DateTime A DateTime object
*/
public function transform($value)
{
$inputTimezone = $this->getOption('input_timezone');
$outputTimezone = $this->getOption('output_timezone');
try
/**
* {@inheritDoc}
*/
protected function configure()
{
$dateTime = new \DateTime("@$value $inputTimezone");
if ($inputTimezone != $outputTimezone)
{
$dateTime->setTimezone(new \DateTimeZone($outputTimezone));
}
return $dateTime;
}
catch (\Exception $e)
{
throw new \InvalidArgumentException('Expected a valid timestamp. ' . $e->getMessage(), 0, $e);
}
}
/**
* Transforms a DateTime object into a timestamp in the configured timezone
*
* @param DateTime $value A DateTime object
* @return integer A timestamp
*/
public function reverseTransform($value)
{
if (!$value instanceof \DateTime)
{
throw new \InvalidArgumentException('Expected value of type \DateTime');
$this->addOption('input_timezone', 'UTC');
$this->addOption('output_timezone', 'UTC');
}
$value->setTimezone(new \DateTimeZone($this->getOption('input_timezone')));
/**
* Transforms a timestamp in the configured timezone into a DateTime object
*
* @param string $value A value as produced by PHP's date() function
* @return DateTime A DateTime object
*/
public function transform($value)
{
$inputTimezone = $this->getOption('input_timezone');
$outputTimezone = $this->getOption('output_timezone');
return (int)$value->format('U');
}
try {
$dateTime = new \DateTime("@$value $inputTimezone");
if ($inputTimezone != $outputTimezone) {
$dateTime->setTimezone(new \DateTimeZone($outputTimezone));
}
return $dateTime;
} catch (\Exception $e) {
throw new \InvalidArgumentException('Expected a valid timestamp. ' . $e->getMessage(), 0, $e);
}
}
/**
* Transforms a DateTime object into a timestamp in the configured timezone
*
* @param DateTime $value A DateTime object
* @return integer A timestamp
*/
public function reverseTransform($value)
{
if (!$value instanceof \DateTime) {
throw new \InvalidArgumentException('Expected value of type \DateTime');
}
$value->setTimezone(new \DateTimeZone($this->getOption('input_timezone')));
return (int)$value->format('U');
}
}

View File

@ -9,73 +9,70 @@ namespace Symfony\Components\Form\ValueTransformer;
*/
class ValueTransformerChain implements ValueTransformerInterface
{
/**
* The value transformers
* @var array
*/
protected $transformers;
/**
* The value transformers
* @var array
*/
protected $transformers;
/**
* Uses the given value transformers to transform values
*
* @param array $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 The transformed value
*/
public function transform($value)
{
foreach ($this->transformers as $transformer)
/**
* Uses the given value transformers to transform values
*
* @param array $transformers
*/
public function __construct(array $transformers)
{
$value = $transformer->transform($value);
$this->transformers = $transformers;
}
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 The reverse-transformed value
*/
public function reverseTransform($value)
{
for ($i = count($this->transformers) - 1; $i >= 0; --$i)
/**
* 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 The transformed value
*/
public function transform($value)
{
$value = $this->transformers[$i]->reverseTransform($value);
foreach ($this->transformers as $transformer) {
$value = $transformer->transform($value);
}
return $value;
}
return $value;
}
/**
* {@inheritDoc}
*/
public function setLocale($locale)
{
foreach ($this->transformers as $transformer)
/**
* 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 The reverse-transformed value
*/
public function reverseTransform($value)
{
$transformer->setLocale($locale);
for ($i = count($this->transformers) - 1; $i >= 0; --$i) {
$value = $this->transformers[$i]->reverseTransform($value);
}
return $value;
}
/**
* {@inheritDoc}
*/
public function setLocale($locale)
{
foreach ($this->transformers as $transformer) {
$transformer->setLocale($locale);
}
}
}
}

View File

@ -11,28 +11,28 @@ use Symfony\Components\Form\Localizable;
*/
interface ValueTransformerInterface extends Localizable
{
/**
* Transforms a value from the original representation to a transformed
* representation.
*
* @param mixed $value The value in the original representation
* @return mixed The value in the transformed representation
* @throws InvalidArgument Exception when the argument is no string
* @throws ValueTransformer Exception when the transformation fails
*/
public function transform($value);
/**
* Transforms a value from the original representation to a transformed
* representation.
*
* @param mixed $value The value in the original representation
* @return mixed The value in the transformed representation
* @throws InvalidArgument Exception when the argument is no string
* @throws ValueTransformer Exception when the transformation fails
*/
public function transform($value);
/**
* Transforms a value from the transformed representation to its original
* representation.
*
* This method must be able to deal with null values.
*
* @param mixed $value The value in the transformed representation
* @return mixed The value in the original representation
* @throws InvalidArgument Exception when the argument is not of the
* expected type
* @throws ValueTransformer Exception when the transformation fails
*/
public function reverseTransform($value);
/**
* Transforms a value from the transformed representation to its original
* representation.
*
* This method must be able to deal with null values.
*
* @param mixed $value The value in the transformed representation
* @return mixed The value in the original representation
* @throws InvalidArgument Exception when the argument is not of the
* expected type
* @throws ValueTransformer Exception when the transformation fails
*/
public function reverseTransform($value);
}

View File

@ -9,14 +9,14 @@ namespace Symfony\Components\I18N;
*/
interface TranslatorInterface
{
/**
* Translates a given text string.
*
* @param string $text The text to translate
* @param array $parameters The parameters to inject into the text
* @param string $locale The locale of the translated text. If null,
* the preconfigured locale of the translator
* or the system's default culture is used.
*/
public function translate($text, array $parameters = array(), $locale = null);
/**
* Translates a given text string.
*
* @param string $text The text to translate
* @param array $parameters The parameters to inject into the text
* @param string $locale The locale of the translated text. If null,
* the preconfigured locale of the translator
* or the system's default culture is used.
*/
public function translate($text, array $parameters = array(), $locale = null);
}

View File

@ -19,160 +19,145 @@ use Symfony\Components\Validator\Exception\ConstraintDefinitionException;
*/
class Constraint
{
const DEFAULT_GROUP = 'Default';
const DEFAULT_GROUP = 'Default';
public $groups = self::DEFAULT_GROUP;
public $groups = self::DEFAULT_GROUP;
/**
* Initializes the constraint with options
*
* You should pass an associative array. The keys should be the names of
* existing properties in this class. The values should be the value for these
* properties.
*
* Alternatively you can override the method defaultOption() to return the
* name of an existing property. If no associative array is passed, this
* property is set instead.
*
* You can force that certain options are set by overriding
* requiredOptions() to return the names of these options. If any
* option is not set here, an exception is thrown.
*
* @param mixed $options The options (as associative array)
* or the value for the default
* option (any other type)
* @throws InvalidOptionsException When you pass the names of non-existing
* options
* @throws MissingOptionsException When you don't pass any of the options
* returned by requiredOptions()
* @throws ConstraintDefinitionException When you don't pass an associative
* array, but defaultOption() returns
* NULL
*/
public function __construct($options = null)
{
$invalidOptions = array();
$missingOptions = array_flip((array)$this->requiredOptions());
if (is_array($options) && count($options) == 1 && isset($options['value']))
/**
* Initializes the constraint with options
*
* You should pass an associative array. The keys should be the names of
* existing properties in this class. The values should be the value for these
* properties.
*
* Alternatively you can override the method defaultOption() to return the
* name of an existing property. If no associative array is passed, this
* property is set instead.
*
* You can force that certain options are set by overriding
* requiredOptions() to return the names of these options. If any
* option is not set here, an exception is thrown.
*
* @param mixed $options The options (as associative array)
* or the value for the default
* option (any other type)
* @throws InvalidOptionsException When you pass the names of non-existing
* options
* @throws MissingOptionsException When you don't pass any of the options
* returned by requiredOptions()
* @throws ConstraintDefinitionException When you don't pass an associative
* array, but defaultOption() returns
* NULL
*/
public function __construct($options = null)
{
$options = $options['value'];
}
$invalidOptions = array();
$missingOptions = array_flip((array)$this->requiredOptions());
if (is_array($options) && count($options) > 0 && is_string(key($options)))
{
foreach ($options as $option => $value)
{
if (property_exists($this, $option))
{
$this->$option = $value;
unset($missingOptions[$option]);
if (is_array($options) && count($options) == 1 && isset($options['value'])) {
$options = $options['value'];
}
else
{
$invalidOptions[] = $option;
if (is_array($options) && count($options) > 0 && is_string(key($options))) {
foreach ($options as $option => $value) {
if (property_exists($this, $option)) {
$this->$option = $value;
unset($missingOptions[$option]);
} else {
$invalidOptions[] = $option;
}
}
} else if ($options) {
$option = $this->defaultOption();
if (is_null($option)) {
throw new ConstraintDefinitionException(
sprintf('No default option is configured for constraint %s', get_class($this))
);
}
if (property_exists($this, $option)) {
$this->$option = $options;
unset($missingOptions[$option]);
} else {
$invalidOptions[] = $option;
}
}
}
if (count($invalidOptions) > 0) {
throw new InvalidOptionsException(
sprintf('The options "%s" do not exist in constraint %s', implode('", "', $invalidOptions), get_class($this)),
$invalidOptions
);
}
if (count($missingOptions) > 0) {
throw new MissingOptionsException(
sprintf('The options "%s" must be set for constraint %s', implode('", "', array_keys($missingOptions)), get_class($this)),
array_keys($missingOptions)
);
}
$this->groups = (array)$this->groups;
}
else if ($options)
/**
* Unsupported operation.
*/
public function __set($option, $value)
{
$option = $this->defaultOption();
if (is_null($option))
{
throw new ConstraintDefinitionException(
sprintf('No default option is configured for constraint %s', get_class($this))
);
}
if (property_exists($this, $option))
{
$this->$option = $options;
unset($missingOptions[$option]);
}
else
{
$invalidOptions[] = $option;
}
throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint %s', $option, get_class($this)), array($option));
}
if (count($invalidOptions) > 0)
/**
* Adds the given group if this constraint is in the Default group
*
* @param string $group
*/
public function addImplicitGroupName($group)
{
throw new InvalidOptionsException(
sprintf('The options "%s" do not exist in constraint %s', implode('", "', $invalidOptions), get_class($this)),
$invalidOptions
);
if (in_array(Constraint::DEFAULT_GROUP, $this->groups) && !in_array($group, $this->groups)) {
$this->groups[] = $group;
}
}
if (count($missingOptions) > 0)
/**
* Returns the name of the default option
*
* Override this method to define a default option.
*
* @return string
* @see __construct()
*/
public function defaultOption()
{
throw new MissingOptionsException(
sprintf('The options "%s" must be set for constraint %s', implode('", "', array_keys($missingOptions)), get_class($this)),
array_keys($missingOptions)
);
return null;
}
$this->groups = (array)$this->groups;
}
/**
* Unsupported operation.
*/
public function __set($option, $value)
{
throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint %s', $option, get_class($this)), array($option));
}
/**
* Adds the given group if this constraint is in the Default group
*
* @param string $group
*/
public function addImplicitGroupName($group)
{
if (in_array(Constraint::DEFAULT_GROUP, $this->groups) && !in_array($group, $this->groups))
/**
* Returns the name of the required options
*
* Override this method if you want to define required options.
*
* @return array
* @see __construct()
*/
public function requiredOptions()
{
$this->groups[] = $group;
return array();
}
}
/**
* Returns the name of the default option
*
* Override this method to define a default option.
*
* @return string
* @see __construct()
*/
public function defaultOption()
{
return null;
}
/**
* Returns the name of the required options
*
* Override this method if you want to define required options.
*
* @return array
* @see __construct()
*/
public function requiredOptions()
{
return array();
}
/**
* Returns the name of the class that validates this constraint
*
* By default, this is the fully qualified name of the constraint class
* suffixed with "Validator". You can override this method to change that
* behaviour.
*
* @return string
*/
public function validatedBy()
{
return get_class($this) . 'Validator';
}
/**
* Returns the name of the class that validates this constraint
*
* By default, this is the fully qualified name of the constraint class
* suffixed with "Validator". You can override this method to change that
* behaviour.
*
* @return string
*/
public function validatedBy()
{
return get_class($this) . 'Validator';
}
}

View File

@ -4,30 +4,30 @@ namespace Symfony\Components\Validator;
abstract class ConstraintValidator implements ConstraintValidatorInterface
{
protected $context;
private $messageTemplate;
private $messageParameters;
protected $context;
private $messageTemplate;
private $messageParameters;
public function initialize(ValidationContext $context)
{
$this->context = $context;
$this->messageTemplate = '';
$this->messageParameters = array();
}
public function initialize(ValidationContext $context)
{
$this->context = $context;
$this->messageTemplate = '';
$this->messageParameters = array();
}
public function getMessageTemplate()
{
return $this->messageTemplate;
}
public function getMessageTemplate()
{
return $this->messageTemplate;
}
public function getMessageParameters()
{
return $this->messageParameters;
}
public function getMessageParameters()
{
return $this->messageParameters;
}
protected function setMessage($template, array $parameters = array())
{
$this->messageTemplate = $template;
$this->messageParameters = $parameters;
}
protected function setMessage($template, array $parameters = array())
{
$this->messageTemplate = $template;
$this->messageParameters = $parameters;
}
}

View File

@ -7,17 +7,16 @@ use Symfony\Components\Validator\Constraint;
class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface
{
protected $validators = array();
protected $validators = array();
public function getInstance(Constraint $constraint)
{
$className = $constraint->validatedBy();
if (!isset($this->validators[$className]))
public function getInstance(Constraint $constraint)
{
$this->validators[$className] = new $className();
}
$className = $constraint->validatedBy();
return $this->validators[$className];
}
if (!isset($this->validators[$className])) {
$this->validators[$className] = new $className();
}
return $this->validators[$className];
}
}

View File

@ -6,5 +6,5 @@ use Symfony\Components\Validator\Constraint;
interface ConstraintValidatorFactoryInterface
{
public function getInstance(Constraint $constraint);
public function getInstance(Constraint $constraint);
}

View File

@ -4,11 +4,11 @@ namespace Symfony\Components\Validator;
interface ConstraintValidatorInterface
{
public function initialize(ValidationContext $context);
public function initialize(ValidationContext $context);
public function isValid($value, Constraint $constraint);
public function isValid($value, Constraint $constraint);
public function getMessageTemplate();
public function getMessageTemplate();
public function getMessageParameters();
public function getMessageParameters();
}

View File

@ -4,36 +4,36 @@ namespace Symfony\Components\Validator;
class ConstraintViolation
{
protected $message;
protected $root;
protected $propertyPath;
protected $invalidValue;
protected $message;
protected $root;
protected $propertyPath;
protected $invalidValue;
public function __construct($message, $root, $propertyPath, $invalidValue)
{
$this->message = $message;
$this->root = $root;
$this->propertyPath = $propertyPath;
$this->invalidValue = $invalidValue;
}
public function __construct($message, $root, $propertyPath, $invalidValue)
{
$this->message = $message;
$this->root = $root;
$this->propertyPath = $propertyPath;
$this->invalidValue = $invalidValue;
}
public function getMessage()
{
return $this->message;
}
public function getMessage()
{
return $this->message;
}
public function getRoot()
{
return $this->root;
}
public function getRoot()
{
return $this->root;
}
public function getPropertyPath()
{
return $this->propertyPath;
}
public function getPropertyPath()
{
return $this->propertyPath;
}
public function getInvalidValue()
{
return $this->invalidValue;
}
public function getInvalidValue()
{
return $this->invalidValue;
}
}

View File

@ -4,46 +4,44 @@ namespace Symfony\Components\Validator;
class ConstraintViolationList implements \IteratorAggregate, \Countable
{
protected $violations = array();
protected $violations = array();
public function __toString()
{
$string = '';
foreach ($this->violations as $violation)
public function __toString()
{
$param = $violation->getMessageParameters();
$message = str_replace(array_keys($param), $param, $violation->getMessageTemplate());
$string .= <<<EOF
$string = '';
foreach ($this->violations as $violation) {
$param = $violation->getMessageParameters();
$message = str_replace(array_keys($param), $param, $violation->getMessageTemplate());
$string .= <<<EOF
{$violation->getRoot()}.{$violation->getPropertyPath()}:
$message
$message
EOF;
}
return $string;
}
return $string;
}
public function add(ConstraintViolation $violation)
{
$this->violations[] = $violation;
}
public function addAll(ConstraintViolationList $violations)
{
foreach ($violations->violations as $violation)
public function add(ConstraintViolation $violation)
{
$this->violations[] = $violation;
$this->violations[] = $violation;
}
}
public function getIterator()
{
return new \ArrayIterator($this->violations);
}
public function addAll(ConstraintViolationList $violations)
{
foreach ($violations->violations as $violation) {
$this->violations[] = $violation;
}
}
public function count()
{
return count($this->violations);
}
public function getIterator()
{
return new \ArrayIterator($this->violations);
}
public function count()
{
return count($this->violations);
}
}

View File

@ -4,15 +4,15 @@ namespace Symfony\Components\Validator\Constraints;
class All extends \Symfony\Components\Validator\Constraint
{
public $constraints = array();
public $constraints = array();
public function defaultOption()
{
return 'constraints';
}
public function defaultOption()
{
return 'constraints';
}
public function requiredOptions()
{
return array('constraints');
}
public function requiredOptions()
{
return array('constraints');
}
}

View File

@ -8,34 +8,30 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
class AllValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
if ($value === null) {
return true;
}
if (!is_array($value) && !$value instanceof \Traversable) {
throw new UnexpectedTypeException($value, 'array or Traversable');
}
$walker = $this->context->getGraphWalker();
$group = $this->context->getGroup();
$propertyPath = $this->context->getPropertyPath();
// cannot simply cast to array, because then the object is converted to an
// array instead of wrapped inside
$constraints = is_array($constraint->constraints) ? $constraint->constraints : array($constraint->constraints);
foreach ($value as $key => $element) {
foreach ($constraints as $constr) {
$walker->walkConstraint($constr, $element, $group, $propertyPath.'['.$key.']');
}
}
return true;
}
if (!is_array($value) && !$value instanceof \Traversable)
{
throw new UnexpectedTypeException($value, 'array or Traversable');
}
$walker = $this->context->getGraphWalker();
$group = $this->context->getGroup();
$propertyPath = $this->context->getPropertyPath();
// cannot simply cast to array, because then the object is converted to an
// array instead of wrapped inside
$constraints = is_array($constraint->constraints) ? $constraint->constraints : array($constraint->constraints);
foreach ($value as $key => $element)
{
foreach ($constraints as $constr)
{
$walker->walkConstraint($constr, $element, $group, $propertyPath.'['.$key.']');
}
}
return true;
}
}

View File

@ -4,5 +4,5 @@ namespace Symfony\Components\Validator\Constraints;
class AssertFalse extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.AssertFalse.message';
public $message = 'Symfony.Validator.AssertFalse.message';
}

View File

@ -7,20 +7,18 @@ use Symfony\Components\Validator\ConstraintValidator;
class AssertFalseValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
if ($value === null) {
return true;
}
if ($value) {
$this->setMessage($constraint->message);
return false;
}
return true;
}
if ($value)
{
$this->setMessage($constraint->message);
return false;
}
return true;
}
}

View File

@ -4,5 +4,5 @@ namespace Symfony\Components\Validator\Constraints;
class AssertTrue extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.AssertTrue.message';
public $message = 'Symfony.Validator.AssertTrue.message';
}

View File

@ -7,20 +7,18 @@ use Symfony\Components\Validator\ConstraintValidator;
class AssertTrueValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
if ($value === null) {
return true;
}
if (!$value) {
$this->setMessage($constraint->message);
return false;
}
return true;
}
if (!$value)
{
$this->setMessage($constraint->message);
return false;
}
return true;
}
}

View File

@ -4,22 +4,22 @@ namespace Symfony\Components\Validator\Constraints;
class AssertType extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.AssertType.message';
public $type;
public $message = 'Symfony.Validator.AssertType.message';
public $type;
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'type';
}
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'type';
}
/**
* {@inheritDoc}
*/
public function requiredOptions()
{
return array('type');
}
/**
* {@inheritDoc}
*/
public function requiredOptions()
{
return array('type');
}
}

View File

@ -7,30 +7,26 @@ use Symfony\Components\Validator\ConstraintValidator;
class AssertTypeValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
if ($value === null) {
return true;
}
$type = $constraint->type == 'boolean' ? 'bool' : $constraint->type;
$function = 'is_' . $type;
if (function_exists($function) && call_user_func($function, $value)) {
return true;
} else if ($value instanceof $constraint->type) {
return true;
}
$this->setMessage($constraint->message, array(
'value' => $value,
'type' => $constraint->type,
));
return false;
}
$type = $constraint->type == 'boolean' ? 'bool' : $constraint->type;
$function = 'is_' . $type;
if (function_exists($function) && call_user_func($function, $value))
{
return true;
}
else if ($value instanceof $constraint->type)
{
return true;
}
$this->setMessage($constraint->message, array(
'value' => $value,
'type' => $constraint->type,
));
return false;
}
}

View File

@ -4,5 +4,5 @@ namespace Symfony\Components\Validator\Constraints;
class Blank extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.Blank.message';
public $message = 'Symfony.Validator.Blank.message';
}

View File

@ -7,15 +7,14 @@ use Symfony\Components\Validator\ConstraintValidator;
class BlankValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if ($value !== '' && $value !== null)
public function isValid($value, Constraint $constraint)
{
$this->setMessage($constraint->message, array('value' => $value));
if ($value !== '' && $value !== null) {
$this->setMessage($constraint->message, array('value' => $value));
return false;
return false;
}
return true;
}
return true;
}
}

View File

@ -12,20 +12,20 @@ namespace Symfony\Components\Validator\Constraints;
class Choice extends \Symfony\Components\Validator\Constraint
{
public $choices;
public $callback;
public $multiple = false;
public $min = null;
public $max = null;
public $message = 'Symfony.Validator.Choice.message';
public $minMessage = 'Symfony.Validator.Choice.minMessage';
public $maxMessage = 'Symfony.Validator.Choice.maxMessage';
public $choices;
public $callback;
public $multiple = false;
public $min = null;
public $max = null;
public $message = 'Symfony.Validator.Choice.message';
public $minMessage = 'Symfony.Validator.Choice.minMessage';
public $maxMessage = 'Symfony.Validator.Choice.maxMessage';
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'choices';
}
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'choices';
}
}

View File

@ -24,78 +24,60 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
*/
class ChoiceValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if (!$constraint->choices && !$constraint->callback)
public function isValid($value, Constraint $constraint)
{
throw new ConstraintDefinitionException('Either "choices" or "callback" must be specified on constraint Choice');
}
if ($value === null)
{
return true;
}
if ($constraint->multiple && !is_array($value))
{
throw new UnexpectedTypeException($value, 'array');
}
if ($constraint->callback)
{
if (is_callable(array($this->context->getCurrentClass(), $constraint->callback)))
{
$choices = call_user_func(array($this->context->getCurrentClass(), $constraint->callback));
}
else if (is_callable($constraint->callback))
{
$choices = call_user_func($constraint->callback);
}
else
{
throw new ConstraintDefinitionException('The Choice constraint expects a valid callback');
}
}
else
{
$choices = $constraint->choices;
}
if ($constraint->multiple)
{
foreach ($value as $_value)
{
if (!in_array($_value, $choices, true))
{
$this->setMessage($constraint->message, array('value' => $_value));
return false;
if (!$constraint->choices && !$constraint->callback) {
throw new ConstraintDefinitionException('Either "choices" or "callback" must be specified on constraint Choice');
}
}
$count = count($value);
if ($value === null) {
return true;
}
if ($constraint->min !== null && $count < $constraint->min)
{
$this->setMessage($constraint->minMessage, array('limit' => $constraint->min));
if ($constraint->multiple && !is_array($value)) {
throw new UnexpectedTypeException($value, 'array');
}
return false;
}
if ($constraint->callback) {
if (is_callable(array($this->context->getCurrentClass(), $constraint->callback))) {
$choices = call_user_func(array($this->context->getCurrentClass(), $constraint->callback));
} else if (is_callable($constraint->callback)) {
$choices = call_user_func($constraint->callback);
} else {
throw new ConstraintDefinitionException('The Choice constraint expects a valid callback');
}
} else {
$choices = $constraint->choices;
}
if ($constraint->max !== null && $count > $constraint->max)
{
$this->setMessage($constraint->maxMessage, array('limit' => $constraint->max));
if ($constraint->multiple) {
foreach ($value as $_value) {
if (!in_array($_value, $choices, true)) {
$this->setMessage($constraint->message, array('value' => $_value));
return false;
}
return false;
}
}
$count = count($value);
if ($constraint->min !== null && $count < $constraint->min) {
$this->setMessage($constraint->minMessage, array('limit' => $constraint->min));
return false;
}
if ($constraint->max !== null && $count > $constraint->max) {
$this->setMessage($constraint->maxMessage, array('limit' => $constraint->max));
return false;
}
} elseif (!in_array($value, $choices, true)) {
$this->setMessage($constraint->message, array('value' => $value));
return false;
}
return true;
}
elseif (!in_array($value, $choices, true))
{
$this->setMessage($constraint->message, array('value' => $value));
return false;
}
return true;
}
}

View File

@ -4,14 +4,14 @@ namespace Symfony\Components\Validator\Constraints;
class Collection extends \Symfony\Components\Validator\Constraint
{
public $fields;
public $allowExtraFields = false;
public $allowMissingFields = false;
public $extraFieldsMessage = 'Symfony.Validator.Collection.extraFieldsMessage';
public $missingFieldsMessage = 'Symfony.Validator.Collection.missingFieldsMessage';
public $fields;
public $allowExtraFields = false;
public $allowMissingFields = false;
public $extraFieldsMessage = 'Symfony.Validator.Collection.extraFieldsMessage';
public $missingFieldsMessage = 'Symfony.Validator.Collection.missingFieldsMessage';
public function requiredOptions()
{
return array('fields');
}
public function requiredOptions()
{
return array('fields');
}
}

View File

@ -9,69 +9,59 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
class CollectionValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
}
if (!is_array($value) && !($value instanceof \Traversable && $value instanceof \ArrayAccess))
{
throw new UnexpectedTypeException($value, 'array or Traversable and ArrayAccess');
}
$walker = $this->context->getGraphWalker();
$group = $this->context->getGroup();
$propertyPath = $this->context->getPropertyPath();
$missingFields = array();
$extraFields = array();
foreach ($value as $field => $fieldValue)
{
$extraFields[$field] = $fieldValue;
}
foreach ($constraint->fields as $field => $constraints)
{
if (array_key_exists($field, $value))
{
// cannot simply cast to array, because then the object is converted to an
// array instead of wrapped inside
$constraints = is_array($constraints) ? $constraints : array($constraints);
foreach ($constraints as $constr)
{
$walker->walkConstraint($constr, $value[$field], $group, $propertyPath.'['.$field.']');
if ($value === null) {
return true;
}
unset($extraFields[$field]);
}
else
{
$missingFields[] = $field;
}
if (!is_array($value) && !($value instanceof \Traversable && $value instanceof \ArrayAccess)) {
throw new UnexpectedTypeException($value, 'array or Traversable and ArrayAccess');
}
$walker = $this->context->getGraphWalker();
$group = $this->context->getGroup();
$propertyPath = $this->context->getPropertyPath();
$missingFields = array();
$extraFields = array();
foreach ($value as $field => $fieldValue) {
$extraFields[$field] = $fieldValue;
}
foreach ($constraint->fields as $field => $constraints) {
if (array_key_exists($field, $value)) {
// cannot simply cast to array, because then the object is converted to an
// array instead of wrapped inside
$constraints = is_array($constraints) ? $constraints : array($constraints);
foreach ($constraints as $constr) {
$walker->walkConstraint($constr, $value[$field], $group, $propertyPath.'['.$field.']');
}
unset($extraFields[$field]);
} else {
$missingFields[] = $field;
}
}
if (count($extraFields) > 0 && !$constraint->allowExtraFields) {
$this->setMessage($constraint->extraFieldsMessage, array(
'fields' => '"'.implode('", "', array_keys($extraFields)).'"'
));
return false;
}
if (count($missingFields) > 0 && !$constraint->allowMissingFields) {
$this->setMessage($constraint->missingFieldsMessage, array(
'fields' => '"'.implode('", "', $missingFields).'"'
));
return false;
}
return true;
}
if (count($extraFields) > 0 && !$constraint->allowExtraFields)
{
$this->setMessage($constraint->extraFieldsMessage, array(
'fields' => '"'.implode('", "', array_keys($extraFields)).'"'
));
return false;
}
if (count($missingFields) > 0 && !$constraint->allowMissingFields)
{
$this->setMessage($constraint->missingFieldsMessage, array(
'fields' => '"'.implode('", "', $missingFields).'"'
));
return false;
}
return true;
}
}

View File

@ -4,5 +4,5 @@ namespace Symfony\Components\Validator\Constraints;
class Date extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.Date.message';
public $message = 'Symfony.Validator.Date.message';
}

View File

@ -4,5 +4,5 @@ namespace Symfony\Components\Validator\Constraints;
class DateTime extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.DateTime.message';
public $message = 'Symfony.Validator.DateTime.message';
}

View File

@ -8,29 +8,26 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
class DateTimeValidator extends ConstraintValidator
{
const PATTERN = '/^(\d{4})-(\d{2})-(\d{2}) (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/';
const PATTERN = '/^(\d{4})-(\d{2})-(\d{2}) (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/';
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
if ($value === null) {
return true;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString()'))) {
throw new UnexpectedTypeException($value, 'string');
}
$value = (string)$value;
if (!preg_match(self::PATTERN, $value, $matches)) {
$this->setMessage($constraint->message, array('value' => $value));
return false;
}
return checkdate($matches[2], $matches[3], $matches[1]);
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString()')))
{
throw new UnexpectedTypeException($value, 'string');
}
$value = (string)$value;
if (!preg_match(self::PATTERN, $value, $matches))
{
$this->setMessage($constraint->message, array('value' => $value));
return false;
}
return checkdate($matches[2], $matches[3], $matches[1]);
}
}

View File

@ -8,29 +8,26 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
class DateValidator extends ConstraintValidator
{
const PATTERN = '/^(\d{4})-(\d{2})-(\d{2})$/';
const PATTERN = '/^(\d{4})-(\d{2})-(\d{2})$/';
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
if ($value === null) {
return true;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString()'))) {
throw new UnexpectedTypeException($value, 'string');
}
$value = (string)$value;
if (!preg_match(self::PATTERN, $value, $matches)) {
$this->setMessage($constraint->message, array('value' => $value));
return false;
}
return checkdate($matches[2], $matches[3], $matches[1]);
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString()')))
{
throw new UnexpectedTypeException($value, 'string');
}
$value = (string)$value;
if (!preg_match(self::PATTERN, $value, $matches))
{
$this->setMessage($constraint->message, array('value' => $value));
return false;
}
return checkdate($matches[2], $matches[3], $matches[1]);
}
}

View File

@ -4,6 +4,6 @@ namespace Symfony\Components\Validator\Constraints;
class Email extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.Email.message';
public $checkMX = false;
public $message = 'Symfony.Validator.Email.message';
public $checkMX = false;
}

View File

@ -8,81 +8,71 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
class EmailValidator extends ConstraintValidator
{
const PATTERN = '/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i';
const PATTERN = '/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i';
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString()')))
{
throw new UnexpectedTypeException($value, 'string');
}
$value = (string)$value;
if (!preg_match(self::PATTERN, $value))
{
$this->setMessage($constraint->message, array('value' => $value));
return false;
}
if ($constraint->checkMX)
{
$host = substr($value, strpos($value, '@'));
if (!$this->checkMX($host))
{
$this->setMessage($constraint->message, array('value' => $value));
return false;
}
}
return true;
}
/**
* Check DNA Records for MX type (from Doctrine EmailValidator)
*
* @param string $host Host name
* @return boolean
* @licence This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
*/
private function checkMX($host)
{
// We have different behavior here depending of OS and PHP version
if (strtolower(substr(PHP_OS, 0, 3)) == 'win' && version_compare(PHP_VERSION, '5.3.0', '<')) {
$output = array();
@exec('nslookup -type=MX '.escapeshellcmd($host) . ' 2>&1', $output);
if (empty($output))
{
throw new ValidatorError('Unable to execute DNS lookup. Are you sure PHP can call exec()?');
}
foreach ($output as $line)
{
if (preg_match('/^'.$host.'/', $line))
{
return true;
if ($value === null) {
return true;
}
}
return false;
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString()'))) {
throw new UnexpectedTypeException($value, 'string');
}
$value = (string)$value;
if (!preg_match(self::PATTERN, $value)) {
$this->setMessage($constraint->message, array('value' => $value));
return false;
}
if ($constraint->checkMX) {
$host = substr($value, strpos($value, '@'));
if (!$this->checkMX($host)) {
$this->setMessage($constraint->message, array('value' => $value));
return false;
}
}
return true;
}
else if (function_exists('checkdnsrr'))
/**
* Check DNA Records for MX type (from Doctrine EmailValidator)
*
* @param string $host Host name
* @return boolean
* @licence This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
*/
private function checkMX($host)
{
return checkdnsrr($host, 'MX');
}
// We have different behavior here depending of OS and PHP version
if (strtolower(substr(PHP_OS, 0, 3)) == 'win' && version_compare(PHP_VERSION, '5.3.0', '<')) {
$output = array();
throw new ValidatorError('Could not retrieve DNS record information. Remove check_mx = true to prevent this warning');
}
@exec('nslookup -type=MX '.escapeshellcmd($host) . ' 2>&1', $output);
if (empty($output)) {
throw new ValidatorError('Unable to execute DNS lookup. Are you sure PHP can call exec()?');
}
foreach ($output as $line) {
if (preg_match('/^'.$host.'/', $line)) {
return true;
}
}
return false;
} else if (function_exists('checkdnsrr')) {
return checkdnsrr($host, 'MX');
}
throw new ValidatorError('Could not retrieve DNS record information. Remove check_mx = true to prevent this warning');
}
}

View File

@ -4,10 +4,10 @@ namespace Symfony\Components\Validator\Constraints;
class File extends \Symfony\Components\Validator\Constraint
{
public $maxSize = null;
public $mimeTypes = array();
public $notFoundMessage = 'Symfony.Validator.File.notFoundMessage';
public $notReadableMessage = 'Symfony.Validator.File.notReadableMessage';
public $maxSizeMessage = 'Symfony.Validator.File.maxSizeMessage';
public $mimeTypesMessage = 'Symfony.Validator.File.mimeTypesMessage';
public $maxSize = null;
public $mimeTypes = array();
public $notFoundMessage = 'Symfony.Validator.File.notFoundMessage';
public $notReadableMessage = 'Symfony.Validator.File.notReadableMessage';
public $maxSizeMessage = 'Symfony.Validator.File.maxSizeMessage';
public $mimeTypesMessage = 'Symfony.Validator.File.mimeTypesMessage';
}

View File

@ -10,90 +10,74 @@ use Symfony\Components\File\File;
class FileValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
if ($value === null) {
return true;
}
if (!is_scalar($value) && !$value instanceof File && !(is_object($value) && method_exists($value, '__toString()'))) {
throw new UnexpectedTypeException($value, 'string');
}
$path = $value instanceof File ? $value->getPath() : (string)$value;
if (!file_exists($path)) {
$this->setMessage($constraint->notFoundMessage, array('file' => $path));
return false;
}
if (!is_readable($path)) {
$this->setMessage($constraint->notReadableMessage, array('file' => $path));
return false;
}
if ($constraint->maxSize) {
if (ctype_digit((string)$constraint->maxSize)) {
$size = filesize($path);
$limit = $constraint->maxSize;
$suffix = ' bytes';
} else if (preg_match('/^(\d)k$/', $constraint->maxSize, $matches)) {
$size = round(filesize($path) / 1000, 2);
$limit = $matches[1];
$suffix = ' kB';
} else if (preg_match('/^(\d)M$/', $constraint->maxSize, $matches)) {
$size = round(filesize($path) / 1000000, 2);
$limit = $matches[1];
$suffix = ' MB';
} else {
throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum size', $constraint->maxSize));
}
if ($size > $limit) {
$this->setMessage($constraint->maxSizeMessage, array(
'size' => $size . $suffix,
'limit' => $limit . $suffix,
'file' => $path,
));
return false;
}
}
if ($constraint->mimeTypes) {
if (!$value instanceof File) {
throw new ConstraintValidationException();
}
if (!in_array($value->getMimeType(), (array)$constraint->mimeTypes)) {
$this->setMessage($constraint->mimeTypesMessage, array(
'type' => '"'.$value->getMimeType().'"',
'types' => '"'.implode('", "', (array)$constraint->mimeTypes).'"',
'file' => $path,
));
return false;
}
}
return true;
}
if (!is_scalar($value) && !$value instanceof File && !(is_object($value) && method_exists($value, '__toString()')))
{
throw new UnexpectedTypeException($value, 'string');
}
$path = $value instanceof File ? $value->getPath() : (string)$value;
if (!file_exists($path))
{
$this->setMessage($constraint->notFoundMessage, array('file' => $path));
return false;
}
if (!is_readable($path))
{
$this->setMessage($constraint->notReadableMessage, array('file' => $path));
return false;
}
if ($constraint->maxSize)
{
if (ctype_digit((string)$constraint->maxSize))
{
$size = filesize($path);
$limit = $constraint->maxSize;
$suffix = ' bytes';
}
else if (preg_match('/^(\d)k$/', $constraint->maxSize, $matches))
{
$size = round(filesize($path) / 1000, 2);
$limit = $matches[1];
$suffix = ' kB';
}
else if (preg_match('/^(\d)M$/', $constraint->maxSize, $matches))
{
$size = round(filesize($path) / 1000000, 2);
$limit = $matches[1];
$suffix = ' MB';
}
else
{
throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum size', $constraint->maxSize));
}
if ($size > $limit)
{
$this->setMessage($constraint->maxSizeMessage, array(
'size' => $size . $suffix,
'limit' => $limit . $suffix,
'file' => $path,
));
return false;
}
}
if ($constraint->mimeTypes)
{
if (!$value instanceof File)
{
throw new ConstraintValidationException();
}
if (!in_array($value->getMimeType(), (array)$constraint->mimeTypes))
{
$this->setMessage($constraint->mimeTypesMessage, array(
'type' => '"'.$value->getMimeType().'"',
'types' => '"'.implode('", "', (array)$constraint->mimeTypes).'"',
'file' => $path,
));
return false;
}
}
return true;
}
}

View File

@ -4,22 +4,22 @@ namespace Symfony\Components\Validator\Constraints;
class Max extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.Max.message';
public $limit;
public $message = 'Symfony.Validator.Max.message';
public $limit;
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'limit';
}
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'limit';
}
/**
* {@inheritDoc}
*/
public function requiredOptions()
{
return array('limit');
}
/**
* {@inheritDoc}
*/
public function requiredOptions()
{
return array('limit');
}
}

View File

@ -4,23 +4,23 @@ namespace Symfony\Components\Validator\Constraints;
class MaxLength extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.MaxLength.message';
public $limit;
public $charset = 'UTF-8';
public $message = 'Symfony.Validator.MaxLength.message';
public $limit;
public $charset = 'UTF-8';
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'limit';
}
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'limit';
}
/**
* {@inheritDoc}
*/
public function requiredOptions()
{
return array('limit');
}
/**
* {@inheritDoc}
*/
public function requiredOptions()
{
return array('limit');
}
}

View File

@ -8,32 +8,29 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
class MaxLengthValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
if ($value === null) {
return true;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString()'))) {
throw new UnexpectedTypeException($value, 'string');
}
$value = (string)$value;
$length = function_exists('mb_strlen') ? mb_strlen($value, $constraint->charset) : strlen($value);
if ($length > $constraint->limit) {
$this->setMessage($constraint->message, array(
'value' => $value,
'limit' => $constraint->limit,
));
return false;
}
return true;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString()')))
{
throw new UnexpectedTypeException($value, 'string');
}
$value = (string)$value;
$length = function_exists('mb_strlen') ? mb_strlen($value, $constraint->charset) : strlen($value);
if ($length > $constraint->limit)
{
$this->setMessage($constraint->message, array(
'value' => $value,
'limit' => $constraint->limit,
));
return false;
}
return true;
}
}

View File

@ -8,28 +8,25 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
class MaxValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
if ($value === null) {
return true;
}
if (!is_numeric($value)) {
throw new UnexpectedTypeException($value, 'numeric');
}
if ($value > $constraint->limit) {
$this->setMessage($constraint->message, array(
'value' => $value,
'limit' => $constraint->limit,
));
return false;
}
return true;
}
if (!is_numeric($value))
{
throw new UnexpectedTypeException($value, 'numeric');
}
if ($value > $constraint->limit)
{
$this->setMessage($constraint->message, array(
'value' => $value,
'limit' => $constraint->limit,
));
return false;
}
return true;
}
}

View File

@ -4,22 +4,22 @@ namespace Symfony\Components\Validator\Constraints;
class Min extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.Min.message';
public $limit;
public $message = 'Symfony.Validator.Min.message';
public $limit;
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'limit';
}
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'limit';
}
/**
* {@inheritDoc}
*/
public function requiredOptions()
{
return array('limit');
}
/**
* {@inheritDoc}
*/
public function requiredOptions()
{
return array('limit');
}
}

View File

@ -4,23 +4,23 @@ namespace Symfony\Components\Validator\Constraints;
class MinLength extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.MinLength.message';
public $limit;
public $charset = 'UTF-8';
public $message = 'Symfony.Validator.MinLength.message';
public $limit;
public $charset = 'UTF-8';
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'limit';
}
/**
* {@inheritDoc}
*/
public function defaultOption()
{
return 'limit';
}
/**
* {@inheritDoc}
*/
public function requiredOptions()
{
return array('limit');
}
/**
* {@inheritDoc}
*/
public function requiredOptions()
{
return array('limit');
}
}

View File

@ -8,32 +8,29 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
class MinLengthValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
if ($value === null) {
return true;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString()'))) {
throw new UnexpectedTypeException($value, 'string');
}
$value = (string)$value;
$length = function_exists('mb_strlen') ? mb_strlen($value, $constraint->charset) : strlen($value);
if ($length < $constraint->limit) {
$this->setMessage($constraint->message, array(
'value' => $value,
'limit' => $constraint->limit,
));
return false;
}
return true;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString()')))
{
throw new UnexpectedTypeException($value, 'string');
}
$value = (string)$value;
$length = function_exists('mb_strlen') ? mb_strlen($value, $constraint->charset) : strlen($value);
if ($length < $constraint->limit)
{
$this->setMessage($constraint->message, array(
'value' => $value,
'limit' => $constraint->limit,
));
return false;
}
return true;
}
}

View File

@ -8,28 +8,25 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
class MinValidator extends ConstraintValidator
{
public function isValid($value, Constraint $constraint)
{
if ($value === null)
public function isValid($value, Constraint $constraint)
{
return true;
if ($value === null) {
return true;
}
if (!is_numeric($value)) {
throw new UnexpectedTypeException($value, 'numeric');
}
if ($value < $constraint->limit) {
$this->setMessage($constraint->message, array(
'value' => $value,
'limit' => $constraint->limit,
));
return false;
}
return true;
}
if (!is_numeric($value))
{
throw new UnexpectedTypeException($value, 'numeric');
}
if ($value < $constraint->limit)
{
$this->setMessage($constraint->message, array(
'value' => $value,
'limit' => $constraint->limit,
));
return false;
}
return true;
}
}

View File

@ -4,5 +4,5 @@ namespace Symfony\Components\Validator\Constraints;
class NotBlank extends \Symfony\Components\Validator\Constraint
{
public $message = 'Symfony.Validator.NotBlank.message';
public $message = 'Symfony.Validator.NotBlank.message';
}

Some files were not shown because too many files have changed in this diff Show More