forked from silverstripe/silverstripe-framework
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
API Split Config into ImmutableConfig and MutableConfig
API Remove Config::INHERITED
- Loading branch information
Damian Mooyman
committed
Feb 13, 2017
1 parent
e6aa894
commit 0a2b550
Showing
8 changed files
with
804 additions
and
343 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
<?php | ||
|
||
namespace SilverStripe\Core\Config; | ||
|
||
use micmania1\config\MergeStrategy\Priority; | ||
use Psr\Cache\CacheItemInterface; | ||
use Psr\Cache\CacheItemPoolInterface; | ||
use SilverStripe\Core\Extension; | ||
use SilverStripe\Core\Object; | ||
|
||
class ImmutableConfig extends Config | ||
{ | ||
/** | ||
* In memory cache is used per-request to prevent unnecessary calls to cache | ||
* which can have latency. | ||
* | ||
* Keyed first by class and then lookup mask | ||
* | ||
* @var array | ||
*/ | ||
protected $memoryCache = []; | ||
|
||
/** | ||
* Cache applied to api-level calls (respective of merge options) | ||
* Applies on top of $memoryCache | ||
* | ||
* Note: This cache does NOT include local modifications applied via | ||
* remove(), replace() and merge() | ||
* | ||
* @var CacheItemPoolInterface | ||
*/ | ||
protected $persistentCache = null; | ||
|
||
/** | ||
* @param CacheItemPoolInterface $persistentCache | ||
*/ | ||
public function __construct( | ||
CacheItemPoolInterface $persistentCache | ||
) { | ||
$this->persistentCache = $persistentCache; | ||
} | ||
|
||
/** | ||
* Get the value of a config property class.name | ||
* | ||
* @param string $class | ||
* @param string $name | ||
* @var int $sourceOptions | ||
* @return mixed | ||
*/ | ||
public function get($class, $name = null, $sourceOptions = 0) | ||
{ | ||
$this->validateSourceOption($sourceOptions); | ||
|
||
// Load config from cache | ||
$classConfig = $this->getCachedClassConfig($class, $sourceOptions, $cacheItem); | ||
if (!isset($classConfig)) { | ||
$classConfig = $this->loadClassConfig($class, $sourceOptions, $cacheItem); | ||
if (!isset($classConfig)) { | ||
return null; | ||
} | ||
} | ||
|
||
// Return either name, or whole-class config | ||
if ($name) { | ||
return isset($classConfig[$name]) ? $classConfig[$name] : null; | ||
} | ||
return $classConfig; | ||
} | ||
|
||
/** | ||
* Load class config from manifest and apply local modifications. | ||
* Applies these modifications to the cache as well as necessary. | ||
* | ||
* @param string $class | ||
* @param int $sourceOptions | ||
* @param CacheItemInterface $cacheItem | ||
* @return array | ||
*/ | ||
protected function loadClassConfig($class, $sourceOptions, $cacheItem) | ||
{ | ||
// Load raw cache | ||
$classConfig = $this->getClassConfig($class, $sourceOptions); | ||
|
||
// Save raw config to persistant cache | ||
$cacheItem->set($classConfig); | ||
$this->persistentCache->saveDeferred($cacheItem); | ||
|
||
// Save in local cache | ||
if (!isset($this->memoryCache[$class])) { | ||
$this->memoryCache[$class] = []; | ||
} | ||
$this->memoryCache[$class][$sourceOptions] = $classConfig; | ||
return $classConfig; | ||
} | ||
|
||
/** | ||
* Load config for a given class from cache. | ||
* This config result will include local modifications(). | ||
* Additionally this method will hydrate local cache from persistent | ||
* cache as-needed. | ||
* | ||
* @param string $class | ||
* @param int $sourceOptions | ||
* @param CacheItemInterface $cacheItem Cache item which should be used | ||
* to write back to persistent store | ||
* @return array|null Cached config (with local modifications) or null if not cached | ||
*/ | ||
protected function getCachedClassConfig($class, $sourceOptions, CacheItemInterface &$cacheItem) | ||
{ | ||
$cacheItem = null; | ||
|
||
// First hit from in-memory cache | ||
if (isset($this->memoryCache[$class][$sourceOptions])) { | ||
return $this->memoryCache[$class][$sourceOptions]; | ||
} | ||
|
||
// Get from persistent cache | ||
$key = $this->getClassConfigCacheKey($class, $sourceOptions); | ||
$cacheItem = $this->persistentCache->getItem($key); | ||
if (!$cacheItem->isHit()) { | ||
return null; | ||
} | ||
|
||
// Load from persistent -> local cache | ||
$classConfig = $cacheItem->get(); | ||
if (!isset($this->memoryCache[$class])) { | ||
$this->memoryCache[$class] = []; | ||
} | ||
$this->memoryCache[$class][$sourceOptions] = $classConfig; | ||
return $classConfig; | ||
} | ||
|
||
/** | ||
* Get the class config for the given class with the given source options | ||
* | ||
* @param string $class | ||
* @param int $sourceOptions | ||
* @return array | ||
*/ | ||
protected function getClassConfig($class, $sourceOptions = 0) | ||
{ | ||
$classConfig = ConfigLoader::instance()->getManifest()->get($class); | ||
|
||
if ($this->shouldApplyExtraConfig($class, $sourceOptions)) { | ||
$this->applyExtraConfig($class, $sourceOptions, $classConfig); | ||
} | ||
|
||
return $classConfig; | ||
} | ||
|
||
/** | ||
* Applied config to a class from its extensions | ||
* | ||
* @param string $class | ||
* @param int $sourceOptions | ||
* @param mixed $classConfig | ||
*/ | ||
protected function applyExtraConfig($class, $sourceOptions, &$classConfig) | ||
{ | ||
$extraSources = Object::get_extra_config_sources($class); | ||
if (empty($extraSources)) { | ||
return; | ||
} | ||
|
||
$priority = new Priority; | ||
foreach ($extraSources as $source) { | ||
if (is_string($source)) { | ||
$source = $this->getClassConfig( | ||
$source, | ||
self::UNINHERITED | self::EXCLUDE_EXTRA_SOURCES | ||
); | ||
} | ||
|
||
if (is_array($source)) { | ||
if (is_null($classConfig) || !is_array($classConfig)) { | ||
$classConfig = $source; | ||
continue; | ||
} | ||
|
||
$classConfig = $priority->mergeArray($classConfig, $source); | ||
} elseif (!is_null($source)) { | ||
$classConfig = $source; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* A check to test if we should include extra config (data extensions) | ||
* | ||
* @param string $class | ||
* @param int $sourceOptions | ||
* @return bool | ||
*/ | ||
protected function shouldApplyExtraConfig($class, $sourceOptions) | ||
{ | ||
if (is_a($class, Extension::class, true)) { | ||
return false; | ||
} | ||
|
||
return ($sourceOptions & self::EXCLUDE_EXTRA_SOURCES) != self::EXCLUDE_EXTRA_SOURCES; | ||
} | ||
|
||
/** | ||
* Get cache key | ||
* | ||
* @param string $class | ||
* @param int $sourceOptions | ||
* @return string | ||
*/ | ||
protected function getClassConfigCacheKey($class, $sourceOptions) | ||
{ | ||
$parts = [strtolower($class), $sourceOptions]; | ||
return md5(implode('-', $parts)); | ||
} | ||
|
||
/** | ||
* Build nested config | ||
* | ||
* @return MutableConfig | ||
*/ | ||
public function cloneNest() | ||
{ | ||
// Nested child of immutable config is mutable config | ||
return new MutableConfig($this); | ||
} | ||
|
||
/** | ||
* Get parent config | ||
* | ||
* @return Config | ||
*/ | ||
public function getNestedFrom() | ||
{ | ||
// Immutable config has no parent | ||
return null; | ||
} | ||
} |
Oops, something went wrong.