Skip to content

Commit eec1d82

Browse files
committed
Merge remote-tracking branch 'origin/develop' into MAGETWO-60598
# Conflicts: # app/code/Magento/Config/App/Config/Type/System.php
2 parents cec75df + d816ce7 commit eec1d82

File tree

7 files changed

+412
-146
lines changed

7 files changed

+412
-146
lines changed

app/code/Magento/Config/App/Config/Type/System.php

Lines changed: 133 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
namespace Magento\Config\App\Config\Type;
77

88
use Magento\Framework\App\Config\ConfigTypeInterface;
9-
use Magento\Framework\DataObject;
9+
use Magento\Framework\App\ObjectManager;
10+
use Magento\Config\App\Config\Type\System\Reader;
1011

1112
/**
12-
* Class process source, cache them and retrieve value by path
13+
* System configuration type
1314
* @api
1415
*/
1516
class System implements ConfigTypeInterface
@@ -24,9 +25,9 @@ class System implements ConfigTypeInterface
2425
private $source;
2526

2627
/**
27-
* @var DataObject
28+
* @var array
2829
*/
29-
private $data;
30+
private $data = [];
3031

3132
/**
3233
* @var \Magento\Framework\App\Config\Spi\PostProcessorInterface
@@ -66,14 +67,18 @@ class System implements ConfigTypeInterface
6667
private $configType;
6768

6869
/**
69-
* Key name for flag which displays whether configuration is cached or not.
70+
* @var Reader
71+
*/
72+
private $reader;
73+
74+
/**
75+
* List of scopes that were retrieved from configuration storage
7076
*
71-
* Once configuration is cached additional flag pushed to cache storage
72-
* to be able check cache existence without data load.
77+
* Is used to make sure that we don't try to load non-existing configuration scopes.
7378
*
74-
* @var string
79+
* @var array
7580
*/
76-
private $cacheExistenceKey;
81+
private $availableDataScopes = null;
7782

