ConfigDependencies.php

Same filename and directory in other branches
  1. 9 core/modules/rest/src/Entity/ConfigDependencies.php
  2. 10 core/modules/rest/src/Entity/ConfigDependencies.php
  3. 11.x core/modules/rest/src/Entity/ConfigDependencies.php

Namespace

Drupal\rest\Entity

File

core/modules/rest/src/Entity/ConfigDependencies.php

View source
<?php

namespace Drupal\rest\Entity;

use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\rest\RestResourceConfigInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Calculates rest resource config dependencies.
 *
 * @internal
 */
class ConfigDependencies implements ContainerInjectionInterface {
    
    /**
     * The serialization format providers, keyed by format.
     *
     * @var string[]
     */
    protected $formatProviders;
    
    /**
     * The authentication providers, keyed by ID.
     *
     * @var string[]
     */
    protected $authProviders;
    
    /**
     * Creates a new ConfigDependencies instance.
     *
     * @param string[] $format_providers
     *   The serialization format providers, keyed by format.
     * @param string[] $auth_providers
     *   The authentication providers, keyed by ID.
     */
    public function __construct(array $format_providers, array $auth_providers) {
        $this->formatProviders = $format_providers;
        $this->authProviders = $auth_providers;
    }
    
    /**
     * {@inheritdoc}
     */
    public static function create(ContainerInterface $container) {
        return new static($container->getParameter('serializer.format_providers'), $container->getParameter('authentication_providers'));
    }
    
    /**
     * Calculates dependencies of a specific rest resource configuration.
     *
     * This function returns dependencies in a non-sorted, non-unique manner. It
     * is therefore the caller's responsibility to sort and remove duplicates
     * from the result prior to saving it with the configuration or otherwise
     * using it in a way that requires that. For example,
     * \Drupal\rest\Entity\RestResourceConfig::calculateDependencies() does this
     * via its \Drupal\Core\Entity\DependencyTrait::addDependency() method.
     *
     * @param \Drupal\rest\RestResourceConfigInterface $rest_config
     *   The rest configuration.
     *
     * @return string[][]
     *   Dependencies keyed by dependency type.
     *
     * @see \Drupal\rest\Entity\RestResourceConfig::calculateDependencies()
     */
    public function calculateDependencies(RestResourceConfigInterface $rest_config) {
        $granularity = $rest_config->get('granularity');
        // Dependency calculation is the same for either granularity, the most
        // notable difference is that for the 'resource' granularity, the same
        // authentication providers and formats are supported for every method.
        switch ($granularity) {
            case RestResourceConfigInterface::METHOD_GRANULARITY:
                $methods = $rest_config->getMethods();
                break;
            case RestResourceConfigInterface::RESOURCE_GRANULARITY:
                $methods = array_slice($rest_config->getMethods(), 0, 1);
                break;
            default:
                throw new \InvalidArgumentException('Invalid granularity specified.');
        }
        // The dependency lists for authentication providers and formats
        // generated on container build.
        $dependencies = [];
        foreach ($methods as $request_method) {
            // Add dependencies based on the supported authentication providers.
            foreach ($rest_config->getAuthenticationProviders($request_method) as $auth) {
                if (isset($this->authProviders[$auth])) {
                    $module_name = $this->authProviders[$auth];
                    $dependencies['module'][] = $module_name;
                }
            }
            // Add dependencies based on the supported authentication formats.
            foreach ($rest_config->getFormats($request_method) as $format) {
                if (isset($this->formatProviders[$format])) {
                    $module_name = $this->formatProviders[$format];
                    $dependencies['module'][] = $module_name;
                }
            }
        }
        return $dependencies;
    }
    
    /**
     * Informs the entity that entities it depends on will be deleted.
     *
     * @param \Drupal\rest\RestResourceConfigInterface $rest_config
     *   The rest configuration.
     * @param array $dependencies
     *   An array of dependencies that will be deleted keyed by dependency type.
     *   Dependency types are, for example, entity, module and theme.
     *
     * @return bool
     *   TRUE if the entity has been changed as a result, FALSE if not.
     *
     * @see \Drupal\Core\Config\Entity\ConfigEntityInterface::onDependencyRemoval()
     */
    public function onDependencyRemoval(RestResourceConfigInterface $rest_config, array $dependencies) {
        $granularity = $rest_config->get('granularity');
        switch ($granularity) {
            case RestResourceConfigInterface::METHOD_GRANULARITY:
                return $this->onDependencyRemovalForMethodGranularity($rest_config, $dependencies);
            case RestResourceConfigInterface::RESOURCE_GRANULARITY:
                return $this->onDependencyRemovalForResourceGranularity($rest_config, $dependencies);
            default:
                throw new \InvalidArgumentException('Invalid granularity specified.');
        }
    }
    
