diff --git a/core/includes/config.inc b/core/includes/config.inc index b42bd39..ac24e36 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -181,7 +181,8 @@ function config_sync_get_changes(StorageInterface $source_storage, StorageInterf function config_sync_changes(array $config_changes, StorageInterface $source_storage, StorageInterface $target_storage) { foreach (array('delete', 'create', 'change') as $op) { foreach ($config_changes[$op] as $name) { - Config::validateNamespace($name); + // Validate the configuration object name before importing it. + Config::validateName($name); if ($op == 'delete') { $target_storage->delete($name); } @@ -252,7 +253,8 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou // handle dependencies correctly. foreach (array('delete', 'create', 'change') as $op) { foreach ($config_changes[$op] as $key => $name) { - Config::validateNamespace($name); + // Validate the configuration object name before importing it. + Config::validateName($name); // Extract owner from configuration object name. $module = strtok($name, '.'); // Check whether the module implements hook_config_import() and ask it to diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index 8a12fce..b345da6 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Config; use Drupal\Component\Utility\NestedArray; -use Drupal\Core\Config\ConfigException; +use Drupal\Core\Config\ConfigNameException; use Symfony\Component\EventDispatcher\EventDispatcher; /** @@ -66,6 +66,18 @@ class Config { protected $eventDispatcher; /** + * The maximum length of a configuration object name. + * + * Many filesystems (including HFS, NTFS, and ext4) have a maximum file name + * length of 255 characters. To ensure that no configuration objects + * incompatible with this limitation are created, we enforce a maximum name + * length of 250 characters (leaving 5 characters for the file extension). + * + * @see http://en.wikipedia.org/wiki/Comparison_of_file_systems + */ + const MAX_NAME_LENGTH = 250; + + /** * Constructs a configuration object. * * @param string $name @@ -348,9 +360,8 @@ public function load() { * The configuration object. */ public function save() { - // All configuration objects need to be namespaced by extension, as it would - // be impossible to maintain them otherwise. - $this->validateNamespace($this->name); + // Validate the configuration object name before saving. + $this->validateName($this->name); $this->storage->write($this->name, $this->data); $this->isNew = FALSE; $this->notify('save'); @@ -422,13 +433,31 @@ public function merge(array $data_to_merge) { } /** - * Validates that a configuration object is namespaced by extension. + * Validates the configuration object name. + * + * @throws Drupal\Core\Config\ConfigNameException * - * @throws Drupal\Core\Config\ConfigException + * @see \Drupal\Core\Config\Config::MAX_NAME_LENGTH */ - public static function validateNamespace($name) { + public static function validateName($name) { + // The configuration object name must include the owner namespace. if (strpos($name, '.') === FALSE) { - throw new ConfigException(format_string('Missing namespace in Config name @name', array('@name' => $name))); + throw new ConfigNameException(format_string( + 'Missing namespace in Config object name %name', + array( + '%name' => $name, + ) + )); + } + // The object name must be fewer than Config::MAX_NAME_LENGTH characters. + if (strlen($this->name) > self::MAX_NAME_LENGTH) { + throw new ConfigNameException(format_string( + 'Config object name %name is longer than @max allowed characters', + array( + '%name' => $name, + '@max' => self::MAX_NAME_LENGTH, + ) + )); } } diff --git a/core/lib/Drupal/Core/Config/ConfigNameException.php b/core/lib/Drupal/Core/Config/ConfigNameException.php new file mode 100644 index 0000000..f02f227 --- /dev/null +++ b/core/lib/Drupal/Core/Config/ConfigNameException.php @@ -0,0 +1,12 @@ +save(); - $this->fail('Expected ConfigException was not thrown.'); + $this->fail($message); } catch (ConfigException $e) { - $this->pass('Expected ConfigException was thrown.'); + $this->pass($message); } + // Try to save an object with a name longer than 250 characters. + $name = 'config_test.herman_melville.moby_dick_or_the_whale.harper_1851.now_small_fowls_flew_screaming_over_the_yet_yawning_gulf_a_sullen_white_surf_beat_against_its_steep_sides_then_all_collapsed_and_the_great_shroud_of_the_sea_rolled_on_as_it_rolled_five_thousand_years_ago'; + $message = 'Expected ConfigNameException was thrown for a name longer than Config::MAX_NAME_LENGTH.' + try { + $config = config($name); + $config->save(); + $this->fail($message); + } + catch (ConfigNameException $e) { + $this->pass($message); + } + + // Try to save a valid object name. $name = 'config.namespace'; + $message = 'ConfigNameException was not thrown for a valid object name.'; try { $config = config($name); $config->save(); - $this->pass('ConfigException was not thrown.'); + $this->pass($message); } catch (ConfigException $e) { - $this->fail('ConfigException was thrown.'); + $this->fail($message); } }