7883
/**
7984
* @param \Magento\Framework\App\Config\ConfigSourceInterface $source
@@ -84,6 +89,7 @@ class System implements ConfigTypeInterface
8489
* @param \Magento\Framework\App\Config\Spi\PreProcessorInterface $preProcessor
8590
* @param int $cachingNestedLevel
8691
* @param string $configType
92+
* @param Reader $reader
8793
*/
8894
public function __construct(
8995
\Magento\Framework\App\Config\ConfigSourceInterface $source,
@@ -93,7 +99,8 @@ public function __construct(
9399
\Magento\Framework\Serialize\SerializerInterface $serializer,
94100
\Magento\Framework\App\Config\Spi\PreProcessorInterface $preProcessor,
95101
$cachingNestedLevel = 1,
96-
$configType = self::CONFIG_TYPE
102+
$configType = self::CONFIG_TYPE,
103+
Reader $reader = null
97104
) {
98105
$this->source = $source;
99106
$this->postProcessor = $postProcessor;
@@ -103,150 +110,174 @@ public function __construct(
103110
$this->fallback = $fallback;
104111
$this->serializer = $serializer;
105112
$this->configType = $configType;
106-
$this->cacheExistenceKey = $this->configType . '_CACHE_EXISTS';
113+
$this->reader = $reader ?: ObjectManager::getInstance()->get(Reader::class);
107114
}
108115

109116
/**
117+
* System configuration is separated by scopes (default, websites, stores). Configuration of a scope is inherited
118+
* from its parent scope (store inherits website).
119+
*
120+
* Because there can be many scopes on single instance of application, the configuration data can be pretty large,
121+
* so it does not make sense to load all of it on every application request. That is why we cache configuration
122+
* data by scope and only load configuration scope when a value from that scope is requested.
123+
*
124+
* Possible path values:
125+
* '' - will return whole system configuration (default scope + all other scopes)
126+
* 'default' - will return all default scope configuration values
127+
* '{scopeType}' - will return data from all scopes of a specified {scopeType} (websites, stores)
128+
* '{scopeType}/{scopeCode}' - will return data for all values of the scope specified by {scopeCode} and scope type
129+
* '{scopeType}/{scopeCode}/some/config/variable' - will return value of the config variable in the specified scope
130+
*
110131
* @inheritdoc
111132
*/
112133
public function get($path = '')
113134
{
114-
if ($path === null) {
115-
$path = '';
135+
if ($path === '') {
136+
$this->data = array_replace_recursive($this->loadAllData(), $this->data);
137+
return $this->data;
116138
}
117-
if ($this->isConfigRead($path)) {
118-
return $this->data->getData($path);
139+
$pathParts = explode('/', $path);
140+
if (count($pathParts) === 1 && $pathParts[0] !== 'default') {
141+
if (!isset($this->data[$pathParts[0]])) {
142+
$data = $this->reader->read();
143+
$this->data = array_replace_recursive($data, $this->data);
144+
}
145+
return $this->data[$pathParts[0]];
119146
}
120-
121-
if (!empty($path) && $this->isCacheExists()) {
122-
return $this->readFromCache($path);
147+
$scopeType = array_shift($pathParts);
148+
if ($scopeType === 'default') {
149+
if (!isset($this->data[$scopeType])) {
150+
$this->data = array_replace_recursive($this->loadDefaultScopeData($scopeType), $this->data);
151+
}
152+
return $this->getDataByPathParts($this->data[$scopeType], $pathParts);
123153
}
124-
125-
$config = $this->loadConfig();
126-
$this->cacheConfig($config);
127-
$this->data = new DataObject($config);
128-
return $this->data->getData($path);
129-
}
130-
131-
/**
132-
* Check whether configuration is cached
133-
*
134-
* In case configuration cache exists method 'load' returns
135-
* value equal to $this->cacheExistenceKey
136-
*
137-
* @return bool
138-
*/
139-
private function isCacheExists()
140-
{
141-
return $this->cache->load($this->cacheExistenceKey) !== false;
154+
$scopeId = array_shift($pathParts);
155+
if (!isset($this->data[$scopeType][$scopeId])) {
156+
$this->data = array_replace_recursive($this->loadScopeData($scopeType, $scopeId), $this->data);
157+
}
158+
return isset($this->data[$scopeType][$scopeId])
159+
? $this->getDataByPathParts($this->data[$scopeType][$scopeId], $pathParts)
160+
: null;
142161
}
143162

144163
/**
145-
* Explode path by '/'(forward slash) separator
146-
*
147-
* In case $path string contains forward slash symbol(/) the $path is exploded and parts array is returned
148-
* In other case empty array is returned
164+
* Load configuration data for all scopes
149165
*
150-
* @param string $path
151166
* @return array
152167
*/
153-
private function getPathParts($path)
168+
private function loadAllData()
154169
{
155-
$pathParts = [];
156-
if (strpos($path, '/') !== false) {
157-
$pathParts = explode('/', $path);
170+
$cachedData = $this->cache->load($this->configType);
171+
if ($cachedData === false) {
172+
$data = $this->reader->read();
173+
} else {
174+
$data = $this->serializer->unserialize($cachedData);
158175
}
159-
return $pathParts;
176+
return $data;
160177
}
161178

162179
/**
163-
* Check whether requested configuration data is read to memory
164-
*
165-
* Because of configuration is cached partially each part can be loaded separately
166-
* Method performs check if corresponding system configuration part is already loaded to memory
167-
* and value can be retrieved directly without cache look up
180+
* Load configuration data for default scope
168181
*
169-
*
170-
* @param string $path
171-
* @return bool
182+
* @param string $scopeType
183+
* @return array
172184
*/
173-
private function isConfigRead($path)
185+
private function loadDefaultScopeData($scopeType)
174186
{
175-
$pathParts = $this->getPathParts($path);
176-
return !empty($pathParts) && isset($this->data[$pathParts[0]][$pathParts[1]]);
187+
$cachedData = $this->cache->load($this->configType . '_' . $scopeType);
188+
if ($cachedData === false) {
189+
$data = $this->reader->read();
190+
$this->cacheData($data);
191+
} else {
192+
$data = [$scopeType => $this->serializer->unserialize($cachedData)];
193+
}
194+
return $data;
177195
}
178196

179197
/**
180-
* Load configuration from all the sources
181-
*
182-
* System configuration is loaded in 3 steps performing consecutive calls to
183-
* Pre Processor, Fallback Processor, Post Processor accordingly
198+
* Load configuration data for a specified scope
184199
*
200+
* @param string $scopeType
201+
* @param string $scopeId
185202
* @return array
186203
*/
187-
private function loadConfig()
204+
private function loadScopeData($scopeType, $scopeId)
188205
{
189-
$data = $this->preProcessor->process($this->source->get());
190-
$this->data = new DataObject($data);
191-
$data = $this->fallback->process($data);
192-
$this->data = new DataObject($data);
193-
194-
return $this->postProcessor->process($data);
206+
$cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
207+
if ($cachedData === false) {
208+
if ($this->availableDataScopes === null) {
209+
$cachedScopeData = $this->cache->load($this->configType . '_scopes');
210+
if ($cachedScopeData !== false) {
211+
$this->availableDataScopes = $this->serializer->unserialize($cachedScopeData);
212+
}
213+
}
214+
if (is_array($this->availableDataScopes) && !isset($this->availableDataScopes[$scopeType][$scopeId])) {
215+
return [$scopeType => [$scopeId => []]];
216+
}
217+
$data = $this->reader->read();
218+
$this->cacheData($data);
219+
} else {
220+
$data = [$scopeType => [$scopeId => $this->serializer->unserialize($cachedData)]];
221+
}
222+
return $data;
195223
}
196224

197225
/**
198-
*
199-
* Load configuration and caching it by parts.
200-
*
201-
* To be cached configuration is loaded first.
202-
* Then it is cached by parts to minimize memory usage on load.
203-
* Additional flag cached as well to give possibility check cache existence without data load.
226+
* Cache configuration data.
227+
* Caches data per scope to avoid reading data for all scopes on every request
204228
*
205229
* @param array $data
206230
* @return void
207231
*/
208-
private function cacheConfig($data)
232+
private function cacheData(array $data)
209233
{
210-
foreach ($data as $scope => $scopeData) {
211-
foreach ($scopeData as $key => $config) {
234+
$this->cache->save(
235+
$this->serializer->serialize($data),
236+
$this->configType,
237+
[self::CACHE_TAG]
238+
);
239+
$this->cache->save(
240+
$this->serializer->serialize($data['default']),
241+
$this->configType . '_default',
242+
[self::CACHE_TAG]
243+
);
244+
$scopes = [];
245+
foreach (['websites', 'stores'] as $curScopeType) {
246+
foreach ($data[$curScopeType] as $curScopeId => $curScopeData) {
247+
$scopes[$curScopeType][$curScopeId] = 1;
212248
$this->cache->save(
213-
$this->serializer->serialize($config),
214-
$this->configType . '_' . $scope . $key,
249+
$this->serializer->serialize($curScopeData),
250+
$this->configType . '_' . $curScopeType . '_' . $curScopeId,
215251
[self::CACHE_TAG]
216252
);
217253
}
218254
}
219-
$this->cache->save($this->cacheExistenceKey, $this->cacheExistenceKey, [self::CACHE_TAG]);
255+
$this->cache->save(
256+
$this->serializer->serialize($scopes),
257+
$this->configType . "_scopes",
258+
[self::CACHE_TAG]
259+
);
220260
}
221261

222262
/**
223-
* Read cached configuration
263+
* Walk nested hash map by keys from $pathParts
224264
*
225-
* Read section of system configuration corresponding to requested $path from cache
226-
* Configuration stored to internal property right after load to prevent additional
227-
* requests to cache storage
228-
*
229-
* @param string $path
265+
* @param array $data to walk in
266+
* @param array $pathParts keys path
230267
* @return mixed
231268
*/
232-
private function readFromCache($path)
269+
private function getDataByPathParts($data, $pathParts)
233270
{
234-
if ($this->data === null) {
235-
$this->data = new DataObject();
236-
}
237-
238-
$result = null;
239-
$pathParts = $this->getPathParts($path);
240-
if (!empty($pathParts)) {
241-
$result = $this->cache->load($this->configType . '_' . $pathParts[0] . $pathParts[1]);
242-
if ($result !== false) {
243-
$readData = $this->data->getData();
244-
$readData[$pathParts[0]][$pathParts[1]] = $this->serializer->unserialize($result);
245-
$this->data->setData($readData);
271+
foreach ($pathParts as $key) {
272+
if ((array)$data === $data && isset($data[$key])) {
273+
$data = $data[$key];
274+
} elseif ($data instanceof \Magento\Framework\DataObject) {
275+
$data = $data->getDataByKey($key);
276+
} else {
277+
return null;
246278
}
247279
}
248-
249-
return $this->data->getData($path);
280+
return $data;
250281
}
251282

252283
/**
@@ -260,7 +291,7 @@ private function readFromCache($path)
260291
*/
261292
public function clean()
262293
{
263-
$this->data = null;
294+
$this->data = [];
264295
$this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
265296
}
266297
}

0 commit comments

Comments
 (0)