Issue #2002102 by Berdir, fago, dixon_: Move TypedData primitive types to interfaces.

8.0.x
Alex Pott 2013-06-30 11:03:43 +01:00
parent c56cb20453
commit 2e9c7fefd6
32 changed files with 614 additions and 339 deletions

View File

@ -85,17 +85,10 @@ class Context extends ComponentContext {
* Overrides \Drupal\Component\Plugin\Context\Context::getConstraints().
*/
public function validate() {
$validator = Validation::createValidatorBuilder()
->setTranslator(new DrupalTranslator())
->getValidator();
// @todo We won't need to special case "entity" here once #1868004 lands.
if (!empty($this->contextDefinition['type']) && $this->contextDefinition['type'] == 'entity') {
$value = $this->getTypedContext();
// If the context is typed data, defer to its validation.
if (!empty($this->contextDefinition['type'])) {
return $this->getTypedContext()->validate();
}
else {
$value = $this->getContextValue();
}
return $validator->validateValue($value, $this->getConstraints());
return parent::validate();
}
}

View File

@ -7,6 +7,8 @@
namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\PrimitiveBase;
use Drupal\Core\TypedData\Type\BinaryInterface;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\TypedData\TypedData;
@ -20,11 +22,10 @@ use Drupal\Core\TypedData\TypedData;
*
* @DataType(
* id = "binary",
* label = @Translation("Binary"),
* primitive_type = 8
* label = @Translation("Binary")
* )
*/
class Binary extends TypedData {
class Binary extends PrimitiveBase implements BinaryInterface {
/**
* The file resource URI.

View File

@ -9,7 +9,8 @@ namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\TypedData\TypedData;
use Drupal\Core\TypedData\PrimitiveBase;
use Drupal\Core\TypedData\Type\BooleanInterface;
/**
* The boolean data type.
@ -19,16 +20,9 @@ use Drupal\Core\TypedData\TypedData;
*
* @DataType(
* id = "boolean",
* label = @Translation("Boolean"),
* primitive_type = 1
* label = @Translation("Boolean")
* )
*/
class Boolean extends TypedData {
class Boolean extends PrimitiveBase implements BooleanInterface {
/**
* The data value.
*
* @var boolean
*/
protected $value;
}

View File

@ -1,55 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Plugin\DataType\Date.
*/
namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\TypedData\TypedData;
/**
* The date data type.
*
* The plain value of a date is an instance of the DrupalDateTime class. For
* setting the value any value supported by the __construct() of the
* DrupalDateTime class will work, including a DateTime object, a timestamp, a
* string date, or an array of date parts.
*
* @DataType(
* id = "date",
* label = @Translation("Date"),
* primitive_type = 5
* )
*/
class Date extends TypedData {
/**
* The data value.
*
* @var DateTime
*/
protected $value;
/**
* Overrides TypedData::setValue().
*/
public function setValue($value, $notify = TRUE) {
// Notify the parent of any changes to be made.
if ($notify && isset($this->parent)) {
$this->parent->onChange($this->name);
}
// Don't try to create a date from an empty value.
// It would default to the current time.
if (!isset($value)) {
$this->value = $value;
}
else {
$this->value = $value instanceOf DrupalDateTime ? $value : new DrupalDateTime($value);
}
}
}

View File

@ -0,0 +1,43 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601.
*/
namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\TypedData\Type\DateTimeInterface;
/**
* A data type for ISO 8601 date strings.
*
* The plain value of this data type is a date string in ISO 8601 format.
*
* @DataType(
* id = "datetime_iso8601",
* label = @Translation("Date")
* )
*/
class DateTimeIso8601 extends String implements DateTimeInterface {
/**
* {@inheritdoc}
*/
public function getDateTime() {
if ($this->value) {
return new DrupalDateTime($this->value);
}
}
/**
* {@inheritdoc}
*/
public function setDateTime(DrupalDateTime $dateTime) {
$this->value = $dateTime->format('c');
}
}

View File

@ -1,81 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Plugin\DataType\Duration.
*/
namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\TypedData\TypedData;
use DateInterval;
/**
* The duration data type.
*
* The plain value of a duration is an instance of the DateInterval class. For
* setting the value an instance of the DateInterval class, a ISO8601 string as
* supported by DateInterval::__construct, or an integer in seconds may be
* passed.
*
* @DataType(
* id = "duration",
* label = @Translation("Duration"),
* primitive_type = 6
* )
*/
class Duration extends TypedData {
/**
* The data value.
*
* @var \DateInterval
*/
protected $value;
/**
* Overrides TypedData::setValue().
*/
public function setValue($value, $notify = TRUE) {
// Notify the parent of any changes to be made.
if ($notify && isset($this->parent)) {
$this->parent->onChange($this->name);
}
// Catch any exceptions thrown due to invalid values being passed.
try {
if ($value instanceof DateInterval || !isset($value)) {
$this->value = $value;
}
// Treat integer values as time spans in seconds, even if supplied as PHP
// string.
elseif ((string) (int) $value === (string) $value) {
$this->value = new DateInterval('PT' . $value . 'S');
}
elseif (is_string($value)) {
// @todo: Add support for negative intervals on top of the DateInterval
// constructor.
$this->value = new DateInterval($value);
}
else {
// Unknown value given.
$this->value = $value;
}
}
catch (\Exception $e) {
// An invalid value has been given. Setting any invalid value will let
// validation fail.
$this->value = $e;
}
}
/**
* Overrides TypedData::getString().
*/
public function getString() {
// Generate an ISO 8601 formatted string as supported by
// DateInterval::__construct() and setValue().
return (string) $this->getValue()->format('%rP%yY%mM%dDT%hH%mM%sS');
}
}

View File

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Plugin\DataType\DurationIso8601.
*/
namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\TypedData\Type\DurationInterface;
/**
* The duration ISO8601 data type.
*
* The plain value of this data type is a ISO8601 duration string.
*
* @DataType(
* id = "duration_iso8601",
* label = @Translation("Duration")
* )
*/
class DurationIso8601 extends String implements DurationInterface {
/**
* {@inheritdoc}
*/
public function getDuration() {
if ($this->value) {
// @todo: Add support for negative intervals on top of the DateInterval
// constructor.
return new \DateInterval($this->value);
}
}
/**
* {@inheritdoc}
*/
public function setDuration(\DateInterval $duration) {
// Generate an ISO 8601 formatted string as supported by
// DateInterval::__construct() and setValue().
$this->value = $duration->format('%rP%yY%mM%dDT%hH%mM%sS');
}
}

View File

@ -9,6 +9,7 @@ namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\TypedData\Type\StringInterface;
/**
* The Email data type.
@ -18,10 +19,9 @@ use Drupal\Core\Annotation\Translation;
* @DataType(
* id = "email",
* label = @Translation("Email"),
* primitive_type = 2,
* constraints = {"Email" = TRUE}
* )
*/
class Email extends String {
class Email extends String implements StringInterface {
}

View File

@ -9,7 +9,8 @@ namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\TypedData\TypedData;
use Drupal\Core\TypedData\PrimitiveBase;
use Drupal\Core\TypedData\Type\FloatInterface;
/**
* The float data type.
@ -19,16 +20,9 @@ use Drupal\Core\TypedData\TypedData;
*
* @DataType(
* id = "float",
* label = @Translation("Float"),
* primitive_type = 4
* label = @Translation("Float")
* )
*/
class Float extends TypedData {
class Float extends PrimitiveBase implements FloatInterface {
/**
* The data value.
*
* @var float
*/
protected $value;
}

View File

@ -9,7 +9,8 @@ namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\TypedData\TypedData;
use Drupal\Core\TypedData\PrimitiveBase;
use Drupal\Core\TypedData\Type\IntegerInterface;
/**
* The integer data type.
@ -19,16 +20,9 @@ use Drupal\Core\TypedData\TypedData;
*
* @DataType(
* id = "integer",
* label = @Translation("Integer"),
* primitive_type = 3
* label = @Translation("Integer")
* )
*/
class Integer extends TypedData {
class Integer extends PrimitiveBase implements IntegerInterface {
/**
* The data value.
*
* @var integer
*/
protected $value;
}

View File

@ -9,7 +9,8 @@ namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\TypedData\TypedData;
use Drupal\Core\TypedData\PrimitiveBase;
use Drupal\Core\TypedData\Type\StringInterface;
/**
* The string data type.
@ -19,16 +20,9 @@ use Drupal\Core\TypedData\TypedData;
*
* @DataType(
* id = "string",
* label = @Translation("String"),
* primitive_type = 2
* label = @Translation("String")
* )
*/
class String extends TypedData {
class String extends PrimitiveBase implements StringInterface {
/**
* The data value.
*
* @var string
*/
protected $value;
}

View File

@ -0,0 +1,58 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Type\TimeSpan.
*/
namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\TypedData\Type\DurationInterface;
/**
* The time span data type represents durations as number of seconds.
*
* The plain value is the (integer) number of seconds. Note that time spans only
* map correctly to durations as long as the number of seconds does not exceed
* a day (there is already a difference in applying a duration of a day or 24
* hours due to daylight savings). If that's an issue, consider using
* \Drupal\Core\TypedData\Type\DurationIso8601 instead.
*
* @DataType(
* id = "timespan",
* label = @Translation("Time span in seconds")
* )
*
* @see \Drupal\Core\TypedData\Type\DurationIso8601
*/
class TimeSpan extends Integer implements DurationInterface {
/**
* {@inheritdoc}
*/
public function getDuration() {
if ($this->value) {
// Keep the duration in seconds as there is generally no valid way to
// convert it to days, months or years.
return new \DateInterval('PT' . $this->value . 'S');
}
}
/**
* {@inheritdoc}
*/
public function setDuration(\DateInterval $duration) {
// Note that this applies the assumption of 12 month's a 30 days and
// each year having 365 days. There is no accurate conversion for time spans
// exceeding a day.
$this->value = ($duration->y * 365 * 24 * 60 * 60) +
($duration->m * 30 * 24 * 60 * 60) +
($duration->d * 24 * 60 * 60) +
($duration->h * 60 * 60) +
($duration->i * 60) +
$duration->s;
}
}

View File

@ -0,0 +1,47 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Type\Timestamp.
*/
namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\TypedData\Type\DateTimeInterface;
/**
* The timestamp data type.
*
* @DataType(
* id = "timestamp",
* label = @Translation("String")
* )
*/
class Timestamp extends Integer implements DateTimeInterface {
/**
* The data value as a UNIX timestamp.
*
* @var integer
*/
protected $value;
/**
* {@inheritdoc}
*/
public function getDateTime() {
if ($this->value) {
return new DrupalDateTime($this->value);
}
}
/**
* {@inheritdoc}
*/
public function setDateTime(DrupalDateTime $dateTime) {
$this->value = $dateTime->getTimestamp();
}
}

View File

@ -9,6 +9,8 @@ namespace Drupal\Core\TypedData\Plugin\DataType;
use Drupal\Core\TypedData\Annotation\DataType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\TypedData\PrimitiveBase;
use Drupal\Core\TypedData\Type\UriInterface;
use Drupal\Core\TypedData\TypedData;
/**
@ -18,16 +20,9 @@ use Drupal\Core\TypedData\TypedData;
*
* @DataType(
* id = "uri",
* label = @Translation("URI"),
* primitive_type = 7
* label = @Translation("URI")
* )
*/
class Uri extends TypedData {
class Uri extends PrimitiveBase implements UriInterface {
/**
* The data value.
*
* @var string
*/
protected $value;
}

View File

@ -1,70 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Primitive.
*/
namespace Drupal\Core\TypedData;
/**
* Class that holds constants for all primitive data types.
*/
final class Primitive {
/**
* The BOOLEAN primitive data type.
*
* @see \Drupal\Core\TypedData\Plugin\DataType\Boolean
*/
const BOOLEAN = 1;
/**
* The STRING primitive data type.
*
* @see \Drupal\Core\TypedData\Plugin\DataType\String
*/
const STRING = 2;
/**
* The INTEGER primitive data type.
*
* @see \Drupal\Core\TypedData\Plugin\DataType\Integer
*/
const INTEGER = 3;
/**
* The FLOAT primitive data type.
*
* @see \Drupal\Core\TypedData\Plugin\DataType\Float
*/
const FLOAT = 4;
/**
* The DATE primitive data type.
*
* @see \Drupal\Core\TypedData\Plugin\DataType\Date
*/
const DATE = 5;
/**
* The DURATION primitive data type.
*
* @see \Drupal\Core\TypedData\Plugin\DataType\Duration
*/
const DURATION = 6;
/**
* The URI primitive data type.
*
* @see \Drupal\Core\TypedData\Plugin\DataType\Uri
*/
const URI = 7;
/**
* The BINARY primitive data type.
*
* @see \Drupal\Core\TypedData\Plugin\DataType\Binary
*/
const BINARY = 8;
}

View File

@ -0,0 +1,39 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\PrimitiveBase.
*/
namespace Drupal\Core\TypedData;
/**
* Base class for primitive data types.
*/
abstract class PrimitiveBase extends TypedData implements PrimitiveInterface {
/**
* The data value.
*
* @var mixed
*/
protected $value;
/**
* {@inheritdoc}
*/
public function getValue() {
return $this->value;
}
/**
* {@inheritdoc}
*/
public function setValue($value, $notify = TRUE) {
// Notify the parent of any changes to be made.
if ($notify && isset($this->parent)) {
$this->parent->onChange($this->name);
}
$this->value = $value;
}
}

View File

@ -0,0 +1,31 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\PrimitiveInterface.
*/
namespace Drupal\Core\TypedData;
/**
* Interface for primitive data.
*/
interface PrimitiveInterface {
/**
* Gets the primitive data value.
*
* @return mixed
*/
public function getValue();
/**
* Sets the primitive data value.
*
* @param mixed|null $value
* The value to set in the format as documented for the data type or NULL to
* unset the data value.
*/
public function setValue($value);
}

View File

@ -0,0 +1,21 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Type\BinaryInterface.
*/
namespace Drupal\Core\TypedData\Type;
use Drupal\Core\TypedData\PrimitiveInterface;
/**
* Interface for binary data.
*
* The plain value of binary data is a PHP file resource, see
* http://php.net/manual/en/language.types.resource.php. For setting the value
* a PHP file resource or a (absolute) stream resource URI may be passed.
*/
interface BinaryInterface extends PrimitiveInterface {
}

View File

@ -0,0 +1,17 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Type\BooleanInterface.
*/
namespace Drupal\Core\TypedData\Type;
use Drupal\Core\TypedData\PrimitiveInterface;
/**
* Interface for boolean data.
*/
interface BooleanInterface extends PrimitiveInterface {
}

View File

@ -0,0 +1,33 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Type\DateTimeInterface.
*/
namespace Drupal\Core\TypedData\Type;
use Drupal\Core\Datetime\DrupalDateTime;
/**
* Interface for dates, optionally including a time.
*/
interface DateTimeInterface {
/**
* Returns the date time object.
*
* @return \Drupal\Core\Datetime\DrupalDateTime|null
* A date object or NULL if there is no date.
*/
public function getDateTime();
/**
* Sets the date time object.
*
* @param \Drupal\Core\Datetime\DrupalDateTime $dateTime
* An instance of a date time object.
*/
public function setDateTime(DrupalDateTime $dateTime);
}

View File

@ -0,0 +1,35 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Type\BinaryInterface.
*/
namespace Drupal\Core\TypedData\Type;
use Drupal\Core\TypedData\PrimitiveInterface;
/**
* Interface for binary data.
*/
interface DurationInterface extends PrimitiveInterface {
/**
* Returns the duration.
*
* @return \DateInterval|null
* A DateInterval object or NULL if there is no duration.
*
* @throws \Exception
*/
public function getDuration();
/**
* Sets the duration.
*
* @param \DateInterval $duration
* A duration to set.
*/
public function setDuration(\DateInterval $duration);
}

View File

@ -0,0 +1,20 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Type\FloatInterface.
*/
namespace Drupal\Core\TypedData\Type;
use Drupal\Core\TypedData\PrimitiveInterface;
/**
* Interface for floating-point numbers.
*
* The plain value of a float is a regular PHP float. For setting the value
* any PHP variable that casts to a float may be passed.
*/
interface FloatInterface extends PrimitiveInterface {
}

View File

@ -0,0 +1,20 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Type\IntegerInterface.
*/
namespace Drupal\Core\TypedData\Type;
use Drupal\Core\TypedData\PrimitiveInterface;
/**
* Interface for integer numbers.
*
* The plain value of an integer is a regular PHP integer. For setting the value
* any PHP variable that casts to an integer may be passed.
*/
interface IntegerInterface extends PrimitiveInterface {
}

View File

@ -0,0 +1,20 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Type\StringInterface.
*/
namespace Drupal\Core\TypedData\Type;
use Drupal\Core\TypedData\PrimitiveInterface;
/**
* Interface for strings.
*
* The plain value of a string is a regular PHP string. For setting the value
* any PHP variable that casts to a string may be passed.
*/
interface StringInterface extends PrimitiveInterface {
}

View File

@ -0,0 +1,19 @@
<?php
/**
* @file
* Contains \Drupal\Core\TypedData\Type\UriInterface.
*/
namespace Drupal\Core\TypedData\Type;
use Drupal\Core\TypedData\PrimitiveInterface;
/**
* Interface for URIs.
*
* The plain value of a URI is an absolute URI represented as PHP string.
*/
interface UriInterface extends PrimitiveInterface {
}

View File

@ -7,6 +7,7 @@
namespace Drupal\Core\TypedData;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManager;
@ -356,9 +357,10 @@ class TypedDataManager extends DefaultPluginManager {
$validation_manager = $this->getValidationConstraintManager();
$type_definition = $this->getDefinition($definition['type']);
// Auto-generate a constraint for the primitive type if we have a mapping.
if (isset($type_definition['primitive_type'])) {
$constraints[] = $validation_manager->create('PrimitiveType', array('type' => $type_definition['primitive_type']));
// Auto-generate a constraint for data types implementing a primitive
// interface.
if (is_subclass_of($type_definition['class'], '\Drupal\Core\TypedData\PrimitiveInterface')) {
$constraints[] = $validation_manager->create('PrimitiveType', array());
}
// Add in constraints specified by the data type.
if (isset($type_definition['constraints'])) {

View File

@ -91,4 +91,14 @@ class Metadata implements PropertyMetadataInterface {
public function getPropertyValue($container) {
return $this->typedData->getValue();
}
/**
* Returns the typed data object.
*
* @return \Drupal\Core\TypedData\TypedDataInterface
* The typed data object.
*/
public function getTypedData() {
return $this->typedData;
}
}

View File

@ -9,19 +9,17 @@ namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Drupal\Component\Annotation\Plugin;
use Drupal\Core\Annotation\Translation;
use Symfony\Component\Validator\Constraints\Type as SymfonyConstraint;
use Symfony\Component\Validator\Constraint;
/**
* Supports validating all primitive types.
*
* @todo: Move this below the TypedData core component.
*
* @Plugin(
* id = "PrimitiveType",
* label = @Translation("Primitive type", context = "Validation")
* )
*/
class PrimitiveTypeConstraint extends SymfonyConstraint {
class PrimitiveTypeConstraint extends Constraint {
public $message = 'This value should be of type %type.';
public $message = 'This value should be of the correct primitive type.';
}

View File

@ -8,8 +8,14 @@
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use DateInterval;
use Drupal\Core\TypedData\Primitive;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\TypedData\Type\BinaryInterface;
use Drupal\Core\TypedData\Type\BooleanInterface;
use Drupal\Core\TypedData\Type\DateTimeInterface;
use Drupal\Core\TypedData\Type\DurationInterface;
use Drupal\Core\TypedData\Type\FloatInterface;
use Drupal\Core\TypedData\Type\IntegerInterface;
use Drupal\Core\TypedData\Type\StringInterface;
use Drupal\Core\TypedData\Type\UriInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
@ -27,42 +33,44 @@ class PrimitiveTypeConstraintValidator extends ConstraintValidator {
return;
}
switch ($constraint->type) {
case Primitive::BINARY:
$valid = is_resource($value);
break;
case Primitive::BOOLEAN:
$valid = is_bool($value) || $value === 0 || $value === '0' || $value === 1 || $value == '1';
break;
case Primitive::DATE:
$valid = $value instanceOf DrupalDateTime && !$value->hasErrors();
break;
case Primitive::DURATION:
$valid = $value instanceof DateInterval;
break;
case Primitive::FLOAT:
$valid = filter_var($value, FILTER_VALIDATE_FLOAT) !== FALSE;
break;
case Primitive::INTEGER:
$valid = filter_var($value, FILTER_VALIDATE_INT) !== FALSE;
break;
case Primitive::STRING:
// PHP integers, floats or booleans are valid strings also, so we
// cannot use is_string() here.
$valid = is_scalar($value);
break;
case Primitive::URI:
$valid = filter_var($value, FILTER_VALIDATE_URL) ;
break;
default:
$typed_data = $this->context->getMetadata()->getTypedData();
$valid = TRUE;
if ($typed_data instanceof BinaryInterface && !is_resource($value)) {
$valid = FALSE;
}
if ($typed_data instanceof BooleanInterface && !(is_bool($value) || $value === 0 || $value === '0' || $value === 1 || $value == '1')) {
$valid = FALSE;
}
if ($typed_data instanceof FloatInterface && filter_var($value, FILTER_VALIDATE_FLOAT) === FALSE) {
$valid = FALSE;
}
if ($typed_data instanceof IntegerInterface && filter_var($value, FILTER_VALIDATE_INT) === FALSE) {
$valid = FALSE;
}
if ($typed_data instanceof StringInterface && !is_scalar($value)) {
$valid = FALSE;
}
if ($typed_data instanceof UriInterface && filter_var($value, FILTER_VALIDATE_URL) === FALSE) {
$valid = FALSE;
}
// @todo: Move those to separate constraint validators.
try {
if ($typed_data instanceof DateTimeInterface && $typed_data->getDateTime()->hasErrors()) {
$valid = FALSE;
break;
}
if ($typed_data instanceof DurationInterface && !($typed_data->getDuration() instanceof DateInterval)) {
$valid = FALSE;
}
}
catch (\Exception $e) {
// Invalid durations or dates might throw exceptions.
$valid = FALSE;
}
if (!$valid) {
// @todo: Provide a good violation message for each problem.
$this->context->addViolation($constraint->message, array(
'%value' => is_object($value) ? get_class($value) : (is_array($value) ? 'Array' : (string) $value),
'%type' => $constraint->type,
'%value' => is_object($value) ? get_class($value) : (is_array($value) ? 'Array' : (string) $value)
));
}
}

View File

@ -30,7 +30,7 @@ class DateTimeItem extends LegacyConfigFieldItem {
if (!isset(self::$propertyDefinitions)) {
self::$propertyDefinitions['value'] = array(
'type' => 'date',
'type' => 'datetime_iso8601',
'label' => t('Date value'),
);
}

View File

@ -132,47 +132,97 @@ class TypedDataTest extends DrupalUnitTestBase {
$typed_data->setValue('invalid');
$this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
// Date type.
$value = new DrupalDateTime();
$typed_data = $this->createTypedData(array('type' => 'date'), $value);
$this->assertTrue($typed_data->getValue() === $value, 'Date value was fetched.');
// Date Time type.
$value = '2014-01-01T20:00:00+00:00';
$typed_data = $this->createTypedData(array('type' => 'datetime_iso8601'), $value);
$this->assertTrue($typed_data->getValue() == $value, 'Date value was fetched.');
$this->assertEqual($typed_data->getValue(), $typed_data->getDateTime()->format('c'), 'Value representation of a date is ISO 8601');
$this->assertEqual($typed_data->validate()->count(), 0);
$new_value = '2014-01-02T20:00:00+00:00';
$typed_data->setValue($new_value);
$this->assertTrue($typed_data->getDateTime()->format('c') === $new_value, 'Date value was changed and set by an ISO8601 date.');
$this->assertEqual($typed_data->validate()->count(), 0);
$this->assertTrue($typed_data->getDateTime()->format('Y-m-d') == '2014-01-02', 'Date value was changed and set by date string.');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue(NULL);
$this->assertNull($typed_data->getDateTime(), 'Date wrapper is null-able.');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue('invalid');
$this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
// Check implementation of DateTimeInterface.
$typed_data = $this->createTypedData(array('type' => 'datetime_iso8601'), '2014-01-01T20:00:00+00:00');
$this->assertTrue($typed_data->getDateTime() instanceof DrupalDateTime);
$typed_data->setDateTime(new DrupalDateTime('2014-01-02T20:00:00+00:00'));
$this->assertEqual($typed_data->getValue(), '2014-01-02T20:00:00+00:00');
$typed_data->setValue(NULL);
$this->assertNull($typed_data->getDateTime());
// Timestamp type.
$value = REQUEST_TIME;
$typed_data = $this->createTypedData(array('type' => 'timestamp'), $value);
$this->assertTrue($typed_data->getValue() == $value, 'Timestamp value was fetched.');
$this->assertEqual($typed_data->validate()->count(), 0);
$new_value = REQUEST_TIME + 1;
$typed_data->setValue($new_value);
$this->assertTrue($typed_data->getValue()->getTimestamp() === $new_value, 'Date value was changed and set by timestamp.');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue('2000-01-01');
$this->assertTrue($typed_data->getValue()->format('Y-m-d') == '2000-01-01', 'Date value was changed and set by date string.');
$this->assertTrue(is_string($typed_data->getString()), 'Date value was converted to string');
$this->assertTrue($typed_data->getValue() === $new_value, 'Timestamp value was changed and set.');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue(NULL);
$this->assertNull($typed_data->getValue(), 'Date wrapper is null-able.');
$this->assertNull($typed_data->getDateTime(), 'Timestamp wrapper is null-able.');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue('invalid');
$this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
// Check implementation of DateTimeInterface.
$typed_data = $this->createTypedData(array('type' => 'timestamp'), REQUEST_TIME);
$this->assertTrue($typed_data->getDateTime() instanceof DrupalDateTime);
$typed_data->setDateTime(new DrupalDateTime(REQUEST_TIME + 1));
$this->assertEqual($typed_data->getValue(), REQUEST_TIME + 1);
$typed_data->setValue(NULL);
$this->assertNull($typed_data->getDateTime());
// Duration type.
$value = new DateInterval('PT20S');
$typed_data = $this->createTypedData(array('type' => 'duration'), $value);
$this->assertTrue($typed_data->getValue() === $value, 'Duration value was fetched.');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue(10);
$this->assertTrue($typed_data->getValue()->s == 10, 'Duration value was changed and set by time span in seconds.');
// DurationIso8601 type.
$value = 'PT20S';
$typed_data = $this->createTypedData(array('type' => 'duration_iso8601'), $value);
$this->assertIdentical($typed_data->getValue(), $value, 'DurationIso8601 value was fetched.');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue('P40D');
$this->assertTrue($typed_data->getValue()->d == 40, 'Duration value was changed and set by duration string.');
$this->assertTrue(is_string($typed_data->getString()), 'Duration value was converted to string');
$this->assertEqual($typed_data->validate()->count(), 0);
// Test getting the string and passing it back as value.
$duration = $typed_data->getString();
$typed_data->setValue($duration);
$this->assertEqual($typed_data->getString(), $duration, 'Duration formatted as string can be used to set the duration value.');
$this->assertEqual($typed_data->getDuration()->d, 40, 'DurationIso8601 value was changed and set by duration string.');
$this->assertTrue(is_string($typed_data->getString()), 'DurationIso8601 value was converted to string');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue(NULL);
$this->assertNull($typed_data->getValue(), 'Duration wrapper is null-able.');
$this->assertNull($typed_data->getValue(), 'DurationIso8601 wrapper is null-able.');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue('invalid');
$this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
// Check implementation of DurationInterface.
$typed_data = $this->createTypedData(array('type' => 'duration_iso8601'), 'PT20S');
$this->assertTrue($typed_data->getDuration() instanceof DateInterval);
$typed_data->setDuration(new DateInterval('P40D'));
// @todo: Should we make this "nicer"?
$this->assertEqual($typed_data->getValue(), 'P0Y0M40DT0H0M0S');
$typed_data->setValue(NULL);
$this->assertNull($typed_data->getDuration());
// Time span type.
$value = 20;
$typed_data = $this->createTypedData(array('type' => 'timespan'), $value);
$this->assertIdentical($typed_data->getValue(), $value, 'Time span value was fetched.');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue(60 * 60 * 4);
$this->assertEqual($typed_data->getDuration()->s, 14400, 'Time span was changed');
$this->assertTrue(is_string($typed_data->getString()), 'Time span value was converted to string');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue(NULL);
$this->assertNull($typed_data->getValue(), 'Time span wrapper is null-able.');
$this->assertEqual($typed_data->validate()->count(), 0);
$typed_data->setValue('invalid');
$this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
// Check implementation of DurationInterface.
$typed_data = $this->createTypedData(array('type' => 'timespan'), 20);
$this->assertTrue($typed_data->getDuration() instanceof DateInterval);
$typed_data->setDuration(new DateInterval('PT4H'));
$this->assertEqual($typed_data->getValue(), 60 * 60 * 4);
$typed_data->setValue(NULL);
$this->assertNull($typed_data->getDuration());
// URI type.
$uri = 'http://example.com/foo/';

View File

@ -9,7 +9,6 @@ use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Language\Language;
use Drupal\Core\Utility\ModuleInfo;
use Drupal\Core\TypedData\Primitive;
use Drupal\system\Plugin\Block\SystemMenuBlock;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;