Adapted Form, Validator, File and I18N component to new coding standards
This commit is contained in:
parent
ee83847cec
commit
bcd4b6d140
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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('/&([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('/&([a-z]+|(#\d+)|(#x[\da-f]+));/i', '&$1;', $escaped);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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).' %';
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 !== '';
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -6,5 +6,5 @@ use Symfony\Components\Validator\Constraint;
|
|||
|
||||
interface ConstraintValidatorFactoryInterface
|
||||
{
|
||||
public function getInstance(Constraint $constraint);
|
||||
public function getInstance(Constraint $constraint);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
}
|
|
@ -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';
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
Reference in New Issue