123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- <?php
- namespace Drupal\Core\TypedData;
- use Drupal\Component\Plugin\Exception\PluginException;
- use Drupal\Core\Cache\CacheBackendInterface;
- use Drupal\Core\DependencyInjection\ClassResolverInterface;
- use Drupal\Core\DependencyInjection\DependencySerializationTrait;
- use Drupal\Core\Extension\ModuleHandlerInterface;
- use Drupal\Core\Plugin\DefaultPluginManager;
- use Drupal\Core\TypedData\Validation\ExecutionContextFactory;
- use Drupal\Core\TypedData\Validation\RecursiveValidator;
- use Drupal\Core\Validation\ConstraintManager;
- use Drupal\Core\Validation\ConstraintValidatorFactory;
- use Drupal\Core\Validation\DrupalTranslator;
- use Symfony\Component\Validator\Validator\ValidatorInterface;
- /**
- * Manages data type plugins.
- */
- class TypedDataManager extends DefaultPluginManager implements TypedDataManagerInterface {
- use DependencySerializationTrait;
- /**
- * The validator used for validating typed data.
- *
- * @var \Symfony\Component\Validator\Validator\ValidatorInterface
- */
- protected $validator;
- /**
- * The validation constraint manager to use for instantiating constraints.
- *
- * @var \Drupal\Core\Validation\ConstraintManager
- */
- protected $constraintManager;
- /**
- * An array of typed data property prototypes.
- *
- * @var array
- */
- protected $prototypes = [];
- /**
- * The class resolver.
- *
- * @var \Drupal\Core\DependencyInjection\ClassResolverInterface
- */
- protected $classResolver;
- /**
- * Constructs a new TypedDataManager.
- *
- * @param \Traversable $namespaces
- * An object that implements \Traversable which contains the root paths
- * keyed by the corresponding namespace to look for plugin implementations.
- * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
- * Cache backend instance to use.
- * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
- * The module handler.
- * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
- * The class resolver.
- */
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, ClassResolverInterface $class_resolver) {
- $this->alterInfo('data_type_info');
- $this->setCacheBackend($cache_backend, 'typed_data_types_plugins');
- $this->classResolver = $class_resolver;
- parent::__construct('Plugin/DataType', $namespaces, $module_handler, NULL, 'Drupal\Core\TypedData\Annotation\DataType');
- }
- /**
- * {@inheritdoc}
- */
- public function createInstance($data_type, array $configuration = []) {
- $data_definition = $configuration['data_definition'];
- $type_definition = $this->getDefinition($data_type);
- if (!isset($type_definition)) {
- throw new \InvalidArgumentException("Invalid data type '$data_type' has been given");
- }
- // Allow per-data definition overrides of the used classes, i.e. take over
- // classes specified in the type definition.
- $class = $data_definition->getClass();
- if (!isset($class)) {
- throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $data_type));
- }
- $typed_data = $class::createInstance($data_definition, $configuration['name'], $configuration['parent']);
- $typed_data->setTypedDataManager($this);
- return $typed_data;
- }
- /**
- * {@inheritdoc}
- */
- public function create(DataDefinitionInterface $definition, $value = NULL, $name = NULL, $parent = NULL) {
- $typed_data = $this->createInstance($definition->getDataType(), [
- 'data_definition' => $definition,
- 'name' => $name,
- 'parent' => $parent,
- ]);
- if (isset($value)) {
- $typed_data->setValue($value, FALSE);
- }
- return $typed_data;
- }
- /**
- * {@inheritdoc}
- */
- public function createDataDefinition($data_type) {
- $type_definition = $this->getDefinition($data_type);
- if (!isset($type_definition)) {
- throw new \InvalidArgumentException("Invalid data type '$data_type' has been given");
- }
- $class = $type_definition['definition_class'];
- $data_definition = $class::createFromDataType($data_type);
- if (method_exists($data_definition, 'setTypedDataManager')) {
- $data_definition->setTypedDataManager($this);
- }
- return $data_definition;
- }
- /**
- * {@inheritdoc}
- */
- public function createListDataDefinition($item_type) {
- $type_definition = $this->getDefinition($item_type);
- if (!isset($type_definition)) {
- throw new \InvalidArgumentException("Invalid data type '$item_type' has been given");
- }
- $class = $type_definition['list_definition_class'];
- return $class::createFromItemType($item_type);
- }
- /**
- * {@inheritdoc}
- */
- public function getInstance(array $options) {
- return $this->getPropertyInstance($options['object'], $options['property'], $options['value']);
- }
- /**
- * {@inheritdoc}
- */
- public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) {
- // For performance, try to reuse existing prototypes instead of
- // constructing new objects when possible. A prototype is reused when
- // creating a data object:
- // - for a similar root object (same data type and settings),
- // - at the same property path under that root object.
- $root_definition = $object->getRoot()->getDataDefinition();
- // If the root object is a list, we want to look at the data type and the
- // settings of its item definition.
- if ($root_definition instanceof ListDataDefinition) {
- $root_definition = $root_definition->getItemDefinition();
- }
- // Root data type and settings.
- $parts[] = $root_definition->getDataType();
- if ($settings = $root_definition->getSettings()) {
- // Include the settings serialized as JSON as part of the key. The JSON is
- // a shorter string than the serialized form, so array access is faster.
- $parts[] = json_encode($settings);
- }
- // Property path for the requested data object.
- $parts[] = $object->getPropertyPath();
- // Only property instances of complex data types should be cached by the
- // property name, as they represent different properties. Properties of list
- // data types are the items of the list and the property name represents
- // only the delta in that list and not an unique property, which is why all
- // items should use the same prototype.
- if ($object instanceof ComplexDataInterface) {
- $parts[] = $property_name;
- }
- $key = implode(':', $parts);
- // Create the prototype if needed.
- if (!isset($this->prototypes[$key])) {
- // Fetch the data definition for the child object from the parent.
- if ($object instanceof ComplexDataInterface) {
- $definition = $object->getDataDefinition()->getPropertyDefinition($property_name);
- }
- elseif ($object instanceof ListInterface) {
- $definition = $object->getItemDefinition();
- }
- else {
- throw new \InvalidArgumentException("The passed object has to either implement the ComplexDataInterface or the ListInterface.");
- }
- if (!$definition) {
- throw new \InvalidArgumentException("Property $property_name is unknown.");
- }
- // Create the prototype without any value, but with initial parenting
- // so that constructors can set up the objects correctly.
- $this->prototypes[$key] = $this->create($definition, NULL, $property_name, $object);
- }
- // Clone the prototype, update its parenting information, and assign the
- // value.
- $property = clone $this->prototypes[$key];
- $property->setContext($property_name, $object);
- if (isset($value)) {
- $property->setValue($value, FALSE);
- }
- return $property;
- }
- /**
- * Sets the validator for validating typed data.
- *
- * @param \Symfony\Component\Validator\Validator\ValidatorInterface $validator
- * The validator object to set.
- */
- public function setValidator(ValidatorInterface $validator) {
- $this->validator = $validator;
- }
- /**
- * {@inheritdoc}
- */
- public function getValidator() {
- if (!isset($this->validator)) {
- $this->validator = new RecursiveValidator(
- new ExecutionContextFactory(new DrupalTranslator()),
- new ConstraintValidatorFactory($this->classResolver),
- $this
- );
- }
- return $this->validator;
- }
- /**
- * {@inheritdoc}
- */
- public function setValidationConstraintManager(ConstraintManager $constraintManager) {
- $this->constraintManager = $constraintManager;
- }
- /**
- * {@inheritdoc}
- */
- public function getValidationConstraintManager() {
- return $this->constraintManager;
- }
- /**
- * {@inheritdoc}
- */
- public function getDefaultConstraints(DataDefinitionInterface $definition) {
- $constraints = [];
- $type_definition = $this->getDefinition($definition->getDataType());
- // Auto-generate a constraint for data types implementing a primitive
- // interface.
- if (is_subclass_of($type_definition['class'], '\Drupal\Core\TypedData\PrimitiveInterface')) {
- $constraints['PrimitiveType'] = [];
- }
- // Add in constraints specified by the data type.
- if (isset($type_definition['constraints'])) {
- $constraints += $type_definition['constraints'];
- }
- // Add the NotNull constraint for required data.
- if ($definition->isRequired()) {
- $constraints['NotNull'] = [];
- }
- // Check if the class provides allowed values.
- if (is_subclass_of($definition->getClass(), 'Drupal\Core\TypedData\OptionsProviderInterface')) {
- $constraints['AllowedValues'] = [];
- }
- return $constraints;
- }
- /**
- * {@inheritdoc}
- */
- public function clearCachedDefinitions() {
- parent::clearCachedDefinitions();
- $this->prototypes = [];
- }
- /**
- * {@inheritdoc}
- */
- public function getCanonicalRepresentation(TypedDataInterface $data) {
- $data_definition = $data->getDataDefinition();
- // In case a list is passed, respect the 'wrapped' key of its data type.
- if ($data_definition instanceof ListDataDefinitionInterface) {
- $data_definition = $data_definition->getItemDefinition();
- }
- // Get the plugin definition of the used data type.
- $type_definition = $this->getDefinition($data_definition->getDataType());
- if (!empty($type_definition['unwrap_for_canonical_representation'])) {
- return $data->getValue();
- }
- return $data;
- }
- }
|