Pull merge.
[yaffs-website] / web / core / lib / Drupal / Core / Plugin / Context / ContextDefinition.php
1 <?php
2
3 namespace Drupal\Core\Plugin\Context;
4
5 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
6 use Drupal\Core\TypedData\TypedDataTrait;
7
8 /**
9  * Defines a class for context definitions.
10  */
11 class ContextDefinition implements ContextDefinitionInterface {
12
13   use DependencySerializationTrait {
14     __sleep as traitSleep;
15     __wakeup as traitWakeup;
16   }
17
18   use TypedDataTrait;
19
20   /**
21    * The data type of the data.
22    *
23    * @var string
24    *   The data type.
25    */
26   protected $dataType;
27
28   /**
29    * The human-readable label.
30    *
31    * @var string
32    *   The label.
33    */
34   protected $label;
35
36   /**
37    * The human-readable description.
38    *
39    * @var string|null
40    *   The description, or NULL if no description is available.
41    */
42   protected $description;
43
44   /**
45    * Whether the data is multi-valued, i.e. a list of data items.
46    *
47    * @var bool
48    */
49   protected $isMultiple = FALSE;
50
51   /**
52    * Determines whether a data value is required.
53    *
54    * @var bool
55    *   Whether a data value is required.
56    */
57   protected $isRequired = TRUE;
58
59   /**
60    * The default value.
61    *
62    * @var mixed
63    */
64   protected $defaultValue;
65
66   /**
67    * An array of constraints.
68    *
69    * @var array[]
70    */
71   protected $constraints = [];
72
73   /**
74    * An EntityContextDefinition instance, for backwards compatibility.
75    *
76    * If this context is created with a data type that starts with 'entity:',
77    * this property will be an instance of EntityContextDefinition, and certain
78    * methods of this object will delegate to their overridden counterparts in
79    * $this->entityContextDefinition.
80    *
81    * This property should be kept private so that it is only accessible to this
82    * class for backwards compatibility reasons. It will be removed in Drupal 9.
83    *
84    * @deprecated
85    *   Constructing a context definition for an entity type (i.e., the data type
86    *   begins with 'entity:') is deprecated in Drupal 8.6.0. Instead, use
87    *   the static factory methods of EntityContextDefinition to create context
88    *   definitions for entity types, or the static ::create() method of this
89    *   class for any other data type. See https://www.drupal.org/node/2976400
90    *   for more information.
91    *
92    * @see ::__construct()
93    * @see ::__sleep()
94    * @see ::__wakeup()
95    * @see ::getConstraintObjects()
96    * @see ::getSampleValues()
97    * @see ::initializeEntityContextDefinition()
98    * @see https://www.drupal.org/node/2932462
99    * @see https://www.drupal.org/node/2976400
100    *
101    * @var \Drupal\Core\Plugin\Context\EntityContextDefinition
102    */
103   private $entityContextDefinition;
104
105   /**
106    * Creates a new context definition.
107    *
108    * @param string $data_type
109    *   The data type for which to create the context definition. Defaults to
110    *   'any'.
111    *
112    * @return static
113    *   The created context definition object.
114    */
115   public static function create($data_type = 'any') {
116     return new static(
117       $data_type
118     );
119   }
120
121   /**
122    * Constructs a new context definition object.
123    *
124    * @param string $data_type
125    *   The required data type.
126    * @param string|null $label
127    *   The label of this context definition for the UI.
128    * @param bool $required
129    *   Whether the context definition is required.
130    * @param bool $multiple
131    *   Whether the context definition is multivalue.
132    * @param string|null $description
133    *   The description of this context definition for the UI.
134    * @param mixed $default_value
135    *   The default value of this definition.
136    */
137   public function __construct($data_type = 'any', $label = NULL, $required = TRUE, $multiple = FALSE, $description = NULL, $default_value = NULL) {
138     $this->dataType = $data_type;
139     $this->label = $label;
140     $this->isRequired = $required;
141     $this->isMultiple = $multiple;
142     $this->description = $description;
143     $this->defaultValue = $default_value;
144
145     if (strpos($data_type, 'entity:') === 0 && !($this instanceof EntityContextDefinition)) {
146       @trigger_error('Constructing a ContextDefinition object for an entity type is deprecated in Drupal 8.6.0. Use ' . __NAMESPACE__ . '\EntityContextDefinition instead. See https://www.drupal.org/node/2976400 for more information.', E_USER_DEPRECATED);
147       $this->initializeEntityContextDefinition();
148     }
149   }
150
151   /**
152    * {@inheritdoc}
153    */
154   public function getDataType() {
155     return $this->dataType;
156   }
157
158   /**
159    * {@inheritdoc}
160    */
161   public function setDataType($data_type) {
162     $this->dataType = $data_type;
163     return $this;
164   }
165
166   /**
167    * {@inheritdoc}
168    */
169   public function getLabel() {
170     return $this->label;
171   }
172
173   /**
174    * {@inheritdoc}
175    */
176   public function setLabel($label) {
177     $this->label = $label;
178     return $this;
179   }
180
181   /**
182    * {@inheritdoc}
183    */
184   public function getDescription() {
185     return $this->description;
186   }
187
188   /**
189    * {@inheritdoc}
190    */
191   public function setDescription($description) {
192     $this->description = $description;
193     return $this;
194   }
195
196   /**
197    * {@inheritdoc}
198    */
199   public function isMultiple() {
200     return $this->isMultiple;
201   }
202
203   /**
204    * {@inheritdoc}
205    */
206   public function setMultiple($multiple = TRUE) {
207     $this->isMultiple = $multiple;
208     return $this;
209   }
210
211   /**
212    * {@inheritdoc}
213    */
214   public function isRequired() {
215     return $this->isRequired;
216   }
217
218   /**
219    * {@inheritdoc}
220    */
221   public function setRequired($required = TRUE) {
222     $this->isRequired = $required;
223     return $this;
224   }
225
226   /**
227    * {@inheritdoc}
228    */
229   public function getDefaultValue() {
230     return $this->defaultValue;
231   }
232
233   /**
234    * {@inheritdoc}
235    */
236   public function setDefaultValue($default_value) {
237     $this->defaultValue = $default_value;
238     return $this;
239   }
240
241   /**
242    * {@inheritdoc}
243    */
244   public function getConstraints() {
245     // If the backwards compatibility layer is present, delegate to that.
246     if ($this->entityContextDefinition) {
247       return $this->entityContextDefinition->getConstraints();
248     }
249
250     // @todo Apply defaults.
251     return $this->constraints;
252   }
253
254   /**
255    * {@inheritdoc}
256    */
257   public function getConstraint($constraint_name) {
258     // If the backwards compatibility layer is present, delegate to that.
259     if ($this->entityContextDefinition) {
260       return $this->entityContextDefinition->getConstraint($constraint_name);
261     }
262
263     $constraints = $this->getConstraints();
264     return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL;
265   }
266
267   /**
268    * {@inheritdoc}
269    */
270   public function setConstraints(array $constraints) {
271     // If the backwards compatibility layer is present, delegate to that.
272     if ($this->entityContextDefinition) {
273       $this->entityContextDefinition->setConstraint();
274     }
275
276     $this->constraints = $constraints;
277     return $this;
278   }
279
280   /**
281    * {@inheritdoc}
282    */
283   public function addConstraint($constraint_name, $options = NULL) {
284     // If the backwards compatibility layer is present, delegate to that.
285     if ($this->entityContextDefinition) {
286       $this->entityContextDefinition->addConstraint($constraint_name, $options);
287     }
288
289     $this->constraints[$constraint_name] = $options;
290     return $this;
291   }
292
293   /**
294    * {@inheritdoc}
295    */
296   public function getDataDefinition() {
297     if ($this->isMultiple()) {
298       $definition = $this->getTypedDataManager()->createListDataDefinition($this->getDataType());
299     }
300     else {
301       $definition = $this->getTypedDataManager()->createDataDefinition($this->getDataType());
302     }
303
304     if (!$definition) {
305       throw new \Exception("The data type '{$this->getDataType()}' is invalid");
306     }
307     $definition->setLabel($this->getLabel())
308       ->setDescription($this->getDescription())
309       ->setRequired($this->isRequired());
310     $constraints = $definition->getConstraints() + $this->getConstraints();
311     $definition->setConstraints($constraints);
312     return $definition;
313   }
314
315   /**
316    * {@inheritdoc}
317    */
318   public function isSatisfiedBy(ContextInterface $context) {
319     $definition = $context->getContextDefinition();
320     // If the data types do not match, this context is invalid unless the
321     // expected data type is any, which means all data types are supported.
322     if ($this->getDataType() != 'any' && $definition->getDataType() != $this->getDataType()) {
323       return FALSE;
324     }
325
326     // Get the value for this context, either directly if possible or by
327     // introspecting the definition.
328     if ($context->hasContextValue()) {
329       $values = [$context->getContextData()];
330     }
331     elseif ($definition instanceof self) {
332       if ($this->entityContextDefinition) {
333         $values = $this->entityContextDefinition->getSampleValues();
334       }
335       else {
336         $values = $definition->getSampleValues();
337       }
338     }
339     else {
340       $values = [];
341     }
342
343     $validator = $this->getTypedDataManager()->getValidator();
344     foreach ($values as $value) {
345       $constraints = array_values($this->getConstraintObjects());
346       $violations = $validator->validate($value, $constraints);
347       foreach ($violations as $delta => $violation) {
348         // Remove any violation that does not correspond to the constraints.
349         if (!in_array($violation->getConstraint(), $constraints)) {
350           $violations->remove($delta);
351         }
352       }
353       // If a value has no violations then the requirement is satisfied.
354       if (!$violations->count()) {
355         return TRUE;
356       }
357     }
358
359     return FALSE;
360   }
361
362   /**
363    * Returns typed data objects representing this context definition.
364    *
365    * This should return as many objects as needed to reflect the variations of
366    * the constraints it supports.
367    *
368    * @yield \Drupal\Core\TypedData\TypedDataInterface
369    *   The set of typed data object.
370    */
371   protected function getSampleValues() {
372     yield $this->getTypedDataManager()->create($this->getDataDefinition());
373   }
374
375   /**
376    * Extracts an array of constraints for a context definition object.
377    *
378    * @return \Symfony\Component\Validator\Constraint[]
379    *   A list of applied constraints for the context definition.
380    */
381   protected function getConstraintObjects() {
382     // If the backwards compatibility layer is present, delegate to that.
383     if ($this->entityContextDefinition) {
384       return $this->entityContextDefinition->getConstraintObjects();
385     }
386
387     $constraint_definitions = $this->getConstraints();
388
389     $validation_constraint_manager = $this->getTypedDataManager()->getValidationConstraintManager();
390     $constraints = [];
391     foreach ($constraint_definitions as $constraint_name => $constraint_definition) {
392       $constraints[$constraint_name] = $validation_constraint_manager->create($constraint_name, $constraint_definition);
393     }
394
395     return $constraints;
396   }
397
398   /**
399    * Implements magic __sleep() method.
400    */
401   public function __sleep() {
402     return array_diff($this->traitSleep(), ['entityContextDefinition']);
403   }
404
405   /**
406    * Implements magic __wakeup() method.
407    */
408   public function __wakeup() {
409     $this->traitWakeup();
410
411     if (strpos($this->getDataType(), 'entity:') === 0) {
412       $this->initializeEntityContextDefinition();
413     }
414   }
415
416   /**
417    * Initializes $this->entityContextDefinition for backwards compatibility.
418    *
419    * This method should be kept private so that it is only accessible to this
420    * class for backwards compatibility reasons. It will be removed in Drupal 9.
421    *
422    * @deprecated
423    */
424   private function initializeEntityContextDefinition() {
425     $this->entityContextDefinition = EntityContextDefinition::create()
426       ->setDataType($this->getDataType())
427       ->setLabel($this->getLabel())
428       ->setRequired($this->isRequired())
429       ->setMultiple($this->isMultiple())
430       ->setDescription($this->getDescription())
431       ->setConstraints($this->getConstraints())
432       ->setDefaultValue($this->getDefaultValue());
433   }
434
435 }