Created
June 26, 2013 15:38
-
-
Save anonymous/5868483 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @file | |
* Contains \Drupal\Core\Config\TypedConfigManager. | |
*/ | |
namespace Drupal\Core\Config; | |
use Drupal\Component\Plugin\PluginManagerBase; | |
use Drupal\Component\Utility\NestedArray; | |
/** | |
* Manages config type plugins. | |
*/ | |
class TypedConfigManager extends PluginManagerBase { | |
/** | |
* A storage controller instance for reading configuration data. | |
* | |
* @var \Drupal\Core\Config\StorageInterface | |
*/ | |
protected $configStorage; | |
/** | |
* A storage controller instance for reading configuration schema data. | |
* | |
* @var \Drupal\Core\Config\StorageInterface | |
*/ | |
protected $schemaStorage; | |
/** | |
* The array of plugin definitions, keyed by plugin id. | |
* | |
* @var array | |
*/ | |
protected $definitions = array(); | |
/** | |
* Creates a new typed configuration manager. | |
* | |
* @param \Drupal\Core\Config\StorageInterface $configStorage | |
* The storage controller object to use for reading schema data | |
* @param \Drupal\Core\Config\StorageInterface $schemaStorage | |
* The storage controller object to use for reading schema data | |
*/ | |
public function __construct(StorageInterface $configStorage, StorageInterface $schemaStorage) { | |
$this->configStorage = $configStorage; | |
$this->schemaStorage = $schemaStorage; | |
$this->loadAllSchema(); | |
} | |
/** | |
* Gets typed configuration data. | |
* | |
* @param string $name | |
* Configuration object name. | |
* | |
* @return \Drupal\Core\Config\Schema\Element | |
* Typed configuration element. | |
*/ | |
public function get($name) { | |
$data = $this->configStorage->read($name); | |
$definition = $this->getDefinition($name); | |
return $this->create($definition, $data); | |
} | |
/** | |
* Overrides \Drupal\Core\TypedData\TypedDataManager::create() | |
* | |
* Fills in default type and does variable replacement. | |
*/ | |
public function create(array $definition, $value = NULL, $name = NULL, $parent = NULL) { | |
if (!isset($definition['type'])) { | |
// Set default type 'string' if possible. If not it will be 'undefined'. | |
if (is_string($value)) { | |
$definition['type'] = 'string'; | |
} | |
else { | |
$definition['type'] = 'undefined'; | |
} | |
} | |
elseif (strpos($definition['type'], ']')) { | |
// Replace variable names in definition. | |
$replace = is_array($value) ? $value : array(); | |
if (isset($parent)) { | |
$replace['%parent'] = $parent->getValue(); | |
} | |
if (isset($name)) { | |
$replace['%key'] = $name; | |
} | |
$definition['type'] = $this->replaceName($definition['type'], $replace); | |
} | |
// Create typed config object. | |
$wrapper = $this->createInstance($definition['type'], $definition, $name, $parent); | |
if (isset($value)) { | |
$wrapper->setValue($value, FALSE); | |
} | |
return $wrapper; | |
} | |
/** | |
* Overrides Drupal\Core\TypedData\TypedDataFactory::createInstance(). | |
*/ | |
public function createInstance($plugin_id, array $configuration, $name = NULL, $parent = NULL) { | |
$type_definition = $this->getDefinition($plugin_id); | |
$configuration += $type_definition; | |
if (!isset($type_definition)) { | |
throw new InvalidArgumentException(format_string('Invalid data type %plugin_id has been given.', array('%plugin_id' => $plugin_id))); | |
} | |
// Allow per-data definition overrides of the used classes, i.e. take over | |
// classes specified in the data definition. | |
$key = empty($configuration['list']) ? 'class' : 'list class'; | |
if (isset($configuration[$key])) { | |
$class = $configuration[$key]; | |
} | |
elseif (isset($type_definition[$key])) { | |
$class = $type_definition[$key]; | |
} | |
if (!isset($class)) { | |
throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $plugin_id)); | |
} | |
return new $class($configuration, $name, $parent); | |
} | |
/** | |
* Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition(). | |
*/ | |
public function getDefinition($base_plugin_id) { | |
if (isset($this->definitions[$base_plugin_id])) { | |
$type = $base_plugin_id; | |
} | |
elseif (strpos($base_plugin_id, '.') && $name = $this->getFallbackName($base_plugin_id)) { | |
// Found a generic name, replacing the last element by '*'. | |
$type = $name; | |
} | |
else { | |
// If we don't have definition, return the 'default' element. | |
// This should map to 'undefined' type by default, unless overridden. | |
$type = 'default'; | |
} | |
$definition = $this->definitions[$type]; | |
// Check whether this type is an extension of another one and compile it. | |
if (isset($definition['type'])) { | |
$merge = $this->getDefinition($definition['type']); | |
$definition = NestedArray::mergeDeep($merge, $definition); | |
// Unset type so we try the merge only once per type. | |
unset($definition['type']); | |
$this->definitions[$type] = $definition; | |
} | |
return $definition; | |
} | |
/** | |
* Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions(). | |
*/ | |
public function getDefinitions() { | |
return $this->definitions; | |
} | |
/** | |
* Load schema for module / theme. | |
*/ | |
protected function loadAllSchema() { | |
foreach ($this->schemaStorage->listAll() as $name) { | |
if ($schema = $this->schemaStorage->read($name)) { | |
foreach ($schema as $type => $definition) { | |
$this->definitions[$type] = $definition; | |
} | |
} | |
} | |
} | |
/** | |
* Gets fallback metadata name. | |
* | |
* @param string $name | |
* Configuration name or key. | |
* | |
* @return null|string | |
* Same name with the last part(s) replaced by the filesystem marker. | |
* for example, breakpoint.breakpoint.module.toolbar.narrow check for | |
* definition in below order: | |
* breakpoint.breakpoint.module.toolbar.* | |
* breakpoint.breakpoint.module.*.* | |
* breakpoint.breakpoint.*.*.* | |
* breakpoint.*.*.*.* | |
* Returns null, if no matching element. | |
*/ | |
protected function getFallbackName($name) { | |
// Check for definition of $name with filesystem marker. | |
$replaced = preg_replace('/(\.[^\.]+)([\.\*]*)$/', '.*\2', $name); | |
if ($replaced != $name ) { | |
if (isset($this->definitions[$replaced])) { | |
return $replaced; | |
} | |
else { | |
// No definition for this level(for example, breakpoint.breakpoint.*), | |
// check for next level (which is, breakpoint.*.*). | |
return self::getFallbackName($replaced); | |
} | |
} | |
} | |
/** | |
* Replaces variables in configuration name. | |
* | |
* The configuration name may contain one or more variables to be replaced, | |
* enclosed in square brackets like '[name]' and will follow the replacement | |
* rules defined by the replaceVariable() method. | |
* | |
* @param string $name | |
* Configuration name with variables in square brackets. | |
* @param mixed $data | |
* Configuration data for the element. | |
* @return string | |
* Configuration name with variables replaced. | |
*/ | |
protected static function replaceName($name, $data) { | |
if (preg_match_all("/\[(.*)\]/U", $name, $matches)) { | |
// Build our list of '[value]' => replacement. | |
foreach (array_combine($matches[0], $matches[1]) as $key => $value) { | |
$replace[$key] = self::replaceVariable($value, $data); | |
} | |
return strtr($name, $replace); | |
} | |
else { | |
return $name; | |
} | |
} | |
/** | |
* Replaces variable values in included names with configuration data. | |
* | |
* Variable values are nested configuration keys that will be replaced by | |
* their value or some of these special strings: | |
* - '%key', will be replaced by the element's key. | |
* - '%parent', to reference the parent element. | |
* | |
* There may be nested configuration keys separated by dots or more complex | |
* patterns like '%parent.name' which references the 'name' value of the | |
* parent element. | |
* | |
* Example patterns: | |
* - 'name.subkey', indicates a nested value of the current element. | |
* - '%parent.name', will be replaced by the 'name' value of the parent. | |
* - '%parent.%key', will be replaced by the parent element's key. | |
* | |
* @param string $value | |
* Variable value to be replaced. | |
* | |
* @return string | |
* The replaced value if a replacement found or the original value if not. | |
*/ | |
protected static function replaceVariable($value, $data) { | |
$parts = explode('.', $value); | |
// Process each value part, one at a time. | |
while ($name = array_shift($parts)) { | |
if (!is_array($data) || !isset($data[$name])) { | |
// Key not found, return original value | |
return $value; | |
} | |
elseif (!$parts) { | |
// If no more parts left, this is the final property. | |
return (string)$data[$name]; | |
} | |
else { | |
// Get nested value and continue processing. | |
$data = $data[$name]; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment