Skip to content

Commit 07e7ef1

Browse files
committed
Merge remote-tracking branch 'origin/MAGETWO-69840' into 2.1.8-develop-pr21
2 parents ef39458 + d9904ea commit 07e7ef1

File tree

9 files changed

+527
-138
lines changed

9 files changed

+527
-138
lines changed

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

Lines changed: 170 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -10,222 +10,310 @@
1010
use Magento\Framework\App\Config\Spi\PostProcessorInterface;
1111
use Magento\Framework\App\Config\Spi\PreProcessorInterface;
1212
use Magento\Framework\Cache\FrontendInterface;
13-
use Magento\Framework\DataObject;
13+
use Magento\Framework\App\ObjectManager;
14+
use Magento\Config\App\Config\Type\System\Reader;
1415
use Magento\Store\Model\Config\Processor\Fallback;
1516

1617
/**
17-
* Class process source, cache them and retrieve value by path
18+
* System configuration type.
1819
*/
1920
class System implements ConfigTypeInterface
2021
{
22+
/**
23+
* Cache tag.
24+
*/
2125
const CACHE_TAG = 'config_scopes';
2226

27+
/**
28+
* Config type.
29+
*/
2330
const CONFIG_TYPE = 'system';
2431

2532
/**
33+
* Config source.
34+
*
2635
* @var ConfigSourceInterface
2736
*/
2837
private $source;
2938

3039
/**
31-
* @var DataObject[]
40+
* Object data.
41+
*
42+
* @var array
3243
*/
33-
private $data;
44+
private $data = [];
3445

3546
/**
47+
* Postprocessor.
48+
*
3649
* @var PostProcessorInterface
3750
*/
3851
private $postProcessor;
3952

4053
/**
54+
* Preprocessor.
55+
*
4156
* @var PreProcessorInterface
4257
*/
4358
private $preProcessor;
4459

4560
/**
61+
* Cache.
62+
*
4663
* @var FrontendInterface
4764
*/
4865
private $cache;
4966

5067
/**
68+
* Caching nested level.
69+
*
5170
* @var int
5271
*/
5372
private $cachingNestedLevel;
5473

5574
/**
75+
* Fallback.
76+
*
5677
* @var Fallback
5778
*/
5879
private $fallback;
5980

6081
/**
61-
* Key name for flag which displays whether configuration is cached or not.
62-
*
63-
* Once configuration is cached additional flag pushed to cache storage
64-
* to be able check cache existence without data load.
82+
* The type of config.
6583
*
6684
* @var string
6785
*/
68-
private $cacheExistenceKey = self::CONFIG_TYPE . '_CACHE_EXISTS';
86+
private $configType;
87+
88+
/**
89+
* Reader.
90+
*
91+
* @var Reader
92+
*/
93+
private $reader;
94+
95+
/**
96+
* List of scopes that were retrieved from configuration storage.
97+
*
98+
* Is used to make sure that we don't try to load non-existing configuration scopes.
99+
*
100+
* @var array
101+
*/
102+
private $availableDataScopes = null;
69103

70104
/**
71-
* System constructor.
72105
* @param ConfigSourceInterface $source
73106
* @param PostProcessorInterface $postProcessor
74107
* @param Fallback $fallback
75108
* @param FrontendInterface $cache
76109
* @param PreProcessorInterface $preProcessor
77110
* @param int $cachingNestedLevel
111+
* @param string $configType
112+
* @param Reader $reader
78113
*/
79114
public function __construct(
80115
ConfigSourceInterface $source,
81116
PostProcessorInterface $postProcessor,
82117
Fallback $fallback,
83118
FrontendInterface $cache,
84119
PreProcessorInterface $preProcessor,
85-
$cachingNestedLevel = 1
120+
$cachingNestedLevel = 1,
121+
$configType = self::CONFIG_TYPE,
122+
Reader $reader = null
86123
) {
87124
$this->source = $source;
88125
$this->postProcessor = $postProcessor;
89126
$this->preProcessor = $preProcessor;
90127
$this->cache = $cache;
91128
$this->cachingNestedLevel = $cachingNestedLevel;
92129
$this->fallback = $fallback;
130+
$this->configType = $configType;
131+
$this->reader = $reader ?: ObjectManager::getInstance()->get(Reader::class);
93132
}
94133

95134
/**
135+
* System configuration is separated by scopes (default, websites, stores). Configuration of a scope is inherited
136+
* from its parent scope (store inherits website).
137+
*
138+
* Because there can be many scopes on single instance of application, the configuration data can be pretty large,
139+
* so it does not make sense to load all of it on every application request. That is why we cache configuration
140+
* data by scope and only load configuration scope when a value from that scope is requested.
141+
*
142+
* Possible path values:
143+
* '' - will return whole system configuration (default scope + all other scopes)
144+
* 'default' - will return all default scope configuration values
145+
* '{scopeType}' - will return data from all scopes of a specified {scopeType} (websites, stores)
146+
* '{scopeType}/{scopeCode}' - will return data for all values of the scope specified by {scopeCode} and scope type
147+
* '{scopeType}/{scopeCode}/some/config/variable' - will return value of the config variable in the specified scope
148+
*
96149
* @inheritdoc
97150
*/
98151
public function get($path = '')
99152
{
100-
if ($path === null) {
101-
$path = '';
102-
}
153+
if ($path === '') {
154+
$this->data = array_replace_recursive($this->loadAllData(), $this->data);
103155

104-
if ($this->isConfigRead($path)) {
105-
return $this->data->getData($path);
156+
return $this->data;
106157
}
158+
$pathParts = explode('/', $path);
159+
if (count($pathParts) === 1 && $pathParts[0] !== 'default') {
160+
if (!isset($this->data[$pathParts[0]])) {
161+
$data = $this->reader->read();
162+
$this->data = array_replace_recursive($data, $this->data);
163+
}
107164

108-
if (!empty($path) && $this->isCacheExists()) {
109-
return $this->readFromCache($path);
165+
return $this->data[$pathParts[0]];
110166
}
167+
$scopeType = array_shift($pathParts);
168+
if ($scopeType === 'default') {
169+
if (!isset($this->data[$scopeType])) {
170+
$this->data = array_replace_recursive($this->loadDefaultScopeData($scopeType), $this->data);
171+
}
111172

112-
$config = $this->loadConfig();
113-
$this->cacheConfig($config);
114-
$this->data = new DataObject($config);
115-
return $this->data->getData($path);
116-
}
173+
return $this->getDataByPathParts($this->data[$scopeType], $pathParts);
174+
}
175+
$scopeId = array_shift($pathParts);
176+
if (!isset($this->data[$scopeType][$scopeId])) {
177+
$this->data = array_replace_recursive($this->loadScopeData($scopeType, $scopeId), $this->data);
178+
}
117179

118-
/**
119-
* Check whether configuration is cached
120-
* @return bool
121-
*/
122-
private function isCacheExists()
123-
{
124-
return $this->cache->load($this->cacheExistenceKey) !== false;
180+
return isset($this->data[$scopeType][$scopeId])
181+
? $this->getDataByPathParts($this->data[$scopeType][$scopeId], $pathParts)
182+
: null;
125183
}
126184

127185
/**
128-
* Explode path by '/'(forward slash) separator
186+
* Load configuration data for all scopes.
129187
*
130-
* @param string $path
131188
* @return array
132189
*/
133-
private function getPathParts($path)
190+
private function loadAllData()
134191
{
135-
$pathParts = [];
136-
if (strpos($path, '/') !== false) {
137-
$pathParts = explode('/', $path);
192+
$cachedData = $this->cache->load($this->configType);
193+
if ($cachedData === false) {
194+
$data = $this->reader->read();
195+
} else {
196+
$data = unserialize($cachedData);
138197
}
139-
return $pathParts;
198+
199+
return $data;
140200
}
141201

142202
/**
143-
* Check whether requested configuration data is read to memory
203+
* Load configuration data for default scope.
144204
*
145-
* @param string $path
146-
* @return bool
205+
* @param string $scopeType
206+
* @return array
147207
*/
148-
private function isConfigRead($path)
208+
private function loadDefaultScopeData($scopeType)
149209
{
150-
$pathParts = $this->getPathParts($path);
151-
return !empty($pathParts) && isset($this->data[$pathParts[0]][$pathParts[1]]);
210+
$cachedData = $this->cache->load($this->configType . '_' . $scopeType);
211+
if ($cachedData === false) {
212+
$data = $this->reader->read();
213+
$this->cacheData($data);
214+
} else {
215+
$data = [$scopeType => unserialize($cachedData)];
216+
}
217+
218+
return $data;
152219
}
153220

154221
/**
155-
* Load configuration from all the sources
222+
* Load configuration data for a specified scope.
156223
*
224+
* @param string $scopeType
225+
* @param string $scopeId
157226
* @return array
158227
*/
159-
private function loadConfig()
228+
private function loadScopeData($scopeType, $scopeId)
160229
{
161-
$data = $this->preProcessor->process($this->source->get());
162-
$this->data = new DataObject($data);
163-
$data = $this->fallback->process($data);
164-
$this->data = new DataObject($data);
230+
$cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
231+
if ($cachedData === false) {
232+
if ($this->availableDataScopes === null) {
233+
$cachedScopeData = $this->cache->load($this->configType . '_scopes');
234+
if ($cachedScopeData !== false) {
235+
$this->availableDataScopes = unserialize($cachedScopeData);
236+
}
237+
}
238+
if (is_array($this->availableDataScopes) && !isset($this->availableDataScopes[$scopeType][$scopeId])) {
239+
return [$scopeType => [$scopeId => []]];
240+
}
241+
$data = $this->reader->read();
242+
$this->cacheData($data);
243+
} else {
244+
$data = [$scopeType => [$scopeId => unserialize($cachedData)]];
245+
}
165246

166-
return $this->postProcessor->process($data);
247+
return $data;
167248
}
168249

169250
/**
170-
*
171-
* Load configuration and caching it by parts.
172-
*
173-
* To be cached configuration is loaded first.
174-
* Then it is cached by parts to minimize memory usage on load.
175-
* Additional flag cached as well to give possibility check cache existence without data load.
251+
* Cache configuration data.
252+
* Caches data per scope to avoid reading data for all scopes on every request.
176253
*
177254
* @param array $data
178255
* @return void
179256
*/
180-
private function cacheConfig($data)
257+
private function cacheData(array $data)
181258
{
182-
foreach ($data as $scope => $scopeData) {
183-
foreach ($scopeData as $key => $config) {
259+
$this->cache->save(
260+
serialize($data),
261+
$this->configType,
262+
[self::CACHE_TAG]
263+
);
264+
$this->cache->save(
265+
serialize($data['default']),
266+
$this->configType . '_default',
267+
[self::CACHE_TAG]
268+
);
269+
$scopes = [];
270+
foreach (['websites', 'stores'] as $curScopeType) {
271+
foreach ($data[$curScopeType] as $curScopeId => $curScopeData) {
272+
$scopes[$curScopeType][$curScopeId] = 1;
184273
$this->cache->save(
185-
serialize($config),
186-
self::CONFIG_TYPE . '_' . $scope . $key,
274+
serialize($curScopeData),
275+
$this->configType . '_' . $curScopeType . '_' . $curScopeId,
187276
[self::CACHE_TAG]
188277
);
189278
}
190279
}
191-
$this->cache->save('1', $this->cacheExistenceKey, [self::CACHE_TAG]);
280+
$this->cache->save(
281+
serialize($scopes),
282+
$this->configType . "_scopes",
283+
[self::CACHE_TAG]
284+
);
192285
}
193286

194287
/**
195-
* Read cached configuration
288+
* Walk nested hash map by keys from $pathParts.
196289
*
197-
* @param string $path
290+
* @param array $data to walk in
291+
* @param array $pathParts keys path
198292
* @return mixed
199293
*/
200-
private function readFromCache($path)
294+
private function getDataByPathParts($data, $pathParts)
201295
{
202-
if ($this->data === null) {
203-
$this->data = new DataObject();
204-
}
205-
206-
$result = null;
207-
$pathParts = $this->getPathParts($path);
208-
if (!empty($pathParts)) {
209-
$result = $this->cache->load(self::CONFIG_TYPE . '_' . $pathParts[0] . $pathParts[1]);
210-
}
211-
212-
if ($result !== false && $result !== null) {
213-
$readData = $this->data->getData();
214-
$readData[$pathParts[0]][$pathParts[1]] = unserialize($result);
215-
$this->data = new DataObject($readData);
296+
foreach ($pathParts as $key) {
297+
if ((array)$data === $data && isset($data[$key])) {
298+
$data = $data[$key];
299+
} elseif ($data instanceof \Magento\Framework\DataObject) {
300+
$data = $data->getDataByKey($key);
301+
} else {
302+
return null;
303+
}
216304
}
217305

218-
return $this->data->getData($path);
306+
return $data;
219307
}
220308

221309
/**
222-
* Clean cache and global variables cache
310+
* Clean cache and global variables cache.
223311
*
224312
* @return void
225313
*/
226314
public function clean()
227315
{
228-
$this->data = null;
316+
$this->data = [];
229317
$this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
230318
}
231319
}

0 commit comments

Comments
 (0)