diff --git a/composer.json b/composer.json
index ab7990283..e554b661b 100644
--- a/composer.json
+++ b/composer.json
@@ -25,8 +25,10 @@
"swiftmailer/swiftmailer": "~5.4",
"symfony/cache": "^3.3@dev",
"symfony/config": "^2.8",
+ "symfony/cache": "^3.1",
"symfony/translation": "^2.8",
- "vlucas/phpdotenv": "^2.4"
+ "vlucas/phpdotenv": "^2.4",
+ "silverstripe/config": "^1@dev"
},
"require-dev": {
"phpunit/PHPUnit": "~4.8",
diff --git a/docs/en/04_Changelogs/4.0.0.md b/docs/en/04_Changelogs/4.0.0.md
index be56bca02..7986a07c6 100644
--- a/docs/en/04_Changelogs/4.0.0.md
+++ b/docs/en/04_Changelogs/4.0.0.md
@@ -997,6 +997,11 @@ specific functions.
* Added a server requirement for the php-intl extension (shipped by default with most PHP distributions)
* Replaced Zend_Date and Zend_Locale with the php-intl extension.
* Consistently use CLDR date formats (rather than a mix of CLDR and date() formats)
+* Moved config into a new module: [silverstripe/config](https://github.com/silverstripe/silverstripe-config/).
+ See upgrading notes below.
+* Falsey config values (null, 0, false, etc) can now replace non-falsey values.
+* Introduced new ModuleLoader manifest, which allows modules to be found via composer name.
+ E.g. `$cms = ModuleLoader::instance()->getManifest()->getModule('silverstripe/cms')`
#### General and Core Removed API
@@ -1033,6 +1038,7 @@ specific functions.
* Removed TextParser and BBCodeParser. These are available in an archived module,
[silverstripe-archive/bbcodeparser](https://github.com/silverstripe-archive/silverstripe-bbcodeparser)
* Removed `ViewableData::ThemeDir`. Use `ThemeResourceLoader::findThemedResource` in conjunction with `SSViewer::get_themes` instead.
+* Removed `Config::FIRST_SET` and `Config::INHERITED`
#### General and Core Deprecated API
@@ -1040,6 +1046,8 @@ A very small number of methods were chosen for deprecation, and will be removed
* `ClassInfo::baseDataClass` - Use `DataObject::getSchema()->baseDataClass()` instead.
* `ClassInfo::table_for_object_field` - Use `DataObject::getSchema()->tableForField()` instead
+* `Config::inst()->update()` is deprecated. Use `Config::modify()->set()` or `Config::modify()->merge()`
+ instead.
### ORM API
@@ -1437,6 +1445,11 @@ New `TimeField` methods replace `getConfig()` / `setConfig()`
* Removed `DatetimeField`, `DateField` and `TimeField` methods `getConfig` and `setConfig`. Individual
getters and setters for individual options are provided instead. See above for list of new methods.
* Removed `NumericField_Readonly`. Use `setReadonly(true)` instead.
+* `SSViewer` deprecated methods removed:
+ * `set_source_file_comments()`
+ * `get_source_file_comments()`
+ * `getOption`
+ * `setOption`
### i18n API
diff --git a/main.php b/main.php
index 1f04991fe..8bcd53be6 100644
--- a/main.php
+++ b/main.php
@@ -163,7 +163,8 @@ $chain
// Next, check if we're in dev mode, or the database doesn't have any security data, or we are admin
if (Director::isDev() || !Security::database_is_ready() || Permission::check('ADMIN')) {
- return $reloadToken->reloadWithToken();
+ $reloadToken->reloadWithToken();
+ return;
}
// Fail and redirect the user to the login page
diff --git a/src/Core/Config/Config.php b/src/Core/Config/Config.php
index 9cc3d5b3f..4dcc7fa3c 100644
--- a/src/Core/Config/Config.php
+++ b/src/Core/Config/Config.php
@@ -2,126 +2,15 @@
namespace SilverStripe\Core\Config;
-use SilverStripe\Core\Object;
-use SilverStripe\Core\Manifest\ConfigStaticManifest;
-use SilverStripe\Core\Manifest\ConfigManifest;
-use UnexpectedValueException;
-use stdClass;
+use InvalidArgumentException;
+use SilverStripe\Config\Collections\ConfigCollectionInterface;
+use SilverStripe\Config\Collections\MutableConfigCollectionInterface;
-/**
- * The configuration system works like this:
- *
- * Each class has a set of named properties
- *
- * Each named property can contain either
- *
- * - An array
- * - A non-array value
- *
- * If the value is an array, each value in the array may also be one of those
- * three types.
- *
- * A property can have a value specified in multiple locations, each of which
- * have a hard coded or explicit priority. We combine all these values together
- * into a "composite" value using rules that depend on the priority order of
- * the locations to give the final value, using these rules:
- *
- * - If the value is an array, each array is added to the _beginning_ of the
- * composite array in ascending priority order. If a higher priority item has
- * a non-integer key which is the same as a lower priority item, the value of
- * those items is merged using these same rules, and the result of the merge
- * is located in the same location the higher priority item would be if there
- * was no key clash. Other than in this key-clash situation, within the
- * particular array, order is preserved.
- *
- * - If the value is not an array, the highest priority value is used without
- * any attempt to merge.
- *
- * It is an error to have mixed types of the same named property in different
- * locations (but an error will not necessarily be raised due to optimizations
- * in the lookup code).
- *
- * The exception to this is "false-ish" values - empty arrays, empty strings,
- * etc. When merging a non-false-ish value with a false-ish value, the result
- * will be the non-false-ish value regardless of priority. When merging two
- * false-ish values the result will be the higher priority false-ish value.
- *
- * The locations that configuration values are taken from in highest -> lowest
- * priority order.
- *
- * - Any values set via a call to Config#update.
- *
- * - The configuration values taken from the YAML files in _config directories
- * (internally sorted in before / after order, where the item that is latest
- * is highest priority).
- *
- * - Any static set on an "additional static source" class (such as an
- * extension) named the same as the name of the property.
- *
- * - Any static set on the class named the same as the name of the property.
- *
- * - The composite configuration value of the parent class of this class.
- *
- * At some of these levels you can also set masks. These remove values from the
- * composite value at their priority point rather than add. They are much
- * simpler. They consist of a list of key / value pairs. When applied against
- * the current composite value:
- *
- * - If the composite value is a sequential array, any member of that array
- * that matches any value in the mask is removed.
- *
- * - If the composite value is an associative array, any member of that array
- * that matches both the key and value of any pair in the mask is removed.
- *
- * - If the composite value is not an array, if that value matches any value
- * in the mask it is removed.
- */
-class Config
+abstract class Config
{
- /**
- * A marker instance for the "anything" singleton value. Don't access
- * directly, even in-class, always use self::anything()
- *
- * @var Object
- */
- private static $_anything = null;
-
- /**
- * @var bool
- */
- protected $collectConfigPHPSettings;
-
- /**
- * @var bool
- */
- protected $configPHPIsSafe;
-
- /**
- * Get a marker class instance that is used to do a "remove anything with
- * this key" by adding $key => Config::anything() to the suppress array
- *
- * @return Object
- */
- public static function anything()
- {
- if (self::$_anything === null) {
- self::$_anything = new stdClass();
- }
-
- return self::$_anything;
- }
-
// -- Source options bitmask --
- /**
- * source options bitmask value - merge all parent configuration in as
- * lowest priority.
- *
- * @const
- */
- const INHERITED = 0;
-
/**
* source options bitmask value - only get configuration set for this
* specific class, not any of it's parents.
@@ -130,104 +19,44 @@ class Config
*/
const UNINHERITED = 1;
- /**
- * source options bitmask value - inherit, but stop on the first class
- * that actually provides a value (event an empty value).
- *
- * @const
- */
- const FIRST_SET = 2;
-
/**
* @const source options bitmask value - do not use additional statics
* sources (such as extension)
*/
const EXCLUDE_EXTRA_SOURCES = 4;
- // -- get_value_type response enum --
-
- /**
- * Return flag for get_value_type indicating value is a scalar (or really
- * just not-an-array, at least ATM)
- *
- * @const
- */
- const ISNT_ARRAY = 1;
-
- /**
- * Return flag for get_value_type indicating value is an array.
- * @const
- */
- const IS_ARRAY = 2;
-
- /**
- * Get whether the value is an array or not. Used to be more complicated,
- * but still nice sugar to have an enum to compare and not just a true /
- * false value.
- *
- * @param mixed $val The value
- *
- * @return int One of ISNT_ARRAY or IS_ARRAY
- */
- protected static function get_value_type($val)
- {
- if (is_array($val)) {
- return self::IS_ARRAY;
- }
-
- return self::ISNT_ARRAY;
- }
-
- /**
- * What to do if there's a type mismatch.
- *
- * @throws UnexpectedValueException
- */
- protected static function type_mismatch()
- {
- throw new UnexpectedValueException('Type mismatch in configuration. All values for a particular property must'
- . ' contain the same type (or no value at all).');
- }
-
- /**
- * @todo If we can, replace next static & static methods with DI once that's in
- */
- protected static $instance;
-
/**
* Get the current active Config instance.
*
- * Configs should not normally be manually created.
- *
* In general use you will use this method to obtain the current Config
- * instance.
+ * instance. It assumes the config instance has already been set.
*
- * @return Config
+ * @return ConfigCollectionInterface
*/
public static function inst()
{
- if (!self::$instance) {
- self::$instance = new Config();
- }
-
- return self::$instance;
+ return ConfigLoader::instance()->getManifest();
}
/**
- * Set the current active {@link Config} instance.
+ * Make this config available to be modified
*
- * {@link Config} objects should not normally be manually created.
- *
- * A use case for replacing the active configuration set would be for
- * creating an isolated environment for unit tests.
- *
- * @param Config $instance New instance of Config to assign
- * @return Config Reference to new active Config instance
+ * @return MutableConfigCollectionInterface
*/
- public static function set_instance($instance)
+ public static function modify()
{
- self::$instance = $instance;
- return $instance;
+ $instance = static::inst();
+ if ($instance instanceof MutableConfigCollectionInterface) {
+ return $instance;
+ }
+
+ // By default nested configs should become mutable
+ $instance = static::nest();
+ if ($instance instanceof MutableConfigCollectionInterface) {
+ return $instance;
+ }
+
+ throw new InvalidArgumentException("Nested config could not be made mutable");
}
/**
@@ -238,527 +67,48 @@ class Config
* remove on the new value returned by {@link Config::inst()}, and then discard
* those changes later by calling unnest.
*
- * @return Config Reference to new active Config instance
+ * @return ConfigCollectionInterface Active config
*/
public static function nest()
{
- $current = self::$instance;
-
- $new = clone $current;
- $new->nestedFrom = $current;
- return self::set_instance($new);
+ // Clone current config and nest
+ $new = self::inst()->nest();
+ ConfigLoader::instance()->pushManifest($new);
+ return $new;
}
/**
* Change the active Config back to the Config instance the current active
* Config object was copied from.
*
- * @return Config Reference to new active Config instance
+ * @return ConfigCollectionInterface
*/
public static function unnest()
{
- if (self::inst()->nestedFrom) {
- self::set_instance(self::inst()->nestedFrom);
- } else {
+ // Unnest unless we would be left at 0 manifests
+ $loader = ConfigLoader::instance();
+ if ($loader->countManifests() < 2) {
user_error(
"Unable to unnest root Config, please make sure you don't have mis-matched nest/unnest",
E_USER_WARNING
);
+ } else {
+ $loader->popManifest();
}
- return self::inst();
+ return static::inst();
}
- /**
- * @var array
- */
- protected $cache;
-
- /**
- * Each copy of the Config object need's it's own cache, so changes don't
- * leak through to other instances.
- */
- public function __construct()
- {
- $this->cache = new Config_MemCache();
- }
-
- public function __clone()
- {
- $this->cache = clone $this->cache;
- }
-
- /**
- * @var Config - The config instance this one was copied from when
- * Config::nest() was called.
- */
- protected $nestedFrom = null;
-
- /**
- * @var array - Array of arrays. Each member is an nested array keyed as
- * $class => $name => $value, where value is a config value to treat as
- * the highest priority item.
- */
- protected $overrides = array();
-
- /**
- * @var array $suppresses Array of arrays. Each member is an nested array
- * keyed as $class => $name => $value, where value is a config value suppress
- * from any lower priority item.
- */
- protected $suppresses = array();
-
- /**
- * @var array
- */
- protected $staticManifests = array();
-
- /**
- * @param ConfigStaticManifest $manifest
- */
- public function pushConfigStaticManifest(ConfigStaticManifest $manifest)
- {
- array_unshift($this->staticManifests, $manifest);
-
- $this->cache->clean();
- }
-
- /** @var [array] - The list of settings pulled from config files to search through */
- protected $manifests = array();
-
- /**
- * Add another manifest to the list of config manifests to search through.
- *
- * WARNING: Config manifests to not merge entries, and do not solve before/after rules inter-manifest -
- * instead, the last manifest to be added always wins
- * @param ConfigManifest $manifest
- */
- public function pushConfigYamlManifest(ConfigManifest $manifest)
- {
- array_unshift($this->manifests, $manifest);
-
- // Now that we've got another yaml config manifest we need to clean the cache
- $this->cache->clean();
- // We also need to clean the cache if the manifest's calculated config values change
- $manifest->registerChangeCallback(array($this->cache, 'clean'));
-
- // @todo: Do anything with these. They're for caching after config.php has executed
- $this->collectConfigPHPSettings = true;
- $this->configPHPIsSafe = false;
-
- $manifest->activateConfig();
-
- $this->collectConfigPHPSettings = false;
- }
-
- /** @var [Config_ForClass] - The list of Config_ForClass instances, keyed off class */
- static protected $for_class_instances = array();
-
/**
* Get an accessor that returns results by class by default.
*
* Shouldn't be overridden, since there might be many Config_ForClass instances already held in the wild. Each
* Config_ForClass instance asks the current_instance of Config for the actual result, so override that instead
*
- * @param $class
+ * @param string $class
* @return Config_ForClass
*/
- public function forClass($class)
+ public static function forClass($class)
{
- if (isset(self::$for_class_instances[$class])) {
- return self::$for_class_instances[$class];
- } else {
- return self::$for_class_instances[$class] = new Config_ForClass($class);
- }
- }
-
- /**
- * Merge a lower priority associative array into an existing higher priority associative array, as per the class
- * docblock rules
- *
- * It is assumed you've already checked that you've got two associative arrays, not scalars or sequential arrays
- *
- * @param $dest array - The existing high priority associative array
- * @param $src array - The low priority associative array to merge in
- */
- public static function merge_array_low_into_high(&$dest, $src)
- {
- foreach ($src as $k => $v) {
- if (!$v) {
- continue;
- } elseif (is_int($k)) {
- $dest[] = $v;
- } elseif (isset($dest[$k])) {
- $newType = self::get_value_type($v);
- $currentType = self::get_value_type($dest[$k]);
-
- // Throw error if types don't match
- if ($currentType !== $newType) {
- self::type_mismatch();
- }
-
- if ($currentType == self::IS_ARRAY) {
- self::merge_array_low_into_high($dest[$k], $v);
- } else {
- continue;
- }
- } else {
- $dest[$k] = $v;
- }
- }
- }
-
- /**
- * Merge a higher priority assocative array into an existing lower priority associative array, as per the class
- * docblock rules.
- *
- * Much more expensive that the other way around, as there's no way to insert an associative k/v pair into an
- * array at the top of the array
- *
- * @static
- * @param $dest array - The existing low priority associative array
- * @param $src array - The high priority array to merge in
- */
- public static function merge_array_high_into_low(&$dest, $src)
- {
- $res = $src;
- self::merge_array_low_into_high($res, $dest);
- $dest = $res;
- }
-
- public static function merge_high_into_low(&$result, $value)
- {
- $newType = self::get_value_type($value);
-
- if (!$result) {
- $result = $value;
- } else {
- $currentType = self::get_value_type($result);
- if ($currentType !== $newType) {
- self::type_mismatch();
- }
-
- if ($currentType == self::ISNT_ARRAY) {
- $result = $value;
- } else {
- self::merge_array_high_into_low($result, $value);
- }
- }
- }
-
- public static function merge_low_into_high(&$result, $value, $suppress)
- {
- $newType = self::get_value_type($value);
-
- if ($suppress) {
- if ($newType == self::IS_ARRAY) {
- $value = self::filter_array_by_suppress_array($value, $suppress);
- if (!$value) {
- return;
- }
- } else {
- if (self::check_value_contained_in_suppress_array($value, $suppress)) {
- return;
- }
- }
- }
-
- if (!$result) {
- $result = $value;
- } else {
- $currentType = self::get_value_type($result);
- if ($currentType !== $newType) {
- self::type_mismatch();
- }
-
- if ($currentType == self::ISNT_ARRAY) {
- return; // PASS
- } else {
- self::merge_array_low_into_high($result, $value);
- }
- }
- }
-
- public static function check_value_contained_in_suppress_array($v, $suppresses)
- {
- foreach ($suppresses as $suppress) {
- list($sk, $sv) = $suppress;
- if ($sv === self::anything() || $v == $sv) {
- return true;
- }
- }
- return false;
- }
-
- protected static function check_key_or_value_contained_in_suppress_array($k, $v, $suppresses)
- {
- foreach ($suppresses as $suppress) {
- list($sk, $sv) = $suppress;
- if (($sk === self::anything() || $k == $sk) && ($sv === self::anything() || $v == $sv)) {
- return true;
- }
- }
- return false;
- }
-
- protected static function filter_array_by_suppress_array($array, $suppress)
- {
- $res = array();
-
- foreach ($array as $k => $v) {
- $suppressed = self::check_key_or_value_contained_in_suppress_array($k, $v, $suppress);
-
- if (!$suppressed) {
- if (is_numeric($k)) {
- $res[] = $v;
- } else {
- $res[$k] = $v;
- }
- }
- }
-
- return $res;
- }
-
- protected $extraConfigSources = array();
-
- public function extraConfigSourcesChanged($class)
- {
- unset($this->extraConfigSources[$class]);
- $this->cache->clean("__{$class}");
- }
-
- protected function getUncached($class, $name, $sourceOptions, &$result, $suppress, &$tags)
- {
- $tags[] = "__{$class}";
- $tags[] = "__{$class}__{$name}";
-
- // If result is already not something to merge into, just return it
- if ($result !== null && !is_array($result)) {
- return $result;
- }
-
- // First, look through the override values
- foreach ($this->overrides as $k => $overrides) {
- if (isset($overrides[$class][$name])) {
- $value = $overrides[$class][$name];
-
- self::merge_low_into_high($result, $value, $suppress);
- if ($result !== null && !is_array($result)) {
- return $result;
- }
- }
-
- if (isset($this->suppresses[$k][$class][$name])) {
- $suppress = $suppress
- ? array_merge($suppress, $this->suppresses[$k][$class][$name])
- : $this->suppresses[$k][$class][$name];
- }
- }
-
- $nothing = null;
-
- // Then the manifest values
- foreach ($this->manifests as $manifest) {
- $value = $manifest->get($class, $name, $nothing);
- if ($value !== $nothing) {
- self::merge_low_into_high($result, $value, $suppress);
- if ($result !== null && !is_array($result)) {
- return $result;
- }
- }
- }
-
- $sources = array($class);
-
- // Include extensions only if not flagged not to, and some have been set
- if (($sourceOptions & self::EXCLUDE_EXTRA_SOURCES) != self::EXCLUDE_EXTRA_SOURCES) {
- // If we don't have a fresh list of extra sources, get it from the class itself
- if (!array_key_exists($class, $this->extraConfigSources)) {
- $this->extraConfigSources[$class] = Object::get_extra_config_sources($class);
- }
-
- // Update $sources with any extra sources
- $extraSources = $this->extraConfigSources[$class];
- if ($extraSources) {
- $sources = array_merge($sources, $extraSources);
- }
- }
-
- $value = $nothing = null;
-
- foreach ($sources as $staticSource) {
- if (is_array($staticSource)) {
- $value = isset($staticSource[$name]) ? $staticSource[$name] : $nothing;
- } else {
- foreach ($this->staticManifests as $i => $statics) {
- $value = $statics->get($staticSource, $name, $nothing);
- if ($value !== $nothing) {
- break;
- }
- }
- }
-
- if ($value !== $nothing) {
- self::merge_low_into_high($result, $value, $suppress);
- if ($result !== null && !is_array($result)) {
- return $result;
- }
- }
- }
-
- // Finally, merge in the values from the parent class
- if (($sourceOptions & self::UNINHERITED) != self::UNINHERITED &&
- (($sourceOptions & self::FIRST_SET) != self::FIRST_SET || $result === null)
- ) {
- $parent = get_parent_class($class);
- if ($parent) {
- $this->getUncached($parent, $name, $sourceOptions, $result, $suppress, $tags);
- }
- }
-
- return $result;
- }
-
- /**
- * Get the config value associated for a given class and property
- *
- * This merges all current sources and overrides together to give final value
- * todo: Currently this is done every time. This function is an inner loop function, so we really need to be
- * caching heavily here.
- *
- * @param $class string - The name of the class to get the value for
- * @param $name string - The property to get the value for
- * @param int $sourceOptions Bitmask which can be set to some combintain of Config::UNINHERITED,
- * Config::FIRST_SET, and Config::EXCLUDE_EXTENSIONS.
- *
- * Config::UNINHERITED does not include parent classes when merging configuration fragments
- * Config::FIRST_SET stops inheriting once the first class that sets a value (even an empty value) is encoutered
- * Config::EXCLUDE_EXTRA_SOURCES does not include any additional static sources (such as extensions)
- *
- * Config::INHERITED is a utility constant that can be used to mean "none of the above", equvilient to 0
- * Setting both Config::UNINHERITED and Config::FIRST_SET behaves the same as just Config::UNINHERITED
- *
- * should the parent classes value be merged in as the lowest priority source?
- * @param $result mixed Reference to a variable to put the result in. Also returned, so this can be left
- * as null safely. If you do pass a value, it will be treated as the highest priority
- * value in the result chain
- * @param $suppress array Internal use when called by child classes. Array of mask pairs to filter value by
- * @return mixed The value of the config item, or null if no value set. Could be an associative array,
- * sequential array or scalar depending on value (see class docblock)
- */
- public function get($class, $name, $sourceOptions = 0, &$result = null, $suppress = null)
- {
- // Have we got a cached value? Use it if so
- $key = $class.$name.$sourceOptions;
-
- list($cacheHit, $result) = $this->cache->checkAndGet($key);
- if (!$cacheHit) {
- $tags = array();
- $result = null;
- $this->getUncached($class, $name, $sourceOptions, $result, $suppress, $tags);
- $this->cache->set($key, $result, $tags);
- }
-
- return $result;
- }
-
- /**
- * Update a configuration value
- *
- * Configuration is modify only. The value passed is merged into the existing configuration. If you want to
- * replace the current array value, you'll need to call remove first.
- *
- * @param string $class The class to update a configuration value for
- * @param string $name The configuration property name to update
- * @param mixed $val The value to update with
- *
- * Arrays are recursively merged into current configuration as "latest" - for associative arrays the passed value
- * replaces any item with the same key, for sequential arrays the items are placed at the end of the array, for
- * non-array values, this value replaces any existing value
- *
- * You will get an error if you try and override array values with non-array values or vice-versa
- */
- public function update($class, $name, $val)
- {
- if (is_null($val)) {
- $this->remove($class, $name);
- } else {
- if (!isset($this->overrides[0][$class])) {
- $this->overrides[0][$class] = array();
- }
-
- if (!array_key_exists($name, $this->overrides[0][$class])) {
- $this->overrides[0][$class][$name] = $val;
- } else {
- self::merge_high_into_low($this->overrides[0][$class][$name], $val);
- }
- }
-
- $this->cache->clean("__{$class}__{$name}");
- }
-
- /**
- * Remove a configuration value
- *
- * You can specify a key, a key and a value, or neither. Either argument can be Config::anything(), which is
- * what is defaulted to if you don't specify something
- *
- * This removes any current configuration value that matches the key and/or value specified
- *
- * Works like this:
- * - Check the current override array, and remove any values that match the arguments provided
- * - Keeps track of the arguments passed to this method, and in get filters everything _except_ the current
- * override array to exclude any match
- *
- * This way we can re-set anything removed by a call to this function by calling set. Because the current override
- * array is only filtered immediately on calling this remove method, that value will then be exposed. However,
- * every other source is filtered on request, so no amount of changes to parent's configuration etc can override a
- * remove call.
- *
- * @param string $class The class to remove a configuration value from
- * @param string $name The configuration name
- * @param mixed $key An optional key to filter against.
- * If referenced config value is an array, only members of that array that match this key will be removed
- * Must also match value if provided to be removed
- * @param mixed $value And optional value to filter against.
- * If referenced config value is an array, only members of that array that match this value will be removed
- * If referenced config value is not an array, value will be removed only if it matches this argument
- * Must also match key if provided and referenced config value is an array to be removed
- *
- * Matching is always by "==", not by "==="
- */
- public function remove($class, $name, $key = null, $value = null)
- {
- if (func_num_args() < 3) {
- $key = self::anything();
- }
- if (func_num_args() < 4) {
- $value = self::anything();
- }
-
- $suppress = array($key, $value);
-
- if (isset($this->overrides[0][$class][$name])) {
- $value = $this->overrides[0][$class][$name];
-
- if (is_array($value)) {
- $this->overrides[0][$class][$name] = self::filter_array_by_suppress_array($value, array($suppress));
- } else {
- if (self::check_value_contained_in_suppress_array($value, array($suppress))) {
- unset($this->overrides[0][$class][$name]);
- }
- }
- }
-
- if (!isset($this->suppresses[0][$class])) {
- $this->suppresses[0][$class] = array();
- }
- if (!isset($this->suppresses[0][$class][$name])) {
- $this->suppresses[0][$class][$name] = array();
- }
-
- $this->suppresses[0][$class][$name][] = $suppress;
-
- $this->cache->clean("__{$class}__{$name}");
+ return new Config_ForClass($class);
}
}
diff --git a/src/Core/Config/ConfigLoader.php b/src/Core/Config/ConfigLoader.php
new file mode 100644
index 000000000..caa50201a
--- /dev/null
+++ b/src/Core/Config/ConfigLoader.php
@@ -0,0 +1,90 @@
+manifests[count($this->manifests) - 1];
+ }
+
+ /**
+ * Returns true if this class loader has a manifest.
+ *
+ * @return bool
+ */
+ public function hasManifest()
+ {
+ return (bool)$this->manifests;
+ }
+
+ /**
+ * Pushes a class manifest instance onto the top of the stack.
+ *
+ * @param ConfigCollectionInterface $manifest
+ */
+ public function pushManifest(ConfigCollectionInterface $manifest)
+ {
+ $this->manifests[] = $manifest;
+ }
+
+ /**
+ * @return ConfigCollectionInterface
+ */
+ public function popManifest()
+ {
+ return array_pop($this->manifests);
+ }
+
+ /**
+ * Check number of manifests
+ *
+ * @return int
+ */
+ public function countManifests()
+ {
+ return count($this->manifests);
+ }
+
+ /**
+ * Nest the current manifest
+ *
+ * @return ConfigCollectionInterface
+ */
+ public function nest()
+ {
+ $manifest = $this->getManifest()->nest();
+ $this->pushManifest($manifest);
+ return $manifest;
+ }
+}
diff --git a/src/Core/Config/Config_ForClass.php b/src/Core/Config/Config_ForClass.php
index 58cbf6520..b89e5007b 100644
--- a/src/Core/Config/Config_ForClass.php
+++ b/src/Core/Config/Config_ForClass.php
@@ -2,20 +2,21 @@
namespace SilverStripe\Core\Config;
+use SilverStripe\Dev\Deprecation;
+
class Config_ForClass
{
-
/**
* @var string $class
*/
protected $class;
/**
- * @param string $class
+ * @param string|object $class
*/
public function __construct($class)
{
- $this->class = $class;
+ $this->class = is_object($class) ? get_class($class) : $class;
}
/**
@@ -33,19 +34,45 @@ class Config_ForClass
*/
public function __set($name, $val)
{
- $this->update($name, $val);
+ $this->set($name, $val);
}
/**
* Explicit pass-through to Config::update()
*
* @param string $name
- * @param mixed $val
+ * @param mixed $value
* @return $this
*/
- public function update($name, $val)
+ public function update($name, $value)
{
- Config::inst()->update($this->class, $name, $val);
+ Deprecation::notice('5.0', 'Use merge() instead');
+ return $this->merge($name, $value);
+ }
+
+ /**
+ * Merge a given config
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return $this
+ */
+ public function merge($name, $value)
+ {
+ Config::modify()->merge($this->class, $name, $value);
+ return $this;
+ }
+
+ /**
+ * Replace config value
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return $this
+ */
+ public function set($name, $value)
+ {
+ Config::modify()->set($this->class, $name, $value);
return $this;
}
@@ -61,12 +88,12 @@ class Config_ForClass
/**
* @param string $name
- * @param int $sourceOptions
+ * @param mixed $options
* @return mixed
*/
- public function get($name, $sourceOptions = 0)
+ public function get($name, $options = 0)
{
- return Config::inst()->get($this->class, $name, $sourceOptions);
+ return Config::inst()->get($this->class, $name, $options);
}
/**
@@ -77,7 +104,7 @@ class Config_ForClass
*/
public function remove($name)
{
- Config::inst()->remove($this->class, $name);
+ Config::modify()->remove($this->class, $name);
return $this;
}
@@ -88,6 +115,17 @@ class Config_ForClass
*/
public function forClass($class)
{
- return Config::inst()->forClass($class);
+ return Config::forClass($class);
+ }
+
+ /**
+ * Get uninherited config
+ *
+ * @param string $name Name of config
+ * @return mixed
+ */
+ public function uninherited($name)
+ {
+ return $this->get($name, Config::UNINHERITED);
}
}
diff --git a/src/Core/Config/Config_MemCache.php b/src/Core/Config/Config_MemCache.php
deleted file mode 100644
index 8159f29e0..000000000
--- a/src/Core/Config/Config_MemCache.php
+++ /dev/null
@@ -1,80 +0,0 @@
-cache = array();
- }
-
- public function set($key, $val, $tags = array())
- {
- foreach ($tags as $t) {
- if (!isset($this->tags[$t])) {
- $this->tags[$t] = array();
- }
- $this->tags[$t][$key] = true;
- }
-
- $this->cache[$key] = array($val, $tags);
- }
-
- private $hit = 0;
- private $miss = 0;
-
- public function stats()
- {
- return $this->miss ? ($this->hit / $this->miss) : 0;
- }
-
- public function get($key)
- {
- list($hit, $result) = $this->checkAndGet($key);
- return $hit ? $result : false;
- }
-
- /**
- * Checks for a cache hit and returns the value as a multi-value return
- *
- * @param string $key
- * @return array First element boolean, isHit. Second element the actual result.
- */
- public function checkAndGet($key)
- {
- if (array_key_exists($key, $this->cache)) {
- ++$this->hit;
- return array(true, $this->cache[$key][0]);
- } else {
- ++$this->miss;
- return array(false, null);
- }
- }
-
- public function clean($tag = null)
- {
- if ($tag) {
- if (isset($this->tags[$tag])) {
- foreach ($this->tags[$tag] as $k => $dud) {
- // Remove the key from everywhere else it is tagged
- $ts = $this->cache[$k][1];
- foreach ($ts as $t) {
- unset($this->tags[$t][$k]);
- }
- unset($this->cache[$k]);
- }
- unset($this->tags[$tag]);
- }
- } else {
- $this->cache = array();
- $this->tags = array();
- }
- }
-}
diff --git a/src/Core/Config/Configurable.php b/src/Core/Config/Configurable.php
index 8e1ad8574..57f3173a3 100644
--- a/src/Core/Config/Configurable.php
+++ b/src/Core/Config/Configurable.php
@@ -2,6 +2,8 @@
namespace SilverStripe\Core\Config;
+use SilverStripe\Dev\Deprecation;
+
/**
* Provides extensions to this object to integrate it with standard config API methods.
*
@@ -17,29 +19,19 @@ trait Configurable
*/
public static function config()
{
- return Config::inst()->forClass(get_called_class());
+ return Config::forClass(get_called_class());
}
/**
- * Gets the first set value for the given config option
+ * Get inherited config value
*
* @param string $name
* @return mixed
*/
public function stat($name)
{
- return Config::inst()->get(get_class($this), $name, Config::FIRST_SET);
- }
-
- /**
- * Update the config value for a given property
- *
- * @param string $name
- * @param mixed $value
- */
- public function set_stat($name, $value)
- {
- Config::inst()->update(get_class($this), $name, $value);
+ Deprecation::notice('5.0', 'Use ->get');
+ return $this->config()->get($name);
}
/**
@@ -50,6 +42,20 @@ trait Configurable
*/
public function uninherited($name)
{
- return Config::inst()->get(get_class($this), $name, Config::UNINHERITED);
+ return $this->config()->uninherited($name);
+ }
+
+ /**
+ * Update the config value for a given property
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return $this
+ */
+ public function set_stat($name, $value)
+ {
+ Deprecation::notice('5.0', 'Use ->config()->set()');
+ $this->config()->set($name, $value);
+ return $this;
}
}
diff --git a/src/Core/Config/CoreConfigCreator.php b/src/Core/Config/CoreConfigCreator.php
new file mode 100644
index 000000000..57bd284f5
--- /dev/null
+++ b/src/Core/Config/CoreConfigCreator.php
@@ -0,0 +1,171 @@
+setPool(new FilesystemAdapter('configcache', 0, getTempFolder()));
+ $instance->setFlush($flush);
+
+ // Set collection creator
+ $instance->setCollectionCreator(function () {
+ return $this->createCore();
+ });
+
+ return $instance;
+ }
+
+ /**
+ * Rebuild new uncached config, which is mutable
+ *
+ * @return MemoryConfigCollection
+ */
+ public function createCore()
+ {
+ $config = new MemoryConfigCollection();
+
+ // Set default middleware
+ $config->setMiddlewares([
+ new InheritanceMiddleware(Config::UNINHERITED),
+ new ExtensionMiddleware(Config::EXCLUDE_EXTRA_SOURCES),
+ ]);
+
+ // Transform
+ $config->transform([
+ $this->buildStaticTransformer(),
+ $this->buildYamlTransformer()
+ ]);
+
+ return $config;
+ }
+
+ /**
+ * @return YamlTransformer
+ */
+ protected function buildYamlTransformer()
+ {
+ // Get all module dirs
+ $modules = ModuleLoader::instance()->getManifest()->getModules();
+ $dirs = [];
+ foreach ($modules as $module) {
+ // Load from _config dirs
+ $path = $module->getPath() . '/_config';
+ if (is_dir($path)) {
+ $dirs[] = $path;
+ }
+ }
+
+ return $this->buildYamlTransformerForPath($dirs);
+ }
+
+ /**
+ * @return PrivateStaticTransformer
+ */
+ public function buildStaticTransformer()
+ {
+ return new PrivateStaticTransformer(function () {
+ $classes = ClassLoader::instance()->getManifest()->getClasses();
+ return array_keys($classes);
+ });
+ }
+
+ /**
+ * @param array|string $dirs Base dir to load from
+ * @return YamlTransformer
+ */
+ public function buildYamlTransformerForPath($dirs)
+ {
+ // Construct
+ $transformer = YamlTransformer::create(
+ BASE_PATH,
+ Finder::create()
+ ->in($dirs)
+ ->files()
+ ->name('/\.(yml|yaml)$/')
+ );
+
+ // Add default rules
+ $envvarset = function ($var, $value = null) {
+ if (getenv($var) === false) {
+ return false;
+ }
+ if ($value) {
+ return getenv($var) === $value;
+ }
+ return true;
+ };
+ $constantdefined = function ($const, $value = null) {
+ if (!defined($const)) {
+ return false;
+ }
+ if ($value) {
+ return constant($const) === $value;
+ }
+ return true;
+ };
+ return $transformer
+ ->addRule('classexists', function ($class) {
+ return class_exists($class);
+ })
+ ->addRule('envvarset', $envvarset)
+ ->addRule('constantdefined', $constantdefined)
+ ->addRule(
+ 'envorconstant',
+ // Composite rule
+ function ($name, $value = null) use ($envvarset, $constantdefined) {
+ return $envvarset($name, $value) || $constantdefined($name, $value);
+ }
+ )
+ ->addRule('environment', function ($env) {
+ $current = Director::get_environment_type();
+ return strtolower($current) === strtolower($env);
+ })
+ ->addRule('moduleexists', function ($module) {
+ return ModuleLoader::instance()->getManifest()->moduleExists($module);
+ });
+ }
+}
diff --git a/src/Core/Config/DAG.php b/src/Core/Config/DAG.php
deleted file mode 100644
index a70d4a977..000000000
--- a/src/Core/Config/DAG.php
+++ /dev/null
@@ -1,115 +0,0 @@
- [$from_idx1, $from_idx2, ...] format
- * @var array
- */
- protected $dag;
-
- public function __construct($data = null)
- {
- $data = $data ? array_values($data) : array();
-
- $this->data = $data;
- $this->dag = array_fill_keys(array_keys($data), array());
- }
-
- /**
- * Add another node/vertex
- * @param mixed $item The item to add to the graph
- */
- public function additem($item)
- {
- $this->data[] = $item;
- $this->dag[] = array();
- }
-
- /**
- * Add an edge from one vertex to another.
- *
- * When passing actual nodes (as opposed to indexes), uses array_search with strict = true to find
- *
- * @param int $from The index in $data of the node/vertex, or the node/vertex
- * itself, that the edge goes from
- * @param int $to The index in $data of the node/vertex, or the node/vertex
- * itself, that the edge goes to
- * @throws Exception
- */
- public function addedge($from, $to)
- {
- $i = is_numeric($from) ? $from : array_search($from, $this->data, true);
- $j = is_numeric($to) ? $to : array_search($to, $this->data, true);
-
- if ($i === false) {
- throw new Exception("Couldnt find 'from' item in data when adding edge to DAG");
- }
- if ($j === false) {
- throw new Exception("Couldnt find 'to' item in data when adding edge to DAG");
- }
-
- if (!isset($this->dag[$j])) {
- $this->dag[$j] = array();
- }
- $this->dag[$j][] = $i;
- }
-
- /**
- * Sort graph so that each node (a) comes before any nodes (b) where an edge exists from a to b
- * @return array - The nodes
- * @throws Exception - If the graph is cyclic (and so can't be sorted)
- */
- public function sort()
- {
- $data = $this->data;
- $dag = $this->dag;
- $sorted = array();
-
- while (true) {
- $withedges = array_filter($dag, 'count');
- $starts = array_diff_key($dag, $withedges);
-
- if (!count($starts)) {
- break;
- }
-
- foreach ($starts as $i => $foo) {
- $sorted[] = $data[$i];
- }
-
- foreach ($withedges as $j => $deps) {
- $withedges[$j] = array_diff($withedges[$j], array_keys($starts));
- }
-
- $dag = $withedges;
- }
-
- if ($dag) {
- $remainder = new DAG($data);
- $remainder->dag = $dag;
- throw new DAG_CyclicException("DAG has cyclic requirements", $remainder);
- }
- return $sorted;
- }
-
- public function getIterator()
- {
- return new DAG_Iterator($this->data, $this->dag);
- }
-}
diff --git a/src/Core/Config/DAG_CyclicException.php b/src/Core/Config/DAG_CyclicException.php
deleted file mode 100644
index 32f190a69..000000000
--- a/src/Core/Config/DAG_CyclicException.php
+++ /dev/null
@@ -1,25 +0,0 @@
-dag = $dag;
- parent::__construct($message);
- }
-}
diff --git a/src/Core/Config/DAG_Iterator.php b/src/Core/Config/DAG_Iterator.php
deleted file mode 100644
index 1a4c39870..000000000
--- a/src/Core/Config/DAG_Iterator.php
+++ /dev/null
@@ -1,57 +0,0 @@
-data = $data;
- $this->dag = $dag;
- $this->rewind();
- }
-
- public function key()
- {
- return $this->i;
- }
-
- public function current()
- {
- $res = array();
-
- $res['from'] = $this->data[$this->i];
-
- $res['to'] = array();
- foreach ($this->dag[$this->i] as $to) {
- $res['to'][] = $this->data[$to];
- }
-
- return $res;
- }
-
- public function next()
- {
- $this->i = array_shift($this->dagkeys);
- }
-
- public function rewind()
- {
- $this->dagkeys = array_keys($this->dag);
- $this->next();
- }
-
- public function valid()
- {
- return $this->i !== null;
- }
-}
diff --git a/src/Core/Config/Middleware/ExtensionMiddleware.php b/src/Core/Config/Middleware/ExtensionMiddleware.php
new file mode 100644
index 000000000..e78b6c1ed
--- /dev/null
+++ b/src/Core/Config/Middleware/ExtensionMiddleware.php
@@ -0,0 +1,84 @@
+enabled($options)) {
+ return $config;
+ }
+
+ foreach ($this->getExtraConfig($class, $config) as $extra) {
+ $config = Priority::mergeArray($extra, $config);
+ }
+ return $config;
+ }
+
+ /**
+ * Applied config to a class from its extensions
+ *
+ * @param string $class
+ * @param array $classConfig
+ * @return Generator
+ */
+ protected function getExtraConfig($class, $classConfig)
+ {
+ if (empty($classConfig['extensions'])) {
+ return;
+ }
+
+ $extensions = $classConfig['extensions'];
+ foreach ($extensions as $extension) {
+ list($extensionClass, $extensionArgs) = Object::parse_class_spec($extension);
+ if (!class_exists($extensionClass)) {
+ throw new InvalidArgumentException("$class references nonexistent $extensionClass in 'extensions'");
+ }
+
+ // Init extension
+ call_user_func(array($extensionClass, 'add_to_class'), $class, $extensionClass, $extensionArgs);
+
+ // Check class hierarchy from root up
+ foreach (ClassInfo::ancestry($extensionClass) as $extensionClassParent) {
+ // Merge config from extension
+ $extensionConfig = Config::inst()->get($extensionClassParent, null, true);
+ if ($extensionConfig) {
+ yield $extensionConfig;
+ }
+ if (ClassInfo::has_method_from($extensionClassParent, 'get_extra_config', $extensionClassParent)) {
+ $extensionConfig = call_user_func(
+ [ $extensionClassParent, 'get_extra_config' ],
+ $class,
+ $extensionClass,
+ $extensionArgs
+ );
+ if ($extensionConfig) {
+ yield $extensionConfig;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Core/Config/Middleware/InheritanceMiddleware.php b/src/Core/Config/Middleware/InheritanceMiddleware.php
new file mode 100644
index 000000000..647954e10
--- /dev/null
+++ b/src/Core/Config/Middleware/InheritanceMiddleware.php
@@ -0,0 +1,36 @@
+enabled($options)) {
+ return $next($class, $options);
+ }
+
+ // Merge hierarchy
+ $config = [];
+ foreach (ClassInfo::ancestry($class) as $nextClass) {
+ $nextConfig = $next($nextClass, $options);
+ $config = Priority::mergeArray($nextConfig, $config);
+ }
+ return $config;
+ }
+}
diff --git a/src/Core/Config/Middleware/MiddlewareCommon.php b/src/Core/Config/Middleware/MiddlewareCommon.php
new file mode 100644
index 000000000..5ff36ffe1
--- /dev/null
+++ b/src/Core/Config/Middleware/MiddlewareCommon.php
@@ -0,0 +1,49 @@
+disableFlag = $disableFlag;
+ }
+
+ protected function enabled($options)
+ {
+ if ($options === true) {
+ return false;
+ }
+ if (!$this->disableFlag) {
+ return true;
+ }
+ if (is_array($options)) {
+ if (!isset($options['disableFlag'])) {
+ return true;
+ }
+ $options = $options['disableFlag'];
+ }
+
+ return ($options & $this->disableFlag) !== $this->disableFlag;
+ }
+
+ public function serialize()
+ {
+ return json_encode([$this->disableFlag]);
+ }
+
+ public function unserialize($serialized)
+ {
+ list($this->disableFlag) = json_decode($serialized, true);
+ }
+}
diff --git a/src/Core/Core.php b/src/Core/Core.php
index 93105fc8e..abc1731af 100644
--- a/src/Core/Core.php
+++ b/src/Core/Core.php
@@ -1,13 +1,15 @@
'SilverStripe\\Core\\Injector\\SilverStripeServiceConfigurationLocator'));
+$injector = new Injector(array('locator' => SilverStripeServiceConfigurationLocator::class));
Injector::set_inst($injector);
///////////////////////////////////////////////////////////////////////////////
@@ -76,13 +78,16 @@ $loader = ClassLoader::instance();
$loader->registerAutoloader();
$loader->pushManifest($manifest);
-// Now that the class manifest is up, load the static configuration
-$configManifest = new ConfigStaticManifest();
-Config::inst()->pushConfigStaticManifest($configManifest);
+// Init module manifest
+$moduleManifest = new ModuleManifest(BASE_PATH, false, $flush);
+ModuleLoader::instance()->pushManifest($moduleManifest);
-// And then the yaml configuration
-$configManifest = new ConfigManifest(BASE_PATH, false, $flush);
-Config::inst()->pushConfigYamlManifest($configManifest);
+// Build config manifest
+$configManifest = CoreConfigCreator::inst()->createRoot($flush);
+ConfigLoader::instance()->pushManifest($configManifest);
+
+// After loading config, boot _config.php files
+ModuleLoader::instance()->getManifest()->activateConfig();
// Load template manifest
SilverStripe\View\ThemeResourceLoader::instance()->addSet('$default', new SilverStripe\View\ThemeManifest(
@@ -103,7 +108,6 @@ if (Director::isLive()) {
/**
* Load error handlers
*/
-
$errorHandler = Injector::inst()->get('ErrorHandler');
$errorHandler->start();
diff --git a/src/Core/Extensible.php b/src/Core/Extensible.php
index 7e92c0c56..6b4953d90 100644
--- a/src/Core/Extensible.php
+++ b/src/Core/Extensible.php
@@ -122,11 +122,7 @@ trait Extensible
if (in_array($class, self::$unextendable_classes)) {
continue;
}
- $extensions = Config::inst()->get(
- $class,
- 'extensions',
- Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES
- );
+ $extensions = Config::inst()->get($class, 'extensions', true);
if ($extensions) {
foreach ($extensions as $extension) {
@@ -217,8 +213,10 @@ trait Extensible
}
}
- Config::inst()->update($class, 'extensions', array($extension));
- Config::inst()->extraConfigSourcesChanged($class);
+ Config::modify()
+ ->merge($class, 'extensions', array(
+ $extension
+ ));
Injector::inst()->unregisterNamedObject($class);
@@ -234,6 +232,8 @@ trait Extensible
/**
* Remove an extension from a class.
+ * Note: This will not remove extensions from parent classes, and must be called
+ * directly on the class assigned the extension.
*
* Keep in mind that this won't revert any datamodel additions
* of the extension at runtime, unless its used before the
@@ -252,22 +252,23 @@ trait Extensible
{
$class = get_called_class();
- Config::inst()->remove($class, 'extensions', Config::anything(), $extension);
-
- // remove any instances of the extension with parameters
- $config = Config::inst()->get($class, 'extensions');
-
- if ($config) {
- foreach ($config as $k => $v) {
- // extensions with parameters will be stored in config as
- // ExtensionName("Param").
- if (preg_match(sprintf("/^(%s)\(/", preg_quote($extension, '/')), $v)) {
- Config::inst()->remove($class, 'extensions', Config::anything(), $v);
- }
+ // Build filtered extension list
+ $found = false;
+ $config = Config::inst()->get($class, 'extensions', true) ?: [];
+ foreach ($config as $key => $candidate) {
+ // extensions with parameters will be stored in config as ExtensionName("Param").
+ if (strcasecmp($candidate, $extension) === 0 ||
+ stripos($candidate, $extension.'(') === 0
+ ) {
+ $found = true;
+ unset($config[$key]);
}
}
-
- Config::inst()->extraConfigSourcesChanged($class);
+ // Don't dirty cache if no changes
+ if (!$found) {
+ return;
+ }
+ Config::modify()->set($class, 'extensions', $config);
// unset singletons to avoid side-effects
Injector::inst()->unregisterAllObjects();
@@ -292,7 +293,7 @@ trait Extensible
*/
public static function get_extensions($class, $includeArgumentString = false)
{
- $extensions = Config::inst()->get($class, 'extensions');
+ $extensions = Config::forClass($class)->get('extensions', Config::EXCLUDE_EXTRA_SOURCES);
if (empty($extensions)) {
return array();
}
@@ -329,7 +330,7 @@ trait Extensible
$sources = null;
// Get a list of extensions
- $extensions = Config::inst()->get($class, 'extensions', Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES);
+ $extensions = Config::inst()->get($class, 'extensions', true);
if (!$extensions) {
return null;
diff --git a/src/Core/Extension.php b/src/Core/Extension.php
index 85221d3d8..0914ab635 100644
--- a/src/Core/Extension.php
+++ b/src/Core/Extension.php
@@ -21,7 +21,7 @@ abstract class Extension
* This is used by extensions designed to be applied to controllers.
* It works the same way as {@link Controller::$allowed_actions}.
*/
- private static $allowed_actions = null;
+ private static $allowed_actions = [];
/**
* The object this extension is applied to.
diff --git a/src/Core/Manifest/ClassManifest.php b/src/Core/Manifest/ClassManifest.php
index 27c0e4bd4..65bd67a30 100644
--- a/src/Core/Manifest/ClassManifest.php
+++ b/src/Core/Manifest/ClassManifest.php
@@ -386,8 +386,8 @@ class ClassManifest
*/
protected function setDefaults()
{
- $this->classes['sstemplateparser'] = FRAMEWORK_PATH.'/View/SSTemplateParser.php';
- $this->classes['sstemplateparseexception'] = FRAMEWORK_PATH.'/View/SSTemplateParseException.php';
+ $this->classes['sstemplateparser'] = FRAMEWORK_PATH.'/src/View/SSTemplateParser.php';
+ $this->classes['sstemplateparseexception'] = FRAMEWORK_PATH.'/src/View/SSTemplateParseException.php';
}
/**
diff --git a/src/Core/Manifest/ConfigStaticManifest.php b/src/Core/Manifest/ConfigStaticManifest.php
deleted file mode 100644
index f7304ae0c..000000000
--- a/src/Core/Manifest/ConfigStaticManifest.php
+++ /dev/null
@@ -1,35 +0,0 @@
-name, $class) === 0) {
- if ($reflection->hasProperty($name)) {
- $property = $reflection->getProperty($name);
- if ($property->isStatic() && $property->isPrivate()) {
- $property->setAccessible(true);
- return $property->getValue();
- }
- }
- }
- }
- return null;
- }
-}