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
|
class AccessDeniedException extends FileException
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param string $path The path to the accessed file
|
* @param string $path The path to the accessed file
|
||||||
*/
|
*/
|
||||||
public function __construct($path)
|
public function __construct($path)
|
||||||
{
|
{
|
||||||
parent::__construct(sprintf('The file %s could not be accessed', $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
|
class FileNotFoundException extends FileException
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param string $path The path to the file that was not found
|
* @param string $path The path to the file that was not found
|
||||||
*/
|
*/
|
||||||
public function __construct($path)
|
public function __construct($path)
|
||||||
{
|
{
|
||||||
parent::__construct(sprintf('The file %s does not exist', $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
|
class ContentTypeMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Returns whether this guesser is supported on the corrent OS/PHP setup
|
* Returns whether this guesser is supported on the corrent OS/PHP setup
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
static public function isSupported()
|
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))
|
|
||||||
{
|
{
|
||||||
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
|
class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Guesses the mime type of the file with the given path
|
* Guesses the mime type of the file with the given path
|
||||||
*
|
*
|
||||||
* @see MimeTypeGuesserInterface::guess()
|
* @see MimeTypeGuesserInterface::guess()
|
||||||
*/
|
*/
|
||||||
public function guess($path)
|
public function guess($path)
|
||||||
{
|
|
||||||
if (!is_file($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
|
class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Returns whether this guesser is supported on the corrent OS/PHP setup
|
* Returns whether this guesser is supported on the corrent OS/PHP setup
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
static public function isSupported()
|
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))
|
|
||||||
{
|
{
|
||||||
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
|
class MimeTypeGuesser implements MimeTypeGuesserInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The singleton instance
|
* The singleton instance
|
||||||
* @var MimeTypeGuesser
|
* @var MimeTypeGuesser
|
||||||
*/
|
*/
|
||||||
static private $instance = null;
|
static private $instance = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All registered MimeTypeGuesserInterface instances
|
* All registered MimeTypeGuesserInterface instances
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $guessers = array();
|
protected $guessers = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the singleton instance
|
* Returns the singleton instance
|
||||||
*
|
*
|
||||||
* @return MimeTypeGuesser
|
* @return MimeTypeGuesser
|
||||||
*/
|
*/
|
||||||
static public function getInstance()
|
static public function getInstance()
|
||||||
{
|
|
||||||
if (is_null(self::$instance))
|
|
||||||
{
|
{
|
||||||
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()
|
||||||
* Registers all natively provided mime type guessers
|
|
||||||
*/
|
|
||||||
private function __construct()
|
|
||||||
{
|
|
||||||
$this->register(new FileBinaryMimeTypeGuesser());
|
|
||||||
|
|
||||||
if (ContentTypeMimeTypeGuesser::isSupported())
|
|
||||||
{
|
{
|
||||||
$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());
|
array_unshift($this->guessers, $guesser);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
interface MimeTypeGuesserInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Guesses the mime type of the file with the given path
|
* Guesses the mime type of the file with the given path
|
||||||
*
|
*
|
||||||
* @param string $path The path to the file
|
* @param string $path The path to the file
|
||||||
* @return string The mime type or NULL, if none could be guessed
|
* @return string The mime type or NULL, if none could be guessed
|
||||||
* @throws FileNotFoundException If the file does not exist
|
* @throws FileNotFoundException If the file does not exist
|
||||||
* @throws AccessDeniedException If the file could not be read
|
* @throws AccessDeniedException If the file could not be read
|
||||||
*/
|
*/
|
||||||
public function guess($path);
|
public function guess($path);
|
||||||
}
|
}
|
@ -20,111 +20,103 @@ use Symfony\Components\File\Exception\FileException;
|
|||||||
*/
|
*/
|
||||||
class UploadedFile extends File
|
class UploadedFile extends File
|
||||||
{
|
{
|
||||||
protected $originalName;
|
protected $originalName;
|
||||||
protected $mimeType;
|
protected $mimeType;
|
||||||
protected $size;
|
protected $size;
|
||||||
protected $error;
|
protected $error;
|
||||||
protected $moved = false;
|
protected $moved = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accepts the information of the uploaded file as provided by the PHP
|
* Accepts the information of the uploaded file as provided by the PHP
|
||||||
* global $_FILES.
|
* global $_FILES.
|
||||||
*
|
*
|
||||||
* @param string $tmpName The full temporary path to the file
|
* @param string $tmpName The full temporary path to the file
|
||||||
* @param string $name The original file name
|
* @param string $name The original file name
|
||||||
* @param string $type The type of the file as provided by PHP
|
* @param string $type The type of the file as provided by PHP
|
||||||
* @param integer $size The file size
|
* @param integer $size The file size
|
||||||
* @param string $error The error constant of the upload. Should be
|
* @param string $error The error constant of the upload. Should be
|
||||||
* one of PHP's UPLOAD_XXX constants.
|
* one of PHP's UPLOAD_XXX constants.
|
||||||
*/
|
*/
|
||||||
public function __construct($path, $originalName, $mimeType, $size, $error)
|
public function __construct($path, $originalName, $mimeType, $size, $error)
|
||||||
{
|
|
||||||
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')));
|
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);
|
/**
|
||||||
|
* Returns the mime type of the file.
|
||||||
if (is_null($error))
|
*
|
||||||
|
* 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;
|
* Returns the upload error.
|
||||||
$this->size = $size;
|
*
|
||||||
$this->error = $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
|
||||||
* Returns the mime type of the file.
|
*/
|
||||||
*
|
public function getError()
|
||||||
* 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))
|
|
||||||
{
|
{
|
||||||
$mimeType = $this->mimeType;
|
return $this->error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $mimeType;
|
/**
|
||||||
}
|
* Moves the file to a new location.
|
||||||
|
*
|
||||||
/**
|
* @param string $newPath
|
||||||
* Returns the original file name including its extension.
|
*/
|
||||||
*
|
public function move($newPath)
|
||||||
* @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)
|
|
||||||
{
|
{
|
||||||
if (!move_uploaded_file($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));
|
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
|
class BirthdayField extends DateField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
{
|
||||||
$currentYear = date('Y');
|
$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
|
class CheckboxField extends ToggleField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function render(array $attributes = array())
|
public function render(array $attributes = array())
|
||||||
{
|
{
|
||||||
return parent::render(array_merge(array(
|
return parent::render(array_merge(array(
|
||||||
'type' => 'checkbox',
|
'type' => 'checkbox',
|
||||||
), $attributes));
|
), $attributes));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,274 +15,237 @@ use Symfony\Components\Form\ValueTransformer\BooleanToStringTransformer;
|
|||||||
*/
|
*/
|
||||||
class ChoiceField extends HybridField
|
class ChoiceField extends HybridField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Stores the preferred choices with the choices as keys
|
* Stores the preferred choices with the choices as keys
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $preferredChoices = array();
|
protected $preferredChoices = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
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)
|
|
||||||
{
|
{
|
||||||
$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))
|
if (count($this->getOption('preferred_choices')) > 0) {
|
||||||
{
|
$this->preferredChoices = array_flip($this->getOption('preferred_choices'));
|
||||||
//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('expanded'))
|
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));
|
||||||
$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 ($this->getOption('translate_choices'))
|
if ($this->getOption('expanded')) {
|
||||||
{
|
$this->setFieldMode(self::GROUP);
|
||||||
$option = $this->translate($option);
|
|
||||||
}
|
|
||||||
|
|
||||||
$options[] = $this->generator->contentTag(
|
$choices = $this->getOption('choices');
|
||||||
'option',
|
|
||||||
$this->generator->escape($option),
|
foreach ($this->getOption('preferred_choices') as $choice) {
|
||||||
$attributes
|
$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
|
class CollectionField extends FieldGroup
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The prototype for the inner fields
|
* The prototype for the inner fields
|
||||||
* @var FieldInterface
|
* @var FieldInterface
|
||||||
*/
|
*/
|
||||||
protected $prototype;
|
protected $prototype;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repeats the given field twice to verify the user's input
|
* Repeats the given field twice to verify the user's input
|
||||||
*
|
*
|
||||||
* @param FieldInterface $innerField
|
* @param FieldInterface $innerField
|
||||||
*/
|
*/
|
||||||
public function __construct(FieldInterface $innerField, array $options = array())
|
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'))
|
|
||||||
{
|
{
|
||||||
$field = $this->newField('$$key$$', null);
|
$this->prototype = $innerField;
|
||||||
// TESTME
|
|
||||||
$field->setRequired(false);
|
|
||||||
$this->add($field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setData($collection)
|
parent::__construct($innerField->getKey(), $options);
|
||||||
{
|
|
||||||
if (!is_array($collection) && !$collection instanceof Traversable)
|
|
||||||
{
|
|
||||||
throw new UnexpectedTypeException('The data must be an array');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 setData($collection)
|
||||||
}
|
|
||||||
|
|
||||||
public function bind($taintedData)
|
|
||||||
{
|
|
||||||
if (is_null($taintedData))
|
|
||||||
{
|
{
|
||||||
$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$$')
|
if (is_null($taintedData)) {
|
||||||
{
|
$taintedData = array();
|
||||||
$this->remove($name);
|
}
|
||||||
}
|
|
||||||
|
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'))
|
$field = clone $this->prototype;
|
||||||
{
|
$field->setKey($key);
|
||||||
$this->add($this->newField($name, $name));
|
$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
|
abstract class Configurable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The options and their values
|
* The options and their values
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $options = array();
|
private $options = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The names of the valid options
|
* The names of the valid options
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $knownOptions = array();
|
private $knownOptions = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The names of the required options
|
* The names of the required options
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $requiredOptions = array();
|
private $requiredOptions = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The allowed values for each option
|
* The allowed values for each option
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $allowedValues = array();
|
private $allowedValues = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads, validates and stores the given options
|
* Reads, validates and stores the given options
|
||||||
*
|
*
|
||||||
* @param array $options
|
* @param array $options
|
||||||
*/
|
*/
|
||||||
public function __construct(array $options = array())
|
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))
|
|
||||||
{
|
{
|
||||||
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.
|
* Adds a new option value with a default value.
|
||||||
*
|
*
|
||||||
* @param string $name The option name
|
* @param string $name The option name
|
||||||
*/
|
* @param mixed $value The default value
|
||||||
protected function addRequiredOption($name, array $allowedValues = array())
|
*/
|
||||||
{
|
protected function addOption($name, $value = null, 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));
|
$this->knownOptions[$name] = true;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (!array_key_exists($name, $this->options)) {
|
||||||
* Returns true if the option exists.
|
$this->options[$name] = $value;
|
||||||
*
|
}
|
||||||
* @param string $name The option name
|
|
||||||
*
|
if (count($allowedValues) > 0 && !in_array($this->options[$name], $allowedValues)) {
|
||||||
* @return bool true if the option is set, false otherwise
|
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));
|
||||||
*/
|
}
|
||||||
public function hasOption($name)
|
}
|
||||||
{
|
|
||||||
return isset($this->options[$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
|
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
|
class ValidatorConfigurator implements ConfiguratorInterface
|
||||||
{
|
{
|
||||||
protected $metaData = null;
|
protected $metaData = null;
|
||||||
protected $classMetaData = null;
|
protected $classMetaData = null;
|
||||||
|
|
||||||
public function __construct(MetaDataInterface $metaData)
|
public function __construct(MetaDataInterface $metaData)
|
||||||
{
|
{
|
||||||
$this->metaData = $metaData;
|
$this->metaData = $metaData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function initialize($object)
|
public function initialize($object)
|
||||||
{
|
{
|
||||||
$this->classMetaData = $this->metaData->getClassMetaData(get_class($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)
|
public function isRequired($fieldName)
|
||||||
{
|
{
|
||||||
return $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotNull')
|
return $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotNull')
|
||||||
|| $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotEmpty');
|
|| $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotEmpty');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,244 +19,224 @@ use Symfony\Components\Form\ValueTransformer\DateTimeToArrayTransformer;
|
|||||||
|
|
||||||
class DateField extends HybridField
|
class DateField extends HybridField
|
||||||
{
|
{
|
||||||
const FULL = 'full';
|
const FULL = 'full';
|
||||||
const LONG = 'long';
|
const LONG = 'long';
|
||||||
const MEDIUM = 'medium';
|
const MEDIUM = 'medium';
|
||||||
const SHORT = 'short';
|
const SHORT = 'short';
|
||||||
|
|
||||||
const DATETIME = 'datetime';
|
const DATETIME = 'datetime';
|
||||||
const STRING = 'string';
|
const STRING = 'string';
|
||||||
const TIMESTAMP = 'timestamp';
|
const TIMESTAMP = 'timestamp';
|
||||||
const RAW = 'raw';
|
const RAW = 'raw';
|
||||||
|
|
||||||
const INPUT = 'input';
|
const INPUT = 'input';
|
||||||
const CHOICE = 'choice';
|
const CHOICE = 'choice';
|
||||||
|
|
||||||
protected static $formats = array(
|
protected static $formats = array(
|
||||||
self::FULL,
|
self::FULL,
|
||||||
self::LONG,
|
self::LONG,
|
||||||
self::MEDIUM,
|
self::MEDIUM,
|
||||||
self::SHORT,
|
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
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$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(
|
$this->addOption('years', range(date('Y') - 5, date('Y') + 5));
|
||||||
'input_timezone' => $this->getOption('data_timezone'),
|
$this->addOption('months', range(1, 12));
|
||||||
'output_timezone' => $this->getOption('data_timezone'),
|
$this->addOption('days', range(1, 31));
|
||||||
'format' => 'Y-m-d',
|
$this->addOption('format', self::MEDIUM, self::$formats);
|
||||||
));
|
$this->addOption('type', self::DATETIME, self::$types);
|
||||||
}
|
$this->addOption('data_timezone', 'UTC');
|
||||||
else if ($this->getOption('type') === self::TIMESTAMP)
|
$this->addOption('user_timezone', 'UTC');
|
||||||
{
|
$this->addOption('widget', self::CHOICE, self::$widgets);
|
||||||
$transformers[] = new TimestampToDateTimeTransformer(array(
|
$this->addOption('pattern');
|
||||||
'output_timezone' => $this->getOption('data_timezone'),
|
|
||||||
'input_timezone' => $this->getOption('data_timezone'),
|
$this->formatter = new \IntlDateFormatter(
|
||||||
));
|
$this->locale,
|
||||||
}
|
self::$intlFormats[$this->getOption('format')],
|
||||||
else if ($this->getOption('type') === self::RAW)
|
\IntlDateFormatter::NONE
|
||||||
{
|
);
|
||||||
$transformers[] = new ReversedTransformer(new DateTimeToArrayTransformer(array(
|
|
||||||
'input_timezone' => $this->getOption('data_timezone'),
|
$transformers = array();
|
||||||
'output_timezone' => $this->getOption('data_timezone'),
|
|
||||||
'fields' => array('year', 'month', 'day'),
|
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(
|
$choices = 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);
|
foreach ($values as $value) {
|
||||||
}
|
$choices[$value] = str_pad($value, $padLength, '0', STR_PAD_LEFT);
|
||||||
else
|
}
|
||||||
{
|
|
||||||
$transformers[] = new DateTimeToArrayTransformer(array(
|
|
||||||
'input_timezone' => $this->getOption('data_timezone'),
|
|
||||||
'output_timezone' => $this->getOption('user_timezone'),
|
|
||||||
));
|
|
||||||
|
|
||||||
$this->setFieldMode(self::GROUP);
|
return $choices;
|
||||||
|
|
||||||
$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)
|
/**
|
||||||
|
* 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();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (preg_match('/M+/', $pattern, $matches)) {
|
||||||
* Generates an array of choices for the given values
|
$this->formatter->setPattern($matches[0]);
|
||||||
*
|
$choices = array();
|
||||||
* 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)
|
foreach ($months as $month) {
|
||||||
{
|
$choices[$month] = $this->formatter->format(gmmktime(0, 0, 0, $month));
|
||||||
$choices[$value] = str_pad($value, $padLength, '0', STR_PAD_LEFT);
|
}
|
||||||
|
|
||||||
|
$this->formatter->setPattern($pattern);
|
||||||
|
} else {
|
||||||
|
$choices = $this->generatePaddedChoices($months, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $choices;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $choices;
|
/**
|
||||||
}
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
/**
|
public function render(array $attributes = array())
|
||||||
* 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))
|
|
||||||
{
|
{
|
||||||
$this->formatter->setPattern($matches[0]);
|
if ($this->getOption('widget') === self::INPUT) {
|
||||||
$choices = array();
|
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)
|
return str_replace(array('%year%', '%month%', '%day%'), array(
|
||||||
{
|
$this->get('year')->render($attributes),
|
||||||
$choices[$month] = $this->formatter->format(gmmktime(0, 0, 0, $month));
|
$this->get('month')->render($attributes),
|
||||||
}
|
$this->get('day')->render($attributes),
|
||||||
|
), $pattern);
|
||||||
$this->formatter->setPattern($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
|
class DateTimeField extends FieldGroup
|
||||||
{
|
{
|
||||||
const DATETIME = 'datetime';
|
const DATETIME = 'datetime';
|
||||||
const STRING = 'string';
|
const STRING = 'string';
|
||||||
const TIMESTAMP = 'timestamp';
|
const TIMESTAMP = 'timestamp';
|
||||||
|
|
||||||
protected static $types = array(
|
protected static $types = array(
|
||||||
self::DATETIME,
|
self::DATETIME,
|
||||||
self::STRING,
|
self::STRING,
|
||||||
self::TIMESTAMP,
|
self::TIMESTAMP,
|
||||||
);
|
);
|
||||||
|
|
||||||
protected static $dateWidgets = array(
|
protected static $dateWidgets = array(
|
||||||
DateField::CHOICE,
|
DateField::CHOICE,
|
||||||
DateField::INPUT,
|
DateField::INPUT,
|
||||||
);
|
);
|
||||||
|
|
||||||
protected static $timeWidgets = array(
|
protected static $timeWidgets = array(
|
||||||
TimeField::CHOICE,
|
TimeField::CHOICE,
|
||||||
TimeField::INPUT,
|
TimeField::INPUT,
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function configure()
|
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)
|
|
||||||
{
|
{
|
||||||
$transformers[] = new StringToDateTimeTransformer(array(
|
$this->addOption('years', range(date('Y') - 5, date('Y') + 5));
|
||||||
'input_timezone' => $this->getOption('data_timezone'),
|
$this->addOption('months', range(1, 12));
|
||||||
'output_timezone' => $this->getOption('data_timezone'),
|
$this->addOption('days', range(1, 31));
|
||||||
));
|
$this->addOption('hours', range(0, 23));
|
||||||
}
|
$this->addOption('minutes', range(0, 59));
|
||||||
else if ($this->getOption('type') == self::TIMESTAMP)
|
$this->addOption('seconds', range(0, 59));
|
||||||
{
|
$this->addOption('data_timezone', 'UTC');
|
||||||
$transformers[] = new TimestampToDateTimeTransformer(array(
|
$this->addOption('user_timezone', 'UTC');
|
||||||
'input_timezone' => $this->getOption('data_timezone'),
|
$this->addOption('date_widget', DateField::INPUT, self::$dateWidgets);
|
||||||
'output_timezone' => $this->getOption('data_timezone'),
|
$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'),
|
* {@inheritDoc}
|
||||||
'output_timezone' => $this->getOption('user_timezone'),
|
*/
|
||||||
));
|
protected function transform($value)
|
||||||
|
{
|
||||||
|
$value = parent::transform($value);
|
||||||
|
|
||||||
$this->setValueTransformer(new ValueTransformerChain($transformers));
|
return array('date' => $value, 'time' => $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function transform($value)
|
protected function reverseTransform($value)
|
||||||
{
|
{
|
||||||
$value = parent::transform($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);
|
||||||
|
|
||||||
/**
|
return $html;
|
||||||
* {@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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,17 +4,17 @@ namespace Symfony\Components\Form\Exception;
|
|||||||
|
|
||||||
class InvalidOptionsException extends FormException
|
class InvalidOptionsException extends FormException
|
||||||
{
|
{
|
||||||
private $options;
|
private $options;
|
||||||
|
|
||||||
public function __construct($message, array $options)
|
public function __construct($message, array $options)
|
||||||
{
|
{
|
||||||
parent::__construct($message);
|
parent::__construct($message);
|
||||||
|
|
||||||
$this->options = $options;
|
$this->options = $options;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOptions()
|
public function getOptions()
|
||||||
{
|
{
|
||||||
return $this->options;
|
return $this->options;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,17 +4,17 @@ namespace Symfony\Components\Form\Exception;
|
|||||||
|
|
||||||
class MissingOptionsException extends FormException
|
class MissingOptionsException extends FormException
|
||||||
{
|
{
|
||||||
private $options;
|
private $options;
|
||||||
|
|
||||||
public function __construct($message, array $options)
|
public function __construct($message, array $options)
|
||||||
{
|
{
|
||||||
parent::__construct($message);
|
parent::__construct($message);
|
||||||
|
|
||||||
$this->options = $options;
|
$this->options = $options;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOptions()
|
public function getOptions()
|
||||||
{
|
{
|
||||||
return $this->options;
|
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
|
interface FieldInterface extends Localizable, Translatable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Marks a constraint violation in a form field
|
* Marks a constraint violation in a form field
|
||||||
* @var integer
|
* @var integer
|
||||||
*/
|
*/
|
||||||
const FIELD_ERROR = 0;
|
const FIELD_ERROR = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks a constraint violation in the data of a form field
|
* Marks a constraint violation in the data of a form field
|
||||||
* @var integer
|
* @var integer
|
||||||
*/
|
*/
|
||||||
const DATA_ERROR = 1;
|
const DATA_ERROR = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clones this field.
|
* Clones this field.
|
||||||
*/
|
*/
|
||||||
public function __clone();
|
public function __clone();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the parent field.
|
* Sets the parent field.
|
||||||
*
|
*
|
||||||
* @param FieldInterface $parent The parent field
|
* @param FieldInterface $parent The parent field
|
||||||
*/
|
*/
|
||||||
public function setParent(FieldInterface $parent = null);
|
public function setParent(FieldInterface $parent = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the key by which the field is identified in field groups.
|
* 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
|
* 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.
|
* called for the first time, this method should throw an exception.
|
||||||
*
|
*
|
||||||
* @param string $key The key of the field
|
* @param string $key The key of the field
|
||||||
* @throws BadMethodCallException When the field already has a parent
|
* @throws BadMethodCallException When the field already has a parent
|
||||||
*/
|
*/
|
||||||
public function setKey($key);
|
public function setKey($key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the key by which the field is identified in field groups.
|
* Returns the key by which the field is identified in field groups.
|
||||||
*
|
*
|
||||||
* @return string The key of the field.
|
* @return string The key of the field.
|
||||||
*/
|
*/
|
||||||
public function getKey();
|
public function getKey();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the field.
|
* Returns the name of the field.
|
||||||
*
|
*
|
||||||
* @return string When the field has no parent, the name is equal to its
|
* @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
|
* 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
|
* the parent's name and the field's key, where the field's
|
||||||
* key is wrapped in squared brackets
|
* key is wrapped in squared brackets
|
||||||
* (e.g. "parent_name[field_key]")
|
* (e.g. "parent_name[field_key]")
|
||||||
*/
|
*/
|
||||||
public function getName();
|
public function getName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ID of the field.
|
* Returns the ID of the field.
|
||||||
*
|
*
|
||||||
* @return string The ID of a field is equal to its name, where all
|
* @return string The ID of a field is equal to its name, where all
|
||||||
* sequences of squared brackets are replaced by a single
|
* sequences of squared brackets are replaced by a single
|
||||||
* underscore (e.g. if the name is "parent_name[field_key]",
|
* underscore (e.g. if the name is "parent_name[field_key]",
|
||||||
* the ID is "parent_name_field_key").
|
* the ID is "parent_name_field_key").
|
||||||
*/
|
*/
|
||||||
public function getId();
|
public function getId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the property path
|
* Sets the property path
|
||||||
*
|
*
|
||||||
* The property path determines the property or a sequence of properties
|
* The property path determines the property or a sequence of properties
|
||||||
* that a field updates in the data of the field group.
|
* that a field updates in the data of the field group.
|
||||||
*
|
*
|
||||||
* @param string $propertyPath
|
* @param string $propertyPath
|
||||||
*/
|
*/
|
||||||
public function setPropertyPath($propertyPath);
|
public function setPropertyPath($propertyPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the property path of the field
|
* Returns the property path of the field
|
||||||
*
|
*
|
||||||
* @return PropertyPath
|
* @return PropertyPath
|
||||||
*/
|
*/
|
||||||
public function getPropertyPath();
|
public function getPropertyPath();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a property value of the object into the field
|
* Writes a property value of the object into the field
|
||||||
*
|
*
|
||||||
* The chosen property is determined by the field's property path.
|
* The chosen property is determined by the field's property path.
|
||||||
*
|
*
|
||||||
* @param array|object $objectOrArray
|
* @param array|object $objectOrArray
|
||||||
*/
|
*/
|
||||||
public function updateFromObject(&$objectOrArray);
|
public function updateFromObject(&$objectOrArray);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a the field value into a property of the object
|
* Writes a the field value into a property of the object
|
||||||
*
|
*
|
||||||
* The chosen property is determined by the field's property path.
|
* The chosen property is determined by the field's property path.
|
||||||
*
|
*
|
||||||
* @param array|object $objectOrArray
|
* @param array|object $objectOrArray
|
||||||
*/
|
*/
|
||||||
public function updateObject(&$objectOrArray);
|
public function updateObject(&$objectOrArray);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the normalized data of the field.
|
* Returns the normalized data of the field.
|
||||||
*
|
*
|
||||||
* @return mixed When the field is not bound, the default data is returned.
|
* @return mixed When the field is not bound, the default data is returned.
|
||||||
* When the field is bound, the normalized bound data is
|
* When the field is bound, the normalized bound data is
|
||||||
* returned if the field is valid, null otherwise.
|
* returned if the field is valid, null otherwise.
|
||||||
*/
|
*/
|
||||||
public function getData();
|
public function getData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the data of the field as it is displayed to the user.
|
* Returns the data of the field as it is displayed to the user.
|
||||||
*
|
*
|
||||||
* @return string|array When the field is not bound, the transformed
|
* @return string|array When the field is not bound, the transformed
|
||||||
* default data is returned. When the field is bound,
|
* default data is returned. When the field is bound,
|
||||||
* the bound data is returned.
|
* the bound data is returned.
|
||||||
*/
|
*/
|
||||||
public function getDisplayedData();
|
public function getDisplayedData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the default data
|
* Sets the default data
|
||||||
*
|
*
|
||||||
* @param mixed $default The default data
|
* @param mixed $default The default data
|
||||||
* @throws UnexpectedTypeException If the default data is invalid
|
* @throws UnexpectedTypeException If the default data is invalid
|
||||||
*/
|
*/
|
||||||
public function setData($default);
|
public function setData($default);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds POST data to the field, transforms and validates it.
|
* Binds POST data to the field, transforms and validates it.
|
||||||
*
|
*
|
||||||
* @param string|array $taintedData The POST data
|
* @param string|array $taintedData The POST data
|
||||||
* @return boolean Whether the form is valid
|
* @return boolean Whether the form is valid
|
||||||
* @throws InvalidConfigurationException when the field is not configured
|
* @throws InvalidConfigurationException when the field is not configured
|
||||||
* correctly
|
* correctly
|
||||||
*/
|
*/
|
||||||
public function bind($taintedData);
|
public function bind($taintedData);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively adds constraint violations to the fields
|
* Recursively adds constraint violations to the fields
|
||||||
*
|
*
|
||||||
* Violations in the form fields usually have property paths like:
|
* Violations in the form fields usually have property paths like:
|
||||||
*
|
*
|
||||||
* <code>
|
* <code>
|
||||||
* iterator[firstName].data
|
* iterator[firstName].data
|
||||||
* iterator[firstName].displayedData
|
* iterator[firstName].displayedData
|
||||||
* iterator[Address].iterator[street].displayedData
|
* iterator[Address].iterator[street].displayedData
|
||||||
* ...
|
* ...
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* Violations in the form data usually have property paths like:
|
* Violations in the form data usually have property paths like:
|
||||||
*
|
*
|
||||||
* <code>
|
* <code>
|
||||||
* data.firstName
|
* data.firstName
|
||||||
* data.Address.street
|
* data.Address.street
|
||||||
* ...
|
* ...
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* @param FieldInterface $field
|
* @param FieldInterface $field
|
||||||
* @param PropertyPath $path
|
* @param PropertyPath $path
|
||||||
* @param ConstraintViolation$violation
|
* @param ConstraintViolation$violation
|
||||||
*/
|
*/
|
||||||
public function addError($message, PropertyPath $path = null, $type = null);
|
public function addError($message, PropertyPath $path = null, $type = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders this field.
|
* Renders this field.
|
||||||
*
|
*
|
||||||
* @param array $attributes The attributes to include in the rendered
|
* @param array $attributes The attributes to include in the rendered
|
||||||
* output
|
* output
|
||||||
* @return string The rendered output of this field
|
* @return string The rendered output of this field
|
||||||
*/
|
*/
|
||||||
public function render(array $attributes = array());
|
public function render(array $attributes = array());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the errors of this field.
|
* Renders the errors of this field.
|
||||||
*
|
*
|
||||||
* @return string The rendered output of the field errors
|
* @return string The rendered output of the field errors
|
||||||
*/
|
*/
|
||||||
public function renderErrors();
|
public function renderErrors();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the field is bound.
|
* Returns whether the field is bound.
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function isBound();
|
public function isBound();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the field is valid.
|
* Returns whether the field is valid.
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function isValid();
|
public function isValid();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the field requires a multipart form.
|
* Returns whether the field requires a multipart form.
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function isMultipart();
|
public function isMultipart();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the field is required to be filled out.
|
* Returns whether the field is required to be filled out.
|
||||||
*
|
*
|
||||||
* If the field has a parent and the parent is not required, this method
|
* If the field has a parent and the parent is not required, this method
|
||||||
* will always return false. Otherwise the value set with setRequired()
|
* will always return false. Otherwise the value set with setRequired()
|
||||||
* is returned.
|
* is returned.
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function isRequired();
|
public function isRequired();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this field is disabled
|
* Returns whether this field is disabled
|
||||||
*
|
*
|
||||||
* The content of a disabled field is displayed, but not allowed to be
|
* The content of a disabled field is displayed, but not allowed to be
|
||||||
* modified. The validation of modified, disabled fields should fail.
|
* modified. The validation of modified, disabled fields should fail.
|
||||||
*
|
*
|
||||||
* Fields whose parents are disabled are considered disabled regardless of
|
* Fields whose parents are disabled are considered disabled regardless of
|
||||||
* their own state.
|
* their own state.
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function isDisabled();
|
public function isDisabled();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the field is hidden
|
* Returns whether the field is hidden
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function isHidden();
|
public function isHidden();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether this field is required to be filled out when submitted.
|
* Sets whether this field is required to be filled out when submitted.
|
||||||
*
|
*
|
||||||
* @param boolean $required
|
* @param boolean $required
|
||||||
*/
|
*/
|
||||||
public function setRequired($required);
|
public function setRequired($required);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the generator used for rendering HTML.
|
* Sets the generator used for rendering HTML.
|
||||||
*
|
*
|
||||||
* Usually there is one generator instance shared between all fields of a
|
* Usually there is one generator instance shared between all fields of a
|
||||||
* form.
|
* form.
|
||||||
*
|
*
|
||||||
* @param string $charset
|
* @param string $charset
|
||||||
*/
|
*/
|
||||||
public function setGenerator(HtmlGeneratorInterface $generator);
|
public function setGenerator(HtmlGeneratorInterface $generator);
|
||||||
}
|
}
|
@ -34,528 +34,491 @@ use Symfony\Components\File\UploadedFile;
|
|||||||
*/
|
*/
|
||||||
class Form extends FieldGroup
|
class Form extends FieldGroup
|
||||||
{
|
{
|
||||||
protected static $defaultCsrfSecret = null;
|
protected static $defaultCsrfSecret = null;
|
||||||
protected static $defaultCsrfProtection = false;
|
protected static $defaultCsrfProtection = false;
|
||||||
protected static $defaultCsrfFieldName = '_csrf_token';
|
protected static $defaultCsrfFieldName = '_csrf_token';
|
||||||
protected static $defaultLocale = null;
|
protected static $defaultLocale = null;
|
||||||
protected static $defaultTranslator = null;
|
protected static $defaultTranslator = null;
|
||||||
|
|
||||||
protected $validator = null;
|
protected $validator = null;
|
||||||
protected $validationGroups = null;
|
protected $validationGroups = null;
|
||||||
|
|
||||||
private $csrfSecret = null;
|
private $csrfSecret = null;
|
||||||
private $csrfFieldName = null;
|
private $csrfFieldName = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param array $defaults An array of field default values
|
* @param array $defaults An array of field default values
|
||||||
* @param array $options An array of options
|
* @param array $options An array of options
|
||||||
* @param string $defaultCsrfSecret A Csrf secret
|
* @param string $defaultCsrfSecret A Csrf secret
|
||||||
*/
|
*/
|
||||||
public function __construct($name, $object, ValidatorInterface $validator, array $options = array())
|
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)
|
|
||||||
{
|
{
|
||||||
$this->setCsrfSecret(self::$defaultCsrfSecret);
|
$this->generator = new HtmlGenerator();
|
||||||
}
|
$this->validator = $validator;
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->setCsrfSecret(md5(__FILE__.php_uname()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self::$defaultCsrfProtection !== false)
|
parent::__construct($name, $options);
|
||||||
{
|
|
||||||
$this->enableCsrfProtection();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self::$defaultLocale !== null)
|
$this->setData($object);
|
||||||
{
|
$this->setCsrfFieldName(self::$defaultCsrfFieldName);
|
||||||
$this->setLocale(self::$defaultLocale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self::$defaultTranslator !== null)
|
if (self::$defaultCsrfSecret !== null) {
|
||||||
{
|
$this->setCsrfSecret(self::$defaultCsrfSecret);
|
||||||
$this->setTranslator(self::$defaultTranslator);
|
} else {
|
||||||
}
|
$this->setCsrfSecret(md5(__FILE__.php_uname()));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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::$defaultCsrfProtection !== false) {
|
||||||
* Binds the form with the given data.
|
$this->enableCsrfProtection();
|
||||||
*
|
}
|
||||||
* @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::$defaultLocale !== null) {
|
||||||
* Gets the stylesheet paths associated with the form.
|
$this->setLocale(self::$defaultLocale);
|
||||||
*
|
}
|
||||||
* @return array An array of stylesheet paths
|
|
||||||
*/
|
|
||||||
public function getStylesheets()
|
|
||||||
{
|
|
||||||
return $this->getWidget()->getStylesheets();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (self::$defaultTranslator !== null) {
|
||||||
* Gets the JavaScript paths associated with the form.
|
$this->setTranslator(self::$defaultTranslator);
|
||||||
*
|
}
|
||||||
* @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;
|
/**
|
||||||
}
|
* Sets the charset used for rendering HTML
|
||||||
|
*
|
||||||
/**
|
* This method overrides the internal HTML generator! If you want to use
|
||||||
* Fixes a malformed PHP $_FILES array.
|
* your own generator, use setGenerator() instead.
|
||||||
*
|
*
|
||||||
* PHP has a bug that the format of the $_FILES array differs, depending on
|
* @param string $charset
|
||||||
* whether the uploaded file fields had normal field names or array-like
|
*/
|
||||||
* field names ("normal" vs. "parent[child]").
|
public function setCharset($charset)
|
||||||
*
|
|
||||||
* 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']))
|
|
||||||
{
|
{
|
||||||
foreach ($fileKeys as $k)
|
$this->setGenerator(new HtmlGenerator($charset));
|
||||||
{
|
|
||||||
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],
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $files;
|
/**
|
||||||
}
|
* Sets the validation groups for this form.
|
||||||
|
*
|
||||||
/**
|
* @param array|string $validationGroups
|
||||||
* Converts uploaded files to instances of clsas UploadedFile.
|
*/
|
||||||
*
|
public function setValidationGroups($validationGroups)
|
||||||
* @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))
|
$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);
|
$keys = array_keys($data);
|
||||||
sort($keys);
|
sort($keys);
|
||||||
|
|
||||||
if ($keys == $fileKeys)
|
$files = $data;
|
||||||
{
|
|
||||||
$files[$key] = new UploadedFile($data['tmp_name'], $data['name'], $data['type'], $data['size'], $data['error']);
|
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
|
|
||||||
{
|
return $files;
|
||||||
$files[$key] = self::convertFileInformation($data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
class HiddenField extends InputField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function render(array $attributes = array())
|
public function render(array $attributes = array())
|
||||||
{
|
{
|
||||||
return parent::render(array_merge(array(
|
return parent::render(array_merge(array(
|
||||||
'type' => 'hidden',
|
'type' => 'hidden',
|
||||||
), $attributes));
|
), $attributes));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function isHidden()
|
public function isHidden()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,136 +10,128 @@ namespace Symfony\Components\Form;
|
|||||||
*/
|
*/
|
||||||
class HtmlGenerator implements HtmlGeneratorInterface
|
class HtmlGenerator implements HtmlGeneratorInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Whether to produce XHTML compliant code
|
* Whether to produce XHTML compliant code
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
protected static $xhtml = true;
|
protected static $xhtml = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The charset used during generating
|
* The charset used during generating
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $charset;
|
protected $charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the charset used for rendering
|
* Sets the charset used for rendering
|
||||||
*
|
*
|
||||||
* @param string $charset
|
* @param string $charset
|
||||||
*/
|
*/
|
||||||
public function __construct($charset = 'UTF-8')
|
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))
|
|
||||||
{
|
{
|
||||||
return '';
|
$this->charset = $charset;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sprintf('<%s%s%s', $tag, $this->attributes($attributes), self::$xhtml ? ' />' : (strtolower($tag) == 'input' ? '>' : sprintf('></%s>', $tag)));
|
/**
|
||||||
}
|
* Sets the XHTML generation flag.
|
||||||
|
*
|
||||||
/**
|
* @param bool $boolean true if renderers must be generated as XHTML, false otherwise
|
||||||
* {@inheritDoc}
|
*/
|
||||||
*/
|
static public function setXhtml($boolean)
|
||||||
public function contentTag($tag, $content = null, $attributes = array())
|
|
||||||
{
|
|
||||||
if (empty($tag))
|
|
||||||
{
|
{
|
||||||
return '';
|
self::$xhtml = (boolean) $boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sprintf('<%s%s>%s</%s>', $tag, $this->attributes($attributes), $content, $tag);
|
/**
|
||||||
}
|
* Returns whether to generate XHTML tags or not.
|
||||||
|
*
|
||||||
/**
|
* @return bool true if renderers must be generated as XHTML, false otherwise
|
||||||
* {@inheritDoc}
|
*/
|
||||||
*/
|
static public function isXhtml()
|
||||||
public function attribute($name, $value)
|
|
||||||
{
|
|
||||||
if (true === $value)
|
|
||||||
{
|
{
|
||||||
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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function attributes(array $attributes)
|
public function contentTag($tag, $content = null, $attributes = array())
|
||||||
{
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function escape($value)
|
public function attributes(array $attributes)
|
||||||
{
|
{
|
||||||
return $this->fixDoubleEscape(htmlspecialchars((string) $value, ENT_QUOTES, $this->charset));
|
return implode('', array_map(array($this, 'attributesCallback'), array_keys($attributes), array_values($attributes)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fixes double escaped strings.
|
* Prepares an attribute key and value for HTML representation.
|
||||||
*
|
*
|
||||||
* @param string $escaped string to fix
|
* It removes empty attributes, except for the value one.
|
||||||
*
|
*
|
||||||
* @return string A single escaped string
|
* @param string $name The attribute name
|
||||||
*/
|
* @param string $value The attribute value
|
||||||
protected function fixDoubleEscape($escaped)
|
*
|
||||||
{
|
* @return string The HTML representation of the HTML key attribute pair.
|
||||||
return preg_replace('/&([a-z]+|(#\d+)|(#x[\da-f]+));/i', '&$1;', $escaped);
|
*/
|
||||||
}
|
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
|
interface HtmlGeneratorInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Escapes a value for safe output in HTML
|
* Escapes a value for safe output in HTML
|
||||||
*
|
*
|
||||||
* Double escaping of already-escaped sequences is avoided by this method.
|
* Double escaping of already-escaped sequences is avoided by this method.
|
||||||
*
|
*
|
||||||
* @param string $value The unescaped or partially escaped value
|
* @param string $value The unescaped or partially escaped value
|
||||||
*
|
*
|
||||||
* @return string The fully escaped value
|
* @return string The fully escaped value
|
||||||
*/
|
*/
|
||||||
public function escape($value);
|
public function escape($value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the HTML code for a tag attribute
|
* Generates the HTML code for a tag attribute
|
||||||
*
|
*
|
||||||
* @param string $name The attribute name
|
* @param string $name The attribute name
|
||||||
* @param string $value The attribute value
|
* @param string $value The attribute value
|
||||||
*
|
*
|
||||||
* @return string The HTML code of the attribute
|
* @return string The HTML code of the attribute
|
||||||
*/
|
*/
|
||||||
public function attribute($name, $value);
|
public function attribute($name, $value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the HTML code for multiple tag attributes
|
* Generates the HTML code for multiple tag attributes
|
||||||
*
|
*
|
||||||
* @param array $attributes An array with attribute names as keys and
|
* @param array $attributes An array with attribute names as keys and
|
||||||
* attribute values as elements
|
* attribute values as elements
|
||||||
*
|
*
|
||||||
* @return string The HTML code of the attribute list
|
* @return string The HTML code of the attribute list
|
||||||
*/
|
*/
|
||||||
public function attributes(array $attributes);
|
public function attributes(array $attributes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the HTML code for a tag without content
|
* Generates the HTML code for a tag without content
|
||||||
*
|
*
|
||||||
* @param string $tag The name of the tag
|
* @param string $tag The name of the tag
|
||||||
* @param array $attributes The attributes for the tag
|
* @param array $attributes The attributes for the tag
|
||||||
*
|
*
|
||||||
* @return string The HTML code for the tag
|
* @return string The HTML code for the tag
|
||||||
*/
|
*/
|
||||||
public function tag($tag, $attributes = array());
|
public function tag($tag, $attributes = array());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the HTML code for a tag with content
|
* Generates the HTML code for a tag with content
|
||||||
*
|
*
|
||||||
* @param string $tag The name of the tag
|
* @param string $tag The name of the tag
|
||||||
* @param string $content The content of the tag
|
* @param string $content The content of the tag
|
||||||
* @param array $attributes The attributes for the tag
|
* @param array $attributes The attributes for the tag
|
||||||
*
|
*
|
||||||
* @return string The HTML code for the tag
|
* @return string The HTML code for the tag
|
||||||
*/
|
*/
|
||||||
public function contentTag($tag, $content, $attributes = array());
|
public function contentTag($tag, $content, $attributes = array());
|
||||||
}
|
}
|
||||||
|
@ -14,88 +14,77 @@ namespace Symfony\Components\Form;
|
|||||||
*/
|
*/
|
||||||
class HybridField extends FieldGroup
|
class HybridField extends FieldGroup
|
||||||
{
|
{
|
||||||
const FIELD = 0;
|
const FIELD = 0;
|
||||||
const GROUP = 1;
|
const GROUP = 1;
|
||||||
|
|
||||||
protected $mode = self::FIELD;
|
protected $mode = self::FIELD;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current mode of the field
|
* Sets the current mode of the field
|
||||||
*
|
*
|
||||||
* Note that you can't switch modes anymore once you have added children to
|
* Note that you can't switch modes anymore once you have added children to
|
||||||
* this field.
|
* this field.
|
||||||
*
|
*
|
||||||
* @param integer $mode One of the constants HybridField::FIELD and
|
* @param integer $mode One of the constants HybridField::FIELD and
|
||||||
* HybridField::GROUP.
|
* HybridField::GROUP.
|
||||||
*/
|
*/
|
||||||
public function setFieldMode($mode)
|
public function setFieldMode($mode)
|
||||||
{
|
|
||||||
if (count($this) > 0 && $mode === self::FIELD)
|
|
||||||
{
|
{
|
||||||
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
|
||||||
* {@inheritDoc}
|
* subfields is not allowed
|
||||||
*
|
*/
|
||||||
* @throws FormException When the field is in mode HybridField::FIELD adding
|
public function add(FieldInterface $field)
|
||||||
* subfields is not allowed
|
|
||||||
*/
|
|
||||||
public function add(FieldInterface $field)
|
|
||||||
{
|
|
||||||
if ($this->mode === self::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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getDisplayedData()
|
public function setData($data)
|
||||||
{
|
|
||||||
if ($this->mode === self::GROUP)
|
|
||||||
{
|
{
|
||||||
return parent::getDisplayedData();
|
if ($this->mode === self::GROUP) {
|
||||||
|
parent::setData($data);
|
||||||
|
} else {
|
||||||
|
Field::setData($data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return Field::getDisplayedData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setData($data)
|
public function bind($data)
|
||||||
{
|
|
||||||
if ($this->mode === self::GROUP)
|
|
||||||
{
|
{
|
||||||
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
|
abstract class InputField extends Field
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function render(array $attributes = array())
|
public function render(array $attributes = array())
|
||||||
{
|
{
|
||||||
return $this->generator->tag('input', array_merge(array(
|
return $this->generator->tag('input', array_merge(array(
|
||||||
'id' => $this->getId(),
|
'id' => $this->getId(),
|
||||||
'name' => $this->getName(),
|
'name' => $this->getName(),
|
||||||
'value' => $this->getDisplayedData(),
|
'value' => $this->getDisplayedData(),
|
||||||
'disabled' => $this->isDisabled(),
|
'disabled' => $this->isDisabled(),
|
||||||
), $attributes));
|
), $attributes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,21 +17,21 @@ namespace Symfony\Components\Form;
|
|||||||
*/
|
*/
|
||||||
class IntegerField extends NumberField
|
class IntegerField extends NumberField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
{
|
||||||
$this->addOption('precision', 0);
|
$this->addOption('precision', 0);
|
||||||
|
|
||||||
parent::configure();
|
parent::configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getData()
|
public function getData()
|
||||||
{
|
{
|
||||||
return (int)parent::getData();
|
return (int)parent::getData();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,18 +6,18 @@ use Symfony\Components\Form\FieldGroupInterface;
|
|||||||
|
|
||||||
class RecursiveFieldsWithPropertyPathIterator extends \IteratorIterator implements \RecursiveIterator
|
class RecursiveFieldsWithPropertyPathIterator extends \IteratorIterator implements \RecursiveIterator
|
||||||
{
|
{
|
||||||
public function __construct(FieldGroupInterface $group)
|
public function __construct(FieldGroupInterface $group)
|
||||||
{
|
{
|
||||||
parent::__construct($group);
|
parent::__construct($group);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getChildren()
|
public function getChildren()
|
||||||
{
|
{
|
||||||
return new self($this->current());
|
return new self($this->current());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasChildren()
|
public function hasChildren()
|
||||||
{
|
{
|
||||||
return $this->current() instanceof FieldGroupInterface && $this->current()->getPropertyPath() === null;
|
return $this->current() instanceof FieldGroupInterface && $this->current()->getPropertyPath() === null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,10 @@ namespace Symfony\Components\Form;
|
|||||||
*/
|
*/
|
||||||
interface Localizable
|
interface Localizable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Sets the locale of the class.
|
* Sets the locale of the class.
|
||||||
*
|
*
|
||||||
* @param string $locale
|
* @param string $locale
|
||||||
*/
|
*/
|
||||||
public function setLocale($locale);
|
public function setLocale($locale);
|
||||||
}
|
}
|
@ -19,93 +19,83 @@ use Symfony\Components\Form\ValueTransformer\MoneyToLocalizedStringTransformer;
|
|||||||
*/
|
*/
|
||||||
class MoneyField extends NumberField
|
class MoneyField extends NumberField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Stores patterns for different locales and cultures
|
* Stores patterns for different locales and cultures
|
||||||
*
|
*
|
||||||
* A pattern decides which currency symbol is displayed and where it is in
|
* A pattern decides which currency symbol is displayed and where it is in
|
||||||
* relation to the number.
|
* relation to the number.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $patterns = array();
|
protected static $patterns = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
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'))
|
|
||||||
{
|
{
|
||||||
return str_replace('%widget%', $input, $this->getPattern($this->locale, $this->getOption('currency')));
|
$this->addOption('precision', 2);
|
||||||
}
|
$this->addOption('divisor', 1);
|
||||||
else
|
$this->addOption('currency');
|
||||||
{
|
|
||||||
return $input;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
parent::configure();
|
||||||
* Returns the pattern for this locale
|
|
||||||
*
|
$this->setValueTransformer(new MoneyToLocalizedStringTransformer(array(
|
||||||
* The pattern contains the placeholder "%widget%" where the HTML tag should
|
'precision' => $this->getOption('precision'),
|
||||||
* be inserted
|
'grouping' => $this->getOption('grouping'),
|
||||||
*
|
'divisor' => $this->getOption('divisor'),
|
||||||
* @param string $locale
|
)));
|
||||||
*/
|
|
||||||
protected static function getPattern($locale, $currency)
|
|
||||||
{
|
|
||||||
if (!isset(self::$patterns[$locale]))
|
|
||||||
{
|
|
||||||
self::$patterns[$locale] = array();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset(self::$patterns[$locale][$currency]))
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function render(array $attributes = array())
|
||||||
{
|
{
|
||||||
$format = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
|
$input = parent::render($attributes);
|
||||||
$pattern = $format->formatCurrency('123', $currency);
|
|
||||||
|
|
||||||
// the spacings between currency symbol and number are ignored, because
|
if ($this->getOption('currency')) {
|
||||||
// a single space leads to better readability in combination with input
|
return str_replace('%widget%', $input, $this->getPattern($this->locale, $this->getOption('currency')));
|
||||||
// fields
|
} else {
|
||||||
|
return $input;
|
||||||
// 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];
|
/**
|
||||||
}
|
* 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
|
class NumberField extends InputField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
{
|
||||||
// default precision is locale specific (usually around 3)
|
// default precision is locale specific (usually around 3)
|
||||||
$this->addOption('precision');
|
$this->addOption('precision');
|
||||||
$this->addOption('grouping', false);
|
$this->addOption('grouping', false);
|
||||||
|
|
||||||
$this->setValueTransformer(new NumberToLocalizedStringTransformer(array(
|
$this->setValueTransformer(new NumberToLocalizedStringTransformer(array(
|
||||||
'precision' => $this->getOption('precision'),
|
'precision' => $this->getOption('precision'),
|
||||||
'grouping' => $this->getOption('grouping'),
|
'grouping' => $this->getOption('grouping'),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function render(array $attributes = array())
|
public function render(array $attributes = array())
|
||||||
{
|
{
|
||||||
return parent::render(array_merge(array(
|
return parent::render(array_merge(array(
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
), $attributes));
|
), $attributes));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,24 +19,24 @@ use Symfony\Components\Form\Renderer\InputPasswordRenderer;
|
|||||||
*/
|
*/
|
||||||
class PasswordField extends TextField
|
class PasswordField extends TextField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
{
|
||||||
parent::configure();
|
parent::configure();
|
||||||
|
|
||||||
$this->addOption('always_empty', true);
|
$this->addOption('always_empty', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function render(array $attributes = array())
|
public function render(array $attributes = array())
|
||||||
{
|
{
|
||||||
return parent::render(array_merge(array(
|
return parent::render(array_merge(array(
|
||||||
'value' => $this->getOption('always_empty') && !$this->isBound() ? '' : $this->getDisplayedData(),
|
'value' => $this->getOption('always_empty') && !$this->isBound() ? '' : $this->getDisplayedData(),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
), $attributes));
|
), $attributes));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,28 +19,28 @@ use Symfony\Components\Form\ValueTransformer\PercentToLocalizedStringTransformer
|
|||||||
*/
|
*/
|
||||||
class PercentField extends NumberField
|
class PercentField extends NumberField
|
||||||
{
|
{
|
||||||
const FRACTIONAL = 'fractional';
|
const FRACTIONAL = 'fractional';
|
||||||
const INTEGER = 'integer';
|
const INTEGER = 'integer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
{
|
||||||
$this->addOption('precision', 0);
|
$this->addOption('precision', 0);
|
||||||
$this->addOption('type', self::FRACTIONAL);
|
$this->addOption('type', self::FRACTIONAL);
|
||||||
|
|
||||||
$this->setValueTransformer(new PercentToLocalizedStringTransformer(array(
|
$this->setValueTransformer(new PercentToLocalizedStringTransformer(array(
|
||||||
'precision' => $this->getOption('precision'),
|
'precision' => $this->getOption('precision'),
|
||||||
'type' => $this->getOption('type'),
|
'type' => $this->getOption('type'),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function render(array $attributes = array())
|
public function render(array $attributes = array())
|
||||||
{
|
{
|
||||||
return parent::render($attributes).' %';
|
return parent::render($attributes).' %';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,152 +11,145 @@ use Symfony\Components\Form\Exception\InvalidPropertyPathException;
|
|||||||
*/
|
*/
|
||||||
class PropertyPath
|
class PropertyPath
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The current index of the traversal
|
* The current index of the traversal
|
||||||
* @var integer
|
* @var integer
|
||||||
*/
|
*/
|
||||||
protected $currentIndex = 0;
|
protected $currentIndex = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The elements of the property path
|
* The elements of the property path
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $elements = array();
|
protected $elements = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains a boolean for each property in $elements denoting whether this
|
* Contains a boolean for each property in $elements denoting whether this
|
||||||
* element is a property. It is an index otherwise.
|
* element is a property. It is an index otherwise.
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $isProperty = array();
|
protected $isProperty = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String representation of the path
|
* String representation of the path
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $string;
|
protected $string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the given property path
|
* Parses the given property path
|
||||||
*
|
*
|
||||||
* @param string $propertyPath
|
* @param string $propertyPath
|
||||||
*/
|
*/
|
||||||
public function __construct($propertyPath)
|
public function __construct($propertyPath)
|
||||||
{
|
|
||||||
if (empty($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;
|
* Returns the string representation of the property path
|
||||||
$remaining = $propertyPath;
|
*
|
||||||
|
* @return string
|
||||||
// first element is evaluated differently - no leading dot for properties
|
*/
|
||||||
$pattern = '/^((\w+)|\[(\w+)\])(.*)/';
|
public function __toString()
|
||||||
|
|
||||||
while (preg_match($pattern, $remaining, $matches))
|
|
||||||
{
|
{
|
||||||
if (!empty($matches[2]))
|
return $this->string;
|
||||||
{
|
|
||||||
$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))
|
/**
|
||||||
|
* Returns the current element of the path
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCurrent()
|
||||||
{
|
{
|
||||||
throw new InvalidPropertyPathException(sprintf(
|
return $this->elements[$this->currentIndex];
|
||||||
'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');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
++$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
|
* Returns whether the currente element is an array index
|
||||||
*/
|
*
|
||||||
public function rewind()
|
* @return boolean
|
||||||
{
|
*/
|
||||||
$this->currentIndex = 0;
|
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
|
class RadioField extends ToggleField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function render(array $attributes = array())
|
public function render(array $attributes = array())
|
||||||
{
|
{
|
||||||
return parent::render(array_merge(array(
|
return parent::render(array_merge(array(
|
||||||
'type' => 'radio',
|
'type' => 'radio',
|
||||||
'name' => $this->getParent() ? $this->getParent()->getName() : $this->getName(),
|
'name' => $this->getParent() ? $this->getParent()->getName() : $this->getName(),
|
||||||
), $attributes));
|
), $attributes));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,79 +13,77 @@ use Symfony\Components\Form\Configurable;
|
|||||||
*/
|
*/
|
||||||
abstract class Renderer extends Configurable implements RendererInterface
|
abstract class Renderer extends Configurable implements RendererInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The generator used for rendering the HTML
|
* The generator used for rendering the HTML
|
||||||
* @var HtmlGeneratorInterface
|
* @var HtmlGeneratorInterface
|
||||||
*/
|
*/
|
||||||
protected $generator;
|
protected $generator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the stylesheet paths associated with the renderer.
|
* Gets the stylesheet paths associated with the renderer.
|
||||||
*
|
*
|
||||||
* The array keys are files and values are the media names (separated by a ,):
|
* 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')
|
* array('/path/to/file.css' => 'all', '/another/file.css' => 'screen,print')
|
||||||
*
|
*
|
||||||
* @return array An array of stylesheet paths
|
* @return array An array of stylesheet paths
|
||||||
*/
|
*/
|
||||||
public function getStylesheets()
|
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())
|
|
||||||
{
|
{
|
||||||
$html .= "<ul>\n";
|
return array();
|
||||||
|
|
||||||
foreach ($field->getErrors() as $error)
|
|
||||||
{
|
|
||||||
$html .= "<li>" . $error . "</li>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$html .= "</ul>\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $html;
|
/**
|
||||||
}
|
* Gets the JavaScript paths associated with the renderer.
|
||||||
|
*
|
||||||
|
* @return array An array of JavaScript paths
|
||||||
|
*/
|
||||||
|
public function getJavaScripts()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setTranslator(TranslatorInterface $translator)
|
public function renderErrors(FieldInterface $field)
|
||||||
{
|
{
|
||||||
// TODO
|
$html = '';
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if ($field->hasErrors()) {
|
||||||
* {@inheritDoc}
|
$html .= "<ul>\n";
|
||||||
*/
|
|
||||||
public function setLocale($locale)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
foreach ($field->getErrors() as $error) {
|
||||||
* {@inheritDoc}
|
$html .= "<li>" . $error . "</li>\n";
|
||||||
*/
|
}
|
||||||
public function setGenerator(HtmlGeneratorInterface $generator)
|
|
||||||
{
|
$html .= "</ul>\n";
|
||||||
$this->generator = $generator;
|
}
|
||||||
}
|
|
||||||
|
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
|
interface RendererInterface extends Localizable, Translatable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Sets the generator used for rendering the HTML
|
* Sets the generator used for rendering the HTML
|
||||||
*
|
*
|
||||||
* @param HtmlGeneratorInterface $generator
|
* @param HtmlGeneratorInterface $generator
|
||||||
*/
|
*/
|
||||||
public function setGenerator(HtmlGeneratorInterface $generator);
|
public function setGenerator(HtmlGeneratorInterface $generator);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the textual representation of the given field.
|
* Returns the textual representation of the given field.
|
||||||
*
|
*
|
||||||
* @param FieldInterface $field The form field
|
* @param FieldInterface $field The form field
|
||||||
* @param array $attributes The attributes to include in the
|
* @param array $attributes The attributes to include in the
|
||||||
* rendered output
|
* rendered output
|
||||||
* @return string The rendered output
|
* @return string The rendered output
|
||||||
* @throws InvalidArgumentException If the $field is not instance of the
|
* @throws InvalidArgumentException If the $field is not instance of the
|
||||||
* expected class
|
* expected class
|
||||||
*/
|
*/
|
||||||
public function render(FieldInterface $field, array $attributes = array());
|
public function render(FieldInterface $field, array $attributes = array());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the textual representation of the errors of the given field.
|
* Returns the textual representation of the errors of the given field.
|
||||||
*
|
*
|
||||||
* @param FieldInterface $field The form field
|
* @param FieldInterface $field The form field
|
||||||
* @return string The rendered output
|
* @return string The rendered output
|
||||||
* @throws InvalidArgumentException If the $field is not instance of the
|
* @throws InvalidArgumentException If the $field is not instance of the
|
||||||
* expected class
|
* expected class
|
||||||
*/
|
*/
|
||||||
public function renderErrors(FieldInterface $field);
|
public function renderErrors(FieldInterface $field);
|
||||||
}
|
}
|
@ -20,36 +20,34 @@ use Symfony\Components\Form\Field\ChoiceField;
|
|||||||
*/
|
*/
|
||||||
class TableRenderer extends Renderer
|
class TableRenderer extends Renderer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function render(FieldInterface $group, array $attributes = array())
|
public function render(FieldInterface $group, array $attributes = array())
|
||||||
{
|
|
||||||
$html = "<table>\n";
|
|
||||||
|
|
||||||
foreach ($group as $field)
|
|
||||||
{
|
{
|
||||||
$label = self::humanize($field->getKey());
|
$html = "<table>\n";
|
||||||
|
|
||||||
$html .= "<tr>\n";
|
foreach ($group as $field) {
|
||||||
$html .= "<td><label for=\"{$field->getId()}\">$label</label></td>\n";
|
$label = self::humanize($field->getKey());
|
||||||
$html .= "<td>\n";
|
|
||||||
if ($field->hasErrors())
|
$html .= "<tr>\n";
|
||||||
{
|
$html .= "<td><label for=\"{$field->getId()}\">$label</label></td>\n";
|
||||||
$html .= $field->renderErrors()."\n";
|
$html .= "<td>\n";
|
||||||
}
|
if ($field->hasErrors()) {
|
||||||
$html .= $field->render()."\n";
|
$html .= $field->renderErrors()."\n";
|
||||||
$html .= "</td>";
|
}
|
||||||
$html .= "</tr>\n";
|
$html .= $field->render()."\n";
|
||||||
|
$html .= "</td>";
|
||||||
|
$html .= "</tr>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= "</table>\n";
|
||||||
|
|
||||||
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
$html .= "</table>\n";
|
protected static function humanize($text)
|
||||||
|
{
|
||||||
return $html;
|
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
|
class RepeatedField extends FieldGroup
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The prototype for the inner fields
|
* The prototype for the inner fields
|
||||||
* @var FieldInterface
|
* @var FieldInterface
|
||||||
*/
|
*/
|
||||||
protected $prototype;
|
protected $prototype;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repeats the given field twice to verify the user's input
|
* Repeats the given field twice to verify the user's input
|
||||||
*
|
*
|
||||||
* @param FieldInterface $innerField
|
* @param FieldInterface $innerField
|
||||||
*/
|
*/
|
||||||
public function __construct(FieldInterface $innerField, array $options = array())
|
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())
|
|
||||||
{
|
{
|
||||||
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
|
class TextField extends InputField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
{
|
||||||
parent::configure();
|
parent::configure();
|
||||||
|
|
||||||
$this->addOption('max_length');
|
$this->addOption('max_length');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function render(array $attributes = array())
|
public function render(array $attributes = array())
|
||||||
{
|
{
|
||||||
return parent::render(array_merge(array(
|
return parent::render(array_merge(array(
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
'maxlength' => $this->getOption('max_length'),
|
'maxlength' => $this->getOption('max_length'),
|
||||||
), $attributes));
|
), $attributes));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,18 +19,18 @@ use Symfony\Components\Form\Renderer\TextareaRenderer;
|
|||||||
*/
|
*/
|
||||||
class TextareaField extends Field
|
class TextareaField extends Field
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function render(array $attributes = array())
|
public function render(array $attributes = array())
|
||||||
{
|
{
|
||||||
$content = $this->generator->escape($this->getDisplayedData());
|
$content = $this->generator->escape($this->getDisplayedData());
|
||||||
|
|
||||||
return $this->generator->contentTag('textarea', $content, array_merge(array(
|
return $this->generator->contentTag('textarea', $content, array_merge(array(
|
||||||
'id' => $this->getId(),
|
'id' => $this->getId(),
|
||||||
'name' => $this->getName(),
|
'name' => $this->getName(),
|
||||||
'rows' => 30,
|
'rows' => 30,
|
||||||
'cols' => 4,
|
'cols' => 4,
|
||||||
), $attributes));
|
), $attributes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,147 +11,134 @@ use Symfony\Components\Form\ValueTransformer\ValueTransformerChain;
|
|||||||
|
|
||||||
class TimeField extends FieldGroup
|
class TimeField extends FieldGroup
|
||||||
{
|
{
|
||||||
const INPUT = 'input';
|
const INPUT = 'input';
|
||||||
const CHOICE = 'choice';
|
const CHOICE = 'choice';
|
||||||
|
|
||||||
const DATETIME = 'datetime';
|
const DATETIME = 'datetime';
|
||||||
const STRING = 'string';
|
const STRING = 'string';
|
||||||
const TIMESTAMP = 'timestamp';
|
const TIMESTAMP = 'timestamp';
|
||||||
const RAW = 'raw';
|
const RAW = 'raw';
|
||||||
|
|
||||||
protected static $widgets = array(
|
protected static $widgets = array(
|
||||||
self::INPUT,
|
self::INPUT,
|
||||||
self::CHOICE,
|
self::CHOICE,
|
||||||
);
|
);
|
||||||
|
|
||||||
protected static $types = array(
|
protected static $types = array(
|
||||||
self::DATETIME,
|
self::DATETIME,
|
||||||
self::STRING,
|
self::STRING,
|
||||||
self::TIMESTAMP,
|
self::TIMESTAMP,
|
||||||
self::RAW,
|
self::RAW,
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
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)
|
|
||||||
{
|
{
|
||||||
$this->add(new TextField('hour', array('max_length' => 2)));
|
$this->addOption('hours', range(0, 23));
|
||||||
$this->add(new TextField('minute', array('max_length' => 2)));
|
$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'))
|
if ($this->getOption('widget') == self::INPUT) {
|
||||||
{
|
$this->add(new TextField('hour', array('max_length' => 2)));
|
||||||
$this->add(new TextField('second', array('max_length' => 2)));
|
$this->add(new TextField('minute', 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'))
|
if ($this->getOption('with_seconds')) {
|
||||||
{
|
$this->add(new TextField('second', array('max_length' => 2)));
|
||||||
$this->add(new ChoiceField('second', array(
|
}
|
||||||
'choices' => $this->generatePaddedChoices($this->getOption('seconds'), 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)
|
$html = $this->get('hour')->render($attributes);
|
||||||
{
|
$html .= ':' . $this->get('minute')->render($attributes);
|
||||||
$transformers[] = new StringToDateTimeTransformer(array(
|
|
||||||
'format' => 'H:i:s',
|
if ($this->getOption('with_seconds')) {
|
||||||
'input_timezone' => $this->getOption('data_timezone'),
|
$html .= ':' . $this->get('second')->render($attributes);
|
||||||
'output_timezone' => $this->getOption('data_timezone'),
|
}
|
||||||
));
|
|
||||||
}
|
return $html;
|
||||||
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'),
|
* Generates an array of choices for the given values
|
||||||
'output_timezone' => $this->getOption('user_timezone'),
|
*
|
||||||
// if the field is rendered as choice field, the values should be trimmed
|
* If the values are shorter than $padLength characters, they are padded with
|
||||||
// of trailing zeros to render the selected choices correctly
|
* zeros on the left side.
|
||||||
'pad' => $this->getOption('widget') == self::INPUT,
|
*
|
||||||
));
|
* @param array $values The available choices
|
||||||
|
* @param integer $padLength The length to pad the choices
|
||||||
$this->setValueTransformer(new ValueTransformerChain($transformers));
|
* @return array An array with the input values as keys and the
|
||||||
}
|
* padded values as values
|
||||||
|
*/
|
||||||
/**
|
protected function generatePaddedChoices(array $values, $padLength)
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function render(array $attributes = array())
|
|
||||||
{
|
|
||||||
if ($this->getOption('widget') == self::INPUT)
|
|
||||||
{
|
{
|
||||||
$attributes = array_merge(array(
|
$choices = array();
|
||||||
'size' => '1',
|
|
||||||
), $attributes);
|
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
|
class TimezoneField extends ChoiceField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Stores the available timezone choices
|
* Stores the available timezone choices
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $timezones = array();
|
protected static $timezones = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function configure()
|
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())
|
|
||||||
{
|
{
|
||||||
$data = date_default_timezone_get();
|
$this->addOption('choices', self::getTimezoneChoices());
|
||||||
|
|
||||||
|
parent::configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
/**
|
||||||
}
|
* Preselects the server timezone if the field is empty and required
|
||||||
|
*
|
||||||
/**
|
* {@inheritDoc}
|
||||||
* Returns the timezone choices
|
*/
|
||||||
*
|
public function getDisplayedData()
|
||||||
* 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)
|
$data = parent::getDisplayedData();
|
||||||
{
|
|
||||||
$parts = explode('/', $timezone);
|
|
||||||
|
|
||||||
if (count($parts) > 2)
|
if ($data == null && $this->isRequired()) {
|
||||||
{
|
$data = date_default_timezone_get();
|
||||||
$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]))
|
return $data;
|
||||||
{
|
|
||||||
self::$timezones[$region] = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$timezones[$region][$timezone] = str_replace('_', ' ', $name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
abstract class ToggleField extends InputField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
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'))
|
|
||||||
{
|
{
|
||||||
if ($this->getOption('translate_label'))
|
$this->addOption('value');
|
||||||
{
|
$this->addOption('label');
|
||||||
$label = $this->translate($label);
|
$this->addOption('translate_label', false);
|
||||||
}
|
|
||||||
|
|
||||||
$html .= ' '.$this->generator->contentTag('label', $label, array(
|
$this->setValueTransformer(new BooleanToStringTransformer());
|
||||||
'for' => $this->getId(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
interface Translatable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Sets the translator unit of the class.
|
* Sets the translator unit of the class.
|
||||||
*
|
*
|
||||||
* @param TranslatorInterface $translator
|
* @param TranslatorInterface $translator
|
||||||
*/
|
*/
|
||||||
public function setTranslator(TranslatorInterface $translator);
|
public function setTranslator(TranslatorInterface $translator);
|
||||||
}
|
}
|
||||||
|
@ -4,41 +4,40 @@ namespace Symfony\Components\Form\ValueTransformer;
|
|||||||
|
|
||||||
abstract class BaseDateTimeTransformer extends BaseValueTransformer
|
abstract class BaseDateTimeTransformer extends BaseValueTransformer
|
||||||
{
|
{
|
||||||
const NONE = 'none';
|
const NONE = 'none';
|
||||||
const FULL = 'full';
|
const FULL = 'full';
|
||||||
const LONG = 'long';
|
const LONG = 'long';
|
||||||
const MEDIUM = 'medium';
|
const MEDIUM = 'medium';
|
||||||
const SHORT = 'short';
|
const SHORT = 'short';
|
||||||
|
|
||||||
protected static $formats = array(
|
protected static $formats = array(
|
||||||
self::NONE,
|
self::NONE,
|
||||||
self::FULL,
|
self::FULL,
|
||||||
self::LONG,
|
self::LONG,
|
||||||
self::MEDIUM,
|
self::MEDIUM,
|
||||||
self::SHORT,
|
self::SHORT,
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the appropriate IntLDateFormatter constant for the given format
|
* Returns the appropriate IntLDateFormatter constant for the given format
|
||||||
*
|
*
|
||||||
* @param string $format One of "short", "medium", "long" and "full"
|
* @param string $format One of "short", "medium", "long" and "full"
|
||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
protected function getIntlFormatConstant($format)
|
protected function getIntlFormatConstant($format)
|
||||||
{
|
|
||||||
switch ($format)
|
|
||||||
{
|
{
|
||||||
case self::FULL:
|
switch ($format) {
|
||||||
return \IntlDateFormatter::FULL;
|
case self::FULL:
|
||||||
case self::LONG:
|
return \IntlDateFormatter::FULL;
|
||||||
return \IntlDateFormatter::LONG;
|
case self::LONG:
|
||||||
case self::SHORT:
|
return \IntlDateFormatter::LONG;
|
||||||
return \IntldateFormatter::SHORT;
|
case self::SHORT:
|
||||||
case self::MEDIUM:
|
return \IntldateFormatter::SHORT;
|
||||||
return \IntlDateFormatter::MEDIUM;
|
case self::MEDIUM:
|
||||||
case self::NONE:
|
return \IntlDateFormatter::MEDIUM;
|
||||||
default:
|
case self::NONE:
|
||||||
return \IntlDateFormatter::NONE;
|
default:
|
||||||
|
return \IntlDateFormatter::NONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
@ -11,32 +11,32 @@ use Symfony\Components\Form\Configurable;
|
|||||||
*/
|
*/
|
||||||
abstract class BaseValueTransformer extends Configurable implements ValueTransformerInterface
|
abstract class BaseValueTransformer extends Configurable implements ValueTransformerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The locale of this transformer as accepted by the class Locale
|
* The locale of this transformer as accepted by the class Locale
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $locale;
|
protected $locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param array $options An array of options
|
* @param array $options An array of options
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException when a option is not supported
|
* @throws \InvalidArgumentException when a option is not supported
|
||||||
* @throws \RuntimeException when a required option is not given
|
* @throws \RuntimeException when a required option is not given
|
||||||
*/
|
*/
|
||||||
public function __construct(array $options = array())
|
public function __construct(array $options = array())
|
||||||
{
|
{
|
||||||
$this->locale = \Locale::getDefault();
|
$this->locale = \Locale::getDefault();
|
||||||
|
|
||||||
parent::__construct($options);
|
parent::__construct($options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setLocale($locale)
|
public function setLocale($locale)
|
||||||
{
|
{
|
||||||
$this->locale = $locale;
|
$this->locale = $locale;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,36 +10,34 @@ namespace Symfony\Components\Form\ValueTransformer;
|
|||||||
*/
|
*/
|
||||||
class BooleanToStringTransformer extends BaseValueTransformer
|
class BooleanToStringTransformer extends BaseValueTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Transforms a boolean into a string.
|
* Transforms a boolean into a string.
|
||||||
*
|
*
|
||||||
* @param boolean $value Boolean value.
|
* @param boolean $value Boolean value.
|
||||||
* @return string String value.
|
* @return string String value.
|
||||||
*/
|
*/
|
||||||
public function transform($value)
|
public function transform($value)
|
||||||
{
|
|
||||||
if (!is_bool($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.
|
||||||
* Transforms a string into a boolean.
|
* @return boolean Boolean value.
|
||||||
*
|
*/
|
||||||
* @param string $value String value.
|
public function reverseTransform($value)
|
||||||
* @return boolean Boolean value.
|
|
||||||
*/
|
|
||||||
public function reverseTransform($value)
|
|
||||||
{
|
|
||||||
if (!is_string($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
|
class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
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)
|
|
||||||
{
|
{
|
||||||
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');
|
* Transforms a normalized date into a localized date string/array.
|
||||||
|
*
|
||||||
if ($inputTimezone != $outputTimezone)
|
* @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'),
|
* Transforms a localized date string/array into a normalized date.
|
||||||
'month' => $dateTime->format('m'),
|
*
|
||||||
'day' => $dateTime->format('d'),
|
* @param array $value Localized date string/array
|
||||||
'hour' => $dateTime->format('H'),
|
* @return DateTime Normalized date
|
||||||
'minute' => $dateTime->format('i'),
|
*/
|
||||||
'second' => $dateTime->format('s'),
|
public function reverseTransform($value)
|
||||||
), array_flip($this->getOption('fields')));
|
|
||||||
|
|
||||||
if (!$this->getOption('pad'))
|
|
||||||
{
|
{
|
||||||
foreach ($result as &$entry)
|
$inputTimezone = $this->getOption('input_timezone');
|
||||||
{
|
$outputTimezone = $this->getOption('output_timezone');
|
||||||
$entry = (int)$entry;
|
|
||||||
}
|
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
|
class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
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))
|
|
||||||
{
|
{
|
||||||
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');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
$inputTimezone = $this->getOption('input_timezone');
|
||||||
* Transforms a normalized date into a localized date string/array.
|
|
||||||
*
|
// convert time to UTC before passing it to the formatter
|
||||||
* @param DateTime $dateTime Normalized date.
|
if ($inputTimezone != 'UTC') {
|
||||||
* @return string|array Localized date string/array.
|
$dateTime->setTimezone(new \DateTimeZone('UTC'));
|
||||||
*/
|
}
|
||||||
public function transform($dateTime)
|
|
||||||
{
|
$value = $this->getIntlDateFormatter()->format((int)$dateTime->format('U'));
|
||||||
if (!$dateTime instanceof \DateTime)
|
|
||||||
{
|
if (intl_get_error_code() != 0) {
|
||||||
throw new \InvalidArgumentException('Expected value of type \DateTime');
|
throw new TransformationFailedException(intl_get_error_message());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
$inputTimezone = $this->getOption('input_timezone');
|
/**
|
||||||
|
* Transforms a localized date string/array into a normalized date.
|
||||||
// convert time to UTC before passing it to the formatter
|
*
|
||||||
if ($inputTimezone != 'UTC')
|
* @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'));
|
/**
|
||||||
|
* Returns a preconfigured IntlDateFormatter instance
|
||||||
if (intl_get_error_code() != 0)
|
*
|
||||||
|
* @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
|
class MoneyToLocalizedStringTransformer extends NumberToLocalizedStringTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
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))
|
|
||||||
{
|
{
|
||||||
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)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
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
|
* Transforms a localized money string into a normalized format.
|
||||||
*/
|
*
|
||||||
public function reverseTransform($value)
|
* @param string $value Localized money string
|
||||||
{
|
* @return number Normalized number
|
||||||
return parent::reverseTransform($value) * $this->getOption('divisor');
|
*/
|
||||||
}
|
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
|
class NumberToLocalizedStringTransformer extends BaseValueTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
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))
|
|
||||||
{
|
{
|
||||||
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);
|
* Transforms a number type into localized number.
|
||||||
|
*
|
||||||
if (intl_is_failure($formatter->getErrorCode()))
|
* @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
|
||||||
* Transforms a localized number into an integer or float
|
*/
|
||||||
*
|
public function reverseTransform($value)
|
||||||
* @param string $value
|
|
||||||
*/
|
|
||||||
public function reverseTransform($value)
|
|
||||||
{
|
|
||||||
if (!is_string($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);
|
* Returns a preconfigured \NumberFormatter instance
|
||||||
|
*
|
||||||
if (intl_is_failure($formatter->getErrorCode()))
|
* @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
|
class PercentToLocalizedStringTransformer extends BaseValueTransformer
|
||||||
{
|
{
|
||||||
const FRACTIONAL = 'fractional';
|
const FRACTIONAL = 'fractional';
|
||||||
const INTEGER = 'integer';
|
const INTEGER = 'integer';
|
||||||
|
|
||||||
protected static $types = array(
|
protected static $types = array(
|
||||||
self::FRACTIONAL,
|
self::FRACTIONAL,
|
||||||
self::INTEGER,
|
self::INTEGER,
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
|
||||||
$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)));
|
$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.
|
||||||
* Transforms between a normalized format (integer or float) into a percentage value.
|
* @return number Percentage value.
|
||||||
*
|
*/
|
||||||
* @param number $value Normalized value.
|
public function transform($value)
|
||||||
* @return number Percentage value.
|
|
||||||
*/
|
|
||||||
public function transform($value)
|
|
||||||
{
|
|
||||||
if (!is_numeric($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);
|
* Returns a preconfigured \NumberFormatter instance
|
||||||
|
*
|
||||||
if (intl_is_failure($formatter->getErrorCode()))
|
* @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
|
class ReversedTransformer implements ValueTransformerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The reversed transformer
|
* The reversed transformer
|
||||||
* @var ValueTransformerInterface
|
* @var ValueTransformerInterface
|
||||||
*/
|
*/
|
||||||
protected $reversedTransformer;
|
protected $reversedTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverses this transformer
|
* Reverses this transformer
|
||||||
*
|
*
|
||||||
* @param ValueTransformerInterface $innerTransformer
|
* @param ValueTransformerInterface $innerTransformer
|
||||||
*/
|
*/
|
||||||
public function __construct(ValueTransformerInterface $reversedTransformer)
|
public function __construct(ValueTransformerInterface $reversedTransformer)
|
||||||
{
|
{
|
||||||
$this->reversedTransformer = $reversedTransformer;
|
$this->reversedTransformer = $reversedTransformer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function transform($value)
|
public function transform($value)
|
||||||
{
|
{
|
||||||
return $this->reversedTransformer->reverseTransform($value);
|
return $this->reversedTransformer->reverseTransform($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function reverseTransform($value)
|
public function reverseTransform($value)
|
||||||
{
|
{
|
||||||
return $this->reversedTransformer->transform($value);
|
return $this->reversedTransformer->transform($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setLocale($locale)
|
public function setLocale($locale)
|
||||||
{
|
{
|
||||||
$this->reversedTransformer->setLocale($locale);
|
$this->reversedTransformer->setLocale($locale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,60 +12,55 @@ use \Symfony\Components\Form\ValueTransformer\ValueTransformerException;
|
|||||||
*/
|
*/
|
||||||
class StringToDateTimeTransformer extends BaseValueTransformer
|
class StringToDateTimeTransformer extends BaseValueTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
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
|
|
||||||
{
|
{
|
||||||
$dateTime = new \DateTime("$value $inputTimezone");
|
$this->addOption('input_timezone', 'UTC');
|
||||||
|
$this->addOption('output_timezone', 'UTC');
|
||||||
if ($inputTimezone != $outputTimezone)
|
$this->addOption('format', 'Y-m-d H:i:s');
|
||||||
{
|
|
||||||
$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')));
|
/**
|
||||||
|
* 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
|
class TimestampToDateTimeTransformer extends BaseValueTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function configure()
|
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
|
|
||||||
{
|
{
|
||||||
$dateTime = new \DateTime("@$value $inputTimezone");
|
$this->addOption('input_timezone', 'UTC');
|
||||||
|
$this->addOption('output_timezone', 'UTC');
|
||||||
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')));
|
/**
|
||||||
|
* 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
|
class ValueTransformerChain implements ValueTransformerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The value transformers
|
* The value transformers
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $transformers;
|
protected $transformers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the given value transformers to transform values
|
* Uses the given value transformers to transform values
|
||||||
*
|
*
|
||||||
* @param array $transformers
|
* @param array $transformers
|
||||||
*/
|
*/
|
||||||
public function __construct(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)
|
|
||||||
{
|
{
|
||||||
$value = $transformer->transform($value);
|
$this->transformers = $transformers;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $value;
|
/**
|
||||||
}
|
* Passes the value through the transform() method of all nested transformers
|
||||||
|
*
|
||||||
/**
|
* The transformers receive the value in the same order as they were passed
|
||||||
* Passes the value through the reverseTransform() method of all nested
|
* to the constructor. Each transformer receives the result of the previous
|
||||||
* transformers
|
* transformer as input. The output of the last transformer is returned
|
||||||
*
|
* by this method.
|
||||||
* The transformers receive the value in the reverse order as they were passed
|
*
|
||||||
* to the constructor. Each transformer receives the result of the previous
|
* @param mixed $value The original value
|
||||||
* transformer as input. The output of the last transformer is returned
|
* @return mixed The transformed value
|
||||||
* by this method.
|
*/
|
||||||
*
|
public function transform($value)
|
||||||
* @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)
|
|
||||||
{
|
{
|
||||||
$value = $this->transformers[$i]->reverseTransform($value);
|
foreach ($this->transformers as $transformer) {
|
||||||
|
$value = $transformer->transform($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $value;
|
/**
|
||||||
}
|
* Passes the value through the reverseTransform() method of all nested
|
||||||
|
* transformers
|
||||||
/**
|
*
|
||||||
* {@inheritDoc}
|
* The transformers receive the value in the reverse order as they were passed
|
||||||
*/
|
* to the constructor. Each transformer receives the result of the previous
|
||||||
public function setLocale($locale)
|
* transformer as input. The output of the last transformer is returned
|
||||||
{
|
* by this method.
|
||||||
foreach ($this->transformers as $transformer)
|
*
|
||||||
|
* @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
|
interface ValueTransformerInterface extends Localizable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Transforms a value from the original representation to a transformed
|
* Transforms a value from the original representation to a transformed
|
||||||
* representation.
|
* representation.
|
||||||
*
|
*
|
||||||
* @param mixed $value The value in the original representation
|
* @param mixed $value The value in the original representation
|
||||||
* @return mixed The value in the transformed representation
|
* @return mixed The value in the transformed representation
|
||||||
* @throws InvalidArgument Exception when the argument is no string
|
* @throws InvalidArgument Exception when the argument is no string
|
||||||
* @throws ValueTransformer Exception when the transformation fails
|
* @throws ValueTransformer Exception when the transformation fails
|
||||||
*/
|
*/
|
||||||
public function transform($value);
|
public function transform($value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms a value from the transformed representation to its original
|
* Transforms a value from the transformed representation to its original
|
||||||
* representation.
|
* representation.
|
||||||
*
|
*
|
||||||
* This method must be able to deal with null values.
|
* This method must be able to deal with null values.
|
||||||
*
|
*
|
||||||
* @param mixed $value The value in the transformed representation
|
* @param mixed $value The value in the transformed representation
|
||||||
* @return mixed The value in the original representation
|
* @return mixed The value in the original representation
|
||||||
* @throws InvalidArgument Exception when the argument is not of the
|
* @throws InvalidArgument Exception when the argument is not of the
|
||||||
* expected type
|
* expected type
|
||||||
* @throws ValueTransformer Exception when the transformation fails
|
* @throws ValueTransformer Exception when the transformation fails
|
||||||
*/
|
*/
|
||||||
public function reverseTransform($value);
|
public function reverseTransform($value);
|
||||||
}
|
}
|
@ -9,14 +9,14 @@ namespace Symfony\Components\I18N;
|
|||||||
*/
|
*/
|
||||||
interface TranslatorInterface
|
interface TranslatorInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Translates a given text string.
|
* Translates a given text string.
|
||||||
*
|
*
|
||||||
* @param string $text The text to translate
|
* @param string $text The text to translate
|
||||||
* @param array $parameters The parameters to inject into the text
|
* @param array $parameters The parameters to inject into the text
|
||||||
* @param string $locale The locale of the translated text. If null,
|
* @param string $locale The locale of the translated text. If null,
|
||||||
* the preconfigured locale of the translator
|
* the preconfigured locale of the translator
|
||||||
* or the system's default culture is used.
|
* or the system's default culture is used.
|
||||||
*/
|
*/
|
||||||
public function translate($text, array $parameters = array(), $locale = null);
|
public function translate($text, array $parameters = array(), $locale = null);
|
||||||
}
|
}
|
@ -19,160 +19,145 @@ use Symfony\Components\Validator\Exception\ConstraintDefinitionException;
|
|||||||
*/
|
*/
|
||||||
class Constraint
|
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
|
* Initializes the constraint with options
|
||||||
*
|
*
|
||||||
* You should pass an associative array. The keys should be the names of
|
* 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
|
* existing properties in this class. The values should be the value for these
|
||||||
* properties.
|
* properties.
|
||||||
*
|
*
|
||||||
* Alternatively you can override the method defaultOption() to return the
|
* Alternatively you can override the method defaultOption() to return the
|
||||||
* name of an existing property. If no associative array is passed, this
|
* name of an existing property. If no associative array is passed, this
|
||||||
* property is set instead.
|
* property is set instead.
|
||||||
*
|
*
|
||||||
* You can force that certain options are set by overriding
|
* You can force that certain options are set by overriding
|
||||||
* requiredOptions() to return the names of these options. If any
|
* requiredOptions() to return the names of these options. If any
|
||||||
* option is not set here, an exception is thrown.
|
* option is not set here, an exception is thrown.
|
||||||
*
|
*
|
||||||
* @param mixed $options The options (as associative array)
|
* @param mixed $options The options (as associative array)
|
||||||
* or the value for the default
|
* or the value for the default
|
||||||
* option (any other type)
|
* option (any other type)
|
||||||
* @throws InvalidOptionsException When you pass the names of non-existing
|
* @throws InvalidOptionsException When you pass the names of non-existing
|
||||||
* options
|
* options
|
||||||
* @throws MissingOptionsException When you don't pass any of the options
|
* @throws MissingOptionsException When you don't pass any of the options
|
||||||
* returned by requiredOptions()
|
* returned by requiredOptions()
|
||||||
* @throws ConstraintDefinitionException When you don't pass an associative
|
* @throws ConstraintDefinitionException When you don't pass an associative
|
||||||
* array, but defaultOption() returns
|
* array, but defaultOption() returns
|
||||||
* NULL
|
* NULL
|
||||||
*/
|
*/
|
||||||
public function __construct($options = null)
|
public function __construct($options = null)
|
||||||
{
|
|
||||||
$invalidOptions = array();
|
|
||||||
$missingOptions = array_flip((array)$this->requiredOptions());
|
|
||||||
|
|
||||||
if (is_array($options) && count($options) == 1 && isset($options['value']))
|
|
||||||
{
|
{
|
||||||
$options = $options['value'];
|
$invalidOptions = array();
|
||||||
}
|
$missingOptions = array_flip((array)$this->requiredOptions());
|
||||||
|
|
||||||
if (is_array($options) && count($options) > 0 && is_string(key($options)))
|
if (is_array($options) && count($options) == 1 && isset($options['value'])) {
|
||||||
{
|
$options = $options['value'];
|
||||||
foreach ($options as $option => $value)
|
|
||||||
{
|
|
||||||
if (property_exists($this, $option))
|
|
||||||
{
|
|
||||||
$this->$option = $value;
|
|
||||||
unset($missingOptions[$option]);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
if (is_array($options) && count($options) > 0 && is_string(key($options))) {
|
||||||
$invalidOptions[] = $option;
|
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();
|
throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint %s', $option, get_class($this)), array($option));
|
||||||
|
|
||||||
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)
|
/**
|
||||||
|
* Adds the given group if this constraint is in the Default group
|
||||||
|
*
|
||||||
|
* @param string $group
|
||||||
|
*/
|
||||||
|
public function addImplicitGroupName($group)
|
||||||
{
|
{
|
||||||
throw new InvalidOptionsException(
|
if (in_array(Constraint::DEFAULT_GROUP, $this->groups) && !in_array($group, $this->groups)) {
|
||||||
sprintf('The options "%s" do not exist in constraint %s', implode('", "', $invalidOptions), get_class($this)),
|
$this->groups[] = $group;
|
||||||
$invalidOptions
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
return null;
|
||||||
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;
|
/**
|
||||||
}
|
* Returns the name of the required options
|
||||||
|
*
|
||||||
/**
|
* Override this method if you want to define required options.
|
||||||
* Unsupported operation.
|
*
|
||||||
*/
|
* @return array
|
||||||
public function __set($option, $value)
|
* @see __construct()
|
||||||
{
|
*/
|
||||||
throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint %s', $option, get_class($this)), array($option));
|
public function requiredOptions()
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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))
|
|
||||||
{
|
{
|
||||||
$this->groups[] = $group;
|
return array();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the default option
|
* Returns the name of the class that validates this constraint
|
||||||
*
|
*
|
||||||
* Override this method to define a default option.
|
* By default, this is the fully qualified name of the constraint class
|
||||||
*
|
* suffixed with "Validator". You can override this method to change that
|
||||||
* @return string
|
* behaviour.
|
||||||
* @see __construct()
|
*
|
||||||
*/
|
* @return string
|
||||||
public function defaultOption()
|
*/
|
||||||
{
|
public function validatedBy()
|
||||||
return null;
|
{
|
||||||
}
|
return get_class($this) . 'Validator';
|
||||||
|
}
|
||||||
/**
|
|
||||||
* 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';
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -4,30 +4,30 @@ namespace Symfony\Components\Validator;
|
|||||||
|
|
||||||
abstract class ConstraintValidator implements ConstraintValidatorInterface
|
abstract class ConstraintValidator implements ConstraintValidatorInterface
|
||||||
{
|
{
|
||||||
protected $context;
|
protected $context;
|
||||||
private $messageTemplate;
|
private $messageTemplate;
|
||||||
private $messageParameters;
|
private $messageParameters;
|
||||||
|
|
||||||
public function initialize(ValidationContext $context)
|
public function initialize(ValidationContext $context)
|
||||||
{
|
{
|
||||||
$this->context = $context;
|
$this->context = $context;
|
||||||
$this->messageTemplate = '';
|
$this->messageTemplate = '';
|
||||||
$this->messageParameters = array();
|
$this->messageParameters = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMessageTemplate()
|
public function getMessageTemplate()
|
||||||
{
|
{
|
||||||
return $this->messageTemplate;
|
return $this->messageTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMessageParameters()
|
public function getMessageParameters()
|
||||||
{
|
{
|
||||||
return $this->messageParameters;
|
return $this->messageParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setMessage($template, array $parameters = array())
|
protected function setMessage($template, array $parameters = array())
|
||||||
{
|
{
|
||||||
$this->messageTemplate = $template;
|
$this->messageTemplate = $template;
|
||||||
$this->messageParameters = $parameters;
|
$this->messageParameters = $parameters;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,17 +7,16 @@ use Symfony\Components\Validator\Constraint;
|
|||||||
|
|
||||||
class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface
|
class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface
|
||||||
{
|
{
|
||||||
protected $validators = array();
|
protected $validators = array();
|
||||||
|
|
||||||
public function getInstance(Constraint $constraint)
|
public function getInstance(Constraint $constraint)
|
||||||
{
|
|
||||||
$className = $constraint->validatedBy();
|
|
||||||
|
|
||||||
if (!isset($this->validators[$className]))
|
|
||||||
{
|
{
|
||||||
$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
|
interface ConstraintValidatorFactoryInterface
|
||||||
{
|
{
|
||||||
public function getInstance(Constraint $constraint);
|
public function getInstance(Constraint $constraint);
|
||||||
}
|
}
|
@ -4,11 +4,11 @@ namespace Symfony\Components\Validator;
|
|||||||
|
|
||||||
interface ConstraintValidatorInterface
|
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
|
class ConstraintViolation
|
||||||
{
|
{
|
||||||
protected $message;
|
protected $message;
|
||||||
protected $root;
|
protected $root;
|
||||||
protected $propertyPath;
|
protected $propertyPath;
|
||||||
protected $invalidValue;
|
protected $invalidValue;
|
||||||
|
|
||||||
public function __construct($message, $root, $propertyPath, $invalidValue)
|
public function __construct($message, $root, $propertyPath, $invalidValue)
|
||||||
{
|
{
|
||||||
$this->message = $message;
|
$this->message = $message;
|
||||||
$this->root = $root;
|
$this->root = $root;
|
||||||
$this->propertyPath = $propertyPath;
|
$this->propertyPath = $propertyPath;
|
||||||
$this->invalidValue = $invalidValue;
|
$this->invalidValue = $invalidValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMessage()
|
public function getMessage()
|
||||||
{
|
{
|
||||||
return $this->message;
|
return $this->message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoot()
|
public function getRoot()
|
||||||
{
|
{
|
||||||
return $this->root;
|
return $this->root;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPropertyPath()
|
public function getPropertyPath()
|
||||||
{
|
{
|
||||||
return $this->propertyPath;
|
return $this->propertyPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getInvalidValue()
|
public function getInvalidValue()
|
||||||
{
|
{
|
||||||
return $this->invalidValue;
|
return $this->invalidValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,46 +4,44 @@ namespace Symfony\Components\Validator;
|
|||||||
|
|
||||||
class ConstraintViolationList implements \IteratorAggregate, \Countable
|
class ConstraintViolationList implements \IteratorAggregate, \Countable
|
||||||
{
|
{
|
||||||
protected $violations = array();
|
protected $violations = array();
|
||||||
|
|
||||||
public function __toString()
|
public function __toString()
|
||||||
{
|
|
||||||
$string = '';
|
|
||||||
|
|
||||||
foreach ($this->violations as $violation)
|
|
||||||
{
|
{
|
||||||
$param = $violation->getMessageParameters();
|
$string = '';
|
||||||
$message = str_replace(array_keys($param), $param, $violation->getMessageTemplate());
|
|
||||||
$string .= <<<EOF
|
foreach ($this->violations as $violation) {
|
||||||
|
$param = $violation->getMessageParameters();
|
||||||
|
$message = str_replace(array_keys($param), $param, $violation->getMessageTemplate());
|
||||||
|
$string .= <<<EOF
|
||||||
{$violation->getRoot()}.{$violation->getPropertyPath()}:
|
{$violation->getRoot()}.{$violation->getPropertyPath()}:
|
||||||
$message
|
$message
|
||||||
|
|
||||||
EOF;
|
EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $string;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $string;
|
public function add(ConstraintViolation $violation)
|
||||||
}
|
|
||||||
|
|
||||||
public function add(ConstraintViolation $violation)
|
|
||||||
{
|
|
||||||
$this->violations[] = $violation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addAll(ConstraintViolationList $violations)
|
|
||||||
{
|
|
||||||
foreach ($violations->violations as $violation)
|
|
||||||
{
|
{
|
||||||
$this->violations[] = $violation;
|
$this->violations[] = $violation;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function getIterator()
|
public function addAll(ConstraintViolationList $violations)
|
||||||
{
|
{
|
||||||
return new \ArrayIterator($this->violations);
|
foreach ($violations->violations as $violation) {
|
||||||
}
|
$this->violations[] = $violation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function count()
|
public function getIterator()
|
||||||
{
|
{
|
||||||
return count($this->violations);
|
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
|
class All extends \Symfony\Components\Validator\Constraint
|
||||||
{
|
{
|
||||||
public $constraints = array();
|
public $constraints = array();
|
||||||
|
|
||||||
public function defaultOption()
|
public function defaultOption()
|
||||||
{
|
{
|
||||||
return 'constraints';
|
return 'constraints';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function requiredOptions()
|
public function requiredOptions()
|
||||||
{
|
{
|
||||||
return array('constraints');
|
return array('constraints');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,34 +8,30 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
|
|||||||
|
|
||||||
class AllValidator extends ConstraintValidator
|
class AllValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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
|
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
|
class AssertFalseValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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
|
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
|
class AssertTrueValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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
|
class AssertType extends \Symfony\Components\Validator\Constraint
|
||||||
{
|
{
|
||||||
public $message = 'Symfony.Validator.AssertType.message';
|
public $message = 'Symfony.Validator.AssertType.message';
|
||||||
public $type;
|
public $type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function defaultOption()
|
public function defaultOption()
|
||||||
{
|
{
|
||||||
return 'type';
|
return 'type';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function requiredOptions()
|
public function requiredOptions()
|
||||||
{
|
{
|
||||||
return array('type');
|
return array('type');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,30 +7,26 @@ use Symfony\Components\Validator\ConstraintValidator;
|
|||||||
|
|
||||||
class AssertTypeValidator extends ConstraintValidator
|
class AssertTypeValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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
|
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
|
class BlankValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value !== '' && $value !== null)
|
|
||||||
{
|
{
|
||||||
$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
|
class Choice extends \Symfony\Components\Validator\Constraint
|
||||||
{
|
{
|
||||||
public $choices;
|
public $choices;
|
||||||
public $callback;
|
public $callback;
|
||||||
public $multiple = false;
|
public $multiple = false;
|
||||||
public $min = null;
|
public $min = null;
|
||||||
public $max = null;
|
public $max = null;
|
||||||
public $message = 'Symfony.Validator.Choice.message';
|
public $message = 'Symfony.Validator.Choice.message';
|
||||||
public $minMessage = 'Symfony.Validator.Choice.minMessage';
|
public $minMessage = 'Symfony.Validator.Choice.minMessage';
|
||||||
public $maxMessage = 'Symfony.Validator.Choice.maxMessage';
|
public $maxMessage = 'Symfony.Validator.Choice.maxMessage';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function defaultOption()
|
public function defaultOption()
|
||||||
{
|
{
|
||||||
return 'choices';
|
return 'choices';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,78 +24,60 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
|
|||||||
*/
|
*/
|
||||||
class ChoiceValidator extends ConstraintValidator
|
class ChoiceValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if (!$constraint->choices && !$constraint->callback)
|
|
||||||
{
|
{
|
||||||
throw new ConstraintDefinitionException('Either "choices" or "callback" must be specified on constraint Choice');
|
if (!$constraint->choices && !$constraint->callback) {
|
||||||
}
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$count = count($value);
|
if ($value === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if ($constraint->min !== null && $count < $constraint->min)
|
if ($constraint->multiple && !is_array($value)) {
|
||||||
{
|
throw new UnexpectedTypeException($value, 'array');
|
||||||
$this->setMessage($constraint->minMessage, array('limit' => $constraint->min));
|
}
|
||||||
|
|
||||||
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)
|
if ($constraint->multiple) {
|
||||||
{
|
foreach ($value as $_value) {
|
||||||
$this->setMessage($constraint->maxMessage, array('limit' => $constraint->max));
|
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
|
class Collection extends \Symfony\Components\Validator\Constraint
|
||||||
{
|
{
|
||||||
public $fields;
|
public $fields;
|
||||||
public $allowExtraFields = false;
|
public $allowExtraFields = false;
|
||||||
public $allowMissingFields = false;
|
public $allowMissingFields = false;
|
||||||
public $extraFieldsMessage = 'Symfony.Validator.Collection.extraFieldsMessage';
|
public $extraFieldsMessage = 'Symfony.Validator.Collection.extraFieldsMessage';
|
||||||
public $missingFieldsMessage = 'Symfony.Validator.Collection.missingFieldsMessage';
|
public $missingFieldsMessage = 'Symfony.Validator.Collection.missingFieldsMessage';
|
||||||
|
|
||||||
public function requiredOptions()
|
public function requiredOptions()
|
||||||
{
|
{
|
||||||
return array('fields');
|
return array('fields');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,69 +9,59 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
|
|||||||
|
|
||||||
class CollectionValidator extends ConstraintValidator
|
class CollectionValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
return true;
|
if ($value === null) {
|
||||||
}
|
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.']');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unset($extraFields[$field]);
|
if (!is_array($value) && !($value instanceof \Traversable && $value instanceof \ArrayAccess)) {
|
||||||
}
|
throw new UnexpectedTypeException($value, 'array or Traversable and ArrayAccess');
|
||||||
else
|
}
|
||||||
{
|
|
||||||
$missingFields[] = $field;
|
$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
|
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
|
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
|
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)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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
|
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)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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
|
class Email extends \Symfony\Components\Validator\Constraint
|
||||||
{
|
{
|
||||||
public $message = 'Symfony.Validator.Email.message';
|
public $message = 'Symfony.Validator.Email.message';
|
||||||
public $checkMX = false;
|
public $checkMX = false;
|
||||||
}
|
}
|
@ -8,81 +8,71 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
|
|||||||
|
|
||||||
class EmailValidator extends ConstraintValidator
|
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)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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))
|
|
||||||
{
|
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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
|
class File extends \Symfony\Components\Validator\Constraint
|
||||||
{
|
{
|
||||||
public $maxSize = null;
|
public $maxSize = null;
|
||||||
public $mimeTypes = array();
|
public $mimeTypes = array();
|
||||||
public $notFoundMessage = 'Symfony.Validator.File.notFoundMessage';
|
public $notFoundMessage = 'Symfony.Validator.File.notFoundMessage';
|
||||||
public $notReadableMessage = 'Symfony.Validator.File.notReadableMessage';
|
public $notReadableMessage = 'Symfony.Validator.File.notReadableMessage';
|
||||||
public $maxSizeMessage = 'Symfony.Validator.File.maxSizeMessage';
|
public $maxSizeMessage = 'Symfony.Validator.File.maxSizeMessage';
|
||||||
public $mimeTypesMessage = 'Symfony.Validator.File.mimeTypesMessage';
|
public $mimeTypesMessage = 'Symfony.Validator.File.mimeTypesMessage';
|
||||||
}
|
}
|
||||||
|
@ -10,90 +10,74 @@ use Symfony\Components\File\File;
|
|||||||
|
|
||||||
class FileValidator extends ConstraintValidator
|
class FileValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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
|
class Max extends \Symfony\Components\Validator\Constraint
|
||||||
{
|
{
|
||||||
public $message = 'Symfony.Validator.Max.message';
|
public $message = 'Symfony.Validator.Max.message';
|
||||||
public $limit;
|
public $limit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function defaultOption()
|
public function defaultOption()
|
||||||
{
|
{
|
||||||
return 'limit';
|
return 'limit';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function requiredOptions()
|
public function requiredOptions()
|
||||||
{
|
{
|
||||||
return array('limit');
|
return array('limit');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,23 +4,23 @@ namespace Symfony\Components\Validator\Constraints;
|
|||||||
|
|
||||||
class MaxLength extends \Symfony\Components\Validator\Constraint
|
class MaxLength extends \Symfony\Components\Validator\Constraint
|
||||||
{
|
{
|
||||||
public $message = 'Symfony.Validator.MaxLength.message';
|
public $message = 'Symfony.Validator.MaxLength.message';
|
||||||
public $limit;
|
public $limit;
|
||||||
public $charset = 'UTF-8';
|
public $charset = 'UTF-8';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function defaultOption()
|
public function defaultOption()
|
||||||
{
|
{
|
||||||
return 'limit';
|
return 'limit';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function requiredOptions()
|
public function requiredOptions()
|
||||||
{
|
{
|
||||||
return array('limit');
|
return array('limit');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,32 +8,29 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
|
|||||||
|
|
||||||
class MaxLengthValidator extends ConstraintValidator
|
class MaxLengthValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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
|
class MaxValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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
|
class Min extends \Symfony\Components\Validator\Constraint
|
||||||
{
|
{
|
||||||
public $message = 'Symfony.Validator.Min.message';
|
public $message = 'Symfony.Validator.Min.message';
|
||||||
public $limit;
|
public $limit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function defaultOption()
|
public function defaultOption()
|
||||||
{
|
{
|
||||||
return 'limit';
|
return 'limit';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function requiredOptions()
|
public function requiredOptions()
|
||||||
{
|
{
|
||||||
return array('limit');
|
return array('limit');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,23 +4,23 @@ namespace Symfony\Components\Validator\Constraints;
|
|||||||
|
|
||||||
class MinLength extends \Symfony\Components\Validator\Constraint
|
class MinLength extends \Symfony\Components\Validator\Constraint
|
||||||
{
|
{
|
||||||
public $message = 'Symfony.Validator.MinLength.message';
|
public $message = 'Symfony.Validator.MinLength.message';
|
||||||
public $limit;
|
public $limit;
|
||||||
public $charset = 'UTF-8';
|
public $charset = 'UTF-8';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function defaultOption()
|
public function defaultOption()
|
||||||
{
|
{
|
||||||
return 'limit';
|
return 'limit';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function requiredOptions()
|
public function requiredOptions()
|
||||||
{
|
{
|
||||||
return array('limit');
|
return array('limit');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,32 +8,29 @@ use Symfony\Components\Validator\Exception\UnexpectedTypeException;
|
|||||||
|
|
||||||
class MinLengthValidator extends ConstraintValidator
|
class MinLengthValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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
|
class MinValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
public function isValid($value, Constraint $constraint)
|
public function isValid($value, Constraint $constraint)
|
||||||
{
|
|
||||||
if ($value === null)
|
|
||||||
{
|
{
|
||||||
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
|
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
Block a user