    /**
     * Informs the entity that entities it depends on will be deleted.
     *
     * @param \Drupal\rest\RestResourceConfigInterface $rest_config
     *   The rest configuration.
     * @param array $dependencies
     *   An array of dependencies that will be deleted keyed by dependency type.
     *   Dependency types are, for example, entity, module and theme.
     *
     * @return bool
     *   TRUE if the entity has been changed as a result, FALSE if not.
     */
    protected function onDependencyRemovalForMethodGranularity(RestResourceConfigInterface $rest_config, array $dependencies) {
        $changed = FALSE;
        // Only module-related dependencies can be fixed. All other types of
        // dependencies cannot, because they were not generated based on supported
        // authentication providers or formats.
        if (isset($dependencies['module'])) {
            // Try to fix dependencies.
            $removed_auth = array_keys(array_intersect($this->authProviders, $dependencies['module']));
            $removed_formats = array_keys(array_intersect($this->formatProviders, $dependencies['module']));
            $configuration_before = $configuration = $rest_config->get('configuration');
            if (!empty($removed_auth) || !empty($removed_formats)) {
                // Try to fix dependency problems by removing affected
                // authentication providers and formats.
                foreach (array_keys($rest_config->get('configuration')) as $request_method) {
                    foreach ($removed_formats as $format) {
                        if (in_array($format, $rest_config->getFormats($request_method), TRUE)) {
                            $configuration[$request_method]['supported_formats'] = array_diff($configuration[$request_method]['supported_formats'], $removed_formats);
                        }
                    }
                    foreach ($removed_auth as $auth) {
                        if (in_array($auth, $rest_config->getAuthenticationProviders($request_method), TRUE)) {
                            $configuration[$request_method]['supported_auth'] = array_diff($configuration[$request_method]['supported_auth'], $removed_auth);
                        }
                    }
                    if (empty($configuration[$request_method]['supported_auth'])) {
                        // Remove the key if there are no more authentication providers
                        // supported by this request method.
                        unset($configuration[$request_method]['supported_auth']);
                    }
                    if (empty($configuration[$request_method]['supported_formats'])) {
                        // Remove the key if there are no more formats supported by this
                        // request method.
                        unset($configuration[$request_method]['supported_formats']);
                    }
                    if (empty($configuration[$request_method])) {
                        // Remove the request method altogether if it no longer has any
                        // supported authentication providers or formats.
                        unset($configuration[$request_method]);
                    }
                }
            }
            if ($configuration_before != $configuration && !empty($configuration)) {
                $rest_config->set('configuration', $configuration);
                // Only mark the dependencies problems as fixed if there is any
                // configuration left.
                $changed = TRUE;
            }
        }
        // If the dependency problems are not marked as fixed at this point they
        // should be related to the resource plugin and the config entity should
        // be deleted.
        return $changed;
    }
    
    /**
     * Informs the entity that entities it depends on will be deleted.
     *
     * @param \Drupal\rest\RestResourceConfigInterface $rest_config
     *   The rest configuration.
     * @param array $dependencies
     *   An array of dependencies that will be deleted keyed by dependency type.
     *   Dependency types are, for example, entity, module and theme.
     *
     * @return bool
     *   TRUE if the entity has been changed as a result, FALSE if not.
     */
    protected function onDependencyRemovalForResourceGranularity(RestResourceConfigInterface $rest_config, array $dependencies) {
        $changed = FALSE;
        // Only module-related dependencies can be fixed. All other types of
        // dependencies cannot, because they were not generated based on supported
        // authentication providers or formats.
        if (isset($dependencies['module'])) {
            // Try to fix dependencies.
            $removed_auth = array_keys(array_intersect($this->authProviders, $dependencies['module']));
            $removed_formats = array_keys(array_intersect($this->formatProviders, $dependencies['module']));
            $configuration_before = $configuration = $rest_config->get('configuration');
            if (!empty($removed_auth) || !empty($removed_formats)) {
                // All methods support the same formats and authentication providers, so
                // get those for whichever the first listed method is.
                $first_method = $rest_config->getMethods()[0];
                // Try to fix dependency problems by removing affected
                // authentication providers and formats.
                foreach ($removed_formats as $format) {
                    if (in_array($format, $rest_config->getFormats($first_method), TRUE)) {
                        $configuration['formats'] = array_diff($configuration['formats'], $removed_formats);
                    }
                }
                foreach ($removed_auth as $auth) {
                    if (in_array($auth, $rest_config->getAuthenticationProviders($first_method), TRUE)) {
                        $configuration['authentication'] = array_diff($configuration['authentication'], $removed_auth);
                    }
                }
                if (empty($configuration['authentication'])) {
                    // Remove the key if there are no more authentication providers
                    // supported.
                    unset($configuration['authentication']);
                }
                if (empty($configuration['formats'])) {
                    // Remove the key if there are no more formats supported.
                    unset($configuration['formats']);
                }
                if (empty($configuration['authentication']) || empty($configuration['formats'])) {
                    // If there no longer are any supported authentication providers or
                    // formats, this REST resource can no longer function, and so we
                    // cannot fix this config entity to keep it working.
                    $configuration = [];
                }
            }
            if ($configuration_before != $configuration && !empty($configuration)) {
                $rest_config->set('configuration', $configuration);
                // Only mark the dependencies problems as fixed if there is any
                // configuration left.
                $changed = TRUE;
            }
        }
        // If the dependency problems are not marked as fixed at this point they
        // should be related to the resource plugin and the config entity should
        // be deleted.
        return $changed;
    }

}

Classes

Title Deprecated Summary
ConfigDependencies Calculates rest resource config dependencies.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.