1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the puli/manager package. |
5
|
|
|
* |
6
|
|
|
* (c) Bernhard Schussek <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Puli\Manager\Json; |
13
|
|
|
|
14
|
|
|
use Puli\Manager\Api\Config\Config; |
15
|
|
|
use Puli\Manager\Api\Config\ConfigFile; |
16
|
|
|
use Puli\Manager\Api\Factory\FactoryManager; |
17
|
|
|
use Puli\Manager\Api\FileNotFoundException; |
18
|
|
|
use Puli\Manager\Api\InvalidConfigException; |
19
|
|
|
use Puli\Manager\Api\Module\ModuleFile; |
20
|
|
|
use Puli\Manager\Api\Module\RootModuleFile; |
21
|
|
|
use Puli\Manager\Api\Storage\ReadException; |
22
|
|
|
use Puli\Manager\Api\Storage\Storage; |
23
|
|
|
use Puli\Manager\Api\Storage\WriteException; |
24
|
|
|
use stdClass; |
25
|
|
|
use Webmozart\Json\Conversion\ConversionFailedException; |
26
|
|
|
use Webmozart\Json\DecodingFailedException; |
27
|
|
|
use Webmozart\Json\EncodingFailedException; |
28
|
|
|
use Webmozart\Json\JsonDecoder; |
29
|
|
|
use Webmozart\Json\JsonEncoder; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Loads and saves JSON files. |
33
|
|
|
* |
34
|
|
|
* @since 1.0 |
35
|
|
|
* |
36
|
|
|
* @author Bernhard Schussek <[email protected]> |
37
|
|
|
*/ |
38
|
|
|
class JsonStorage |
39
|
|
|
{ |
40
|
|
|
/** |
41
|
|
|
* @var Storage |
42
|
|
|
*/ |
43
|
|
|
private $storage; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @var JsonConverterProvider |
47
|
|
|
*/ |
48
|
|
|
private $converterProvider; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @var JsonEncoder |
52
|
|
|
*/ |
53
|
|
|
private $jsonEncoder; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @var JsonDecoder |
57
|
|
|
*/ |
58
|
|
|
private $jsonDecoder; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @var FactoryManager |
62
|
|
|
*/ |
63
|
|
|
private $factoryManager; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Creates a new storage. |
67
|
|
|
* |
68
|
|
|
* @param Storage $storage The file storage. |
69
|
|
|
* @param JsonConverterProvider $converterProvider The provider for the JSON |
70
|
|
|
* converters. |
71
|
|
|
* @param JsonEncoder $jsonEncoder The JSON encoder. |
72
|
|
|
* @param JsonDecoder $jsonDecoder The JSON decoder. |
73
|
|
|
* @param FactoryManager|null $factoryManager The manager used to |
74
|
|
|
* regenerate the factory |
75
|
|
|
* class after saving a file. |
76
|
|
|
*/ |
77
|
68 |
|
public function __construct( |
78
|
|
|
Storage $storage, |
79
|
|
|
JsonConverterProvider $converterProvider, |
80
|
|
|
JsonEncoder $jsonEncoder, |
81
|
|
|
JsonDecoder $jsonDecoder, |
82
|
|
|
FactoryManager $factoryManager = null |
83
|
|
|
) { |
84
|
68 |
|
$this->storage = $storage; |
85
|
68 |
|
$this->converterProvider = $converterProvider; |
86
|
68 |
|
$this->jsonEncoder = $jsonEncoder; |
87
|
68 |
|
$this->jsonDecoder = $jsonDecoder; |
88
|
68 |
|
$this->factoryManager = $factoryManager; |
89
|
68 |
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Loads a configuration file from a path. |
93
|
|
|
* |
94
|
|
|
* @param string $path The path to the configuration file. |
95
|
|
|
* @param Config|null $baseConfig The configuration that the loaded |
96
|
|
|
* configuration will inherit its values |
97
|
|
|
* from. |
98
|
|
|
* |
99
|
|
|
* @return ConfigFile The loaded configuration file. |
100
|
|
|
* |
101
|
|
|
* @throws FileNotFoundException If the file does not exist. |
102
|
|
|
* @throws ReadException If the file cannot be read. |
103
|
|
|
* @throws InvalidConfigException If the file contains invalid configuration. |
104
|
|
|
*/ |
105
|
50 |
|
public function loadConfigFile($path, Config $baseConfig = null) |
106
|
|
|
{ |
107
|
50 |
|
return $this->loadFile($path, 'Puli\Manager\Api\Config\ConfigFile', array( |
108
|
50 |
|
'baseConfig' => $baseConfig, |
109
|
|
|
)); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Saves a configuration file. |
114
|
|
|
* |
115
|
|
|
* The configuration file is saved to the same path that it was read from. |
116
|
|
|
* |
117
|
|
|
* @param ConfigFile $configFile The configuration file to save. |
118
|
|
|
* |
119
|
|
|
* @throws WriteException If the file cannot be written. |
120
|
|
|
* @throws InvalidConfigException If the file contains invalid configuration. |
121
|
|
|
*/ |
122
|
4 |
|
public function saveConfigFile(ConfigFile $configFile) |
123
|
|
|
{ |
124
|
4 |
|
$this->saveFile($configFile, $configFile->getPath()); |
125
|
|
|
|
126
|
2 |
|
if ($this->factoryManager) { |
127
|
1 |
|
$this->factoryManager->autoGenerateFactoryClass(); |
128
|
|
|
} |
129
|
2 |
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Loads a module file from a file path. |
133
|
|
|
* |
134
|
|
|
* @param string $path The path to the module file. |
135
|
|
|
* |
136
|
|
|
* @return ModuleFile The loaded module file. |
137
|
|
|
* |
138
|
|
|
* @throws FileNotFoundException If the file does not exist. |
139
|
|
|
* @throws ReadException If the file cannot be read. |
140
|
|
|
* @throws InvalidConfigException If the file contains invalid configuration. |
141
|
|
|
*/ |
142
|
17 |
|
public function loadModuleFile($path) |
143
|
|
|
{ |
144
|
17 |
|
return $this->loadFile($path, 'Puli\Manager\Api\Module\ModuleFile'); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Saves a module file. |
149
|
|
|
* |
150
|
|
|
* The module file is saved to the same path that it was read from. |
151
|
|
|
* |
152
|
|
|
* @param ModuleFile $moduleFile The module file to save. |
153
|
|
|
* |
154
|
|
|
* @throws WriteException If the file cannot be written. |
155
|
|
|
* @throws InvalidConfigException If the file contains invalid configuration. |
156
|
|
|
*/ |
157
|
3 |
|
public function saveModuleFile(ModuleFile $moduleFile) |
158
|
|
|
{ |
159
|
3 |
|
$this->saveFile($moduleFile, $moduleFile->getPath(), array( |
160
|
3 |
|
'targetVersion' => $moduleFile->getVersion(), |
161
|
|
|
)); |
162
|
1 |
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Loads a root module file from a file path. |
166
|
|
|
* |
167
|
|
|
* @param string $path The path to the module configuration file. |
168
|
|
|
* @param Config $baseConfig The configuration that the module will inherit |
169
|
|
|
* its configuration values from. |
170
|
|
|
* |
171
|
|
|
* @return RootModuleFile The loaded module file. |
172
|
|
|
* |
173
|
|
|
* @throws FileNotFoundException If the file does not exist. |
174
|
|
|
* @throws ReadException If the file cannot be read. |
175
|
|
|
* @throws InvalidConfigException If the file contains invalid configuration. |
176
|
|
|
*/ |
177
|
30 |
|
public function loadRootModuleFile($path, Config $baseConfig) |
178
|
|
|
{ |
179
|
30 |
|
return $this->loadFile($path, 'Puli\Manager\Api\Module\RootModuleFile', array( |
180
|
30 |
|
'baseConfig' => $baseConfig, |
181
|
|
|
)); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Saves a root module file. |
186
|
|
|
* |
187
|
|
|
* The module file is saved to the same path that it was read from. |
188
|
|
|
* |
189
|
|
|
* @param RootModuleFile $moduleFile The module file to save. |
190
|
|
|
* |
191
|
|
|
* @throws WriteException If the file cannot be written. |
192
|
|
|
* @throws InvalidConfigException If the file contains invalid configuration. |
193
|
|
|
*/ |
194
|
4 |
|
public function saveRootModuleFile(RootModuleFile $moduleFile) |
195
|
|
|
{ |
196
|
4 |
|
$this->saveFile($moduleFile, $moduleFile->getPath(), array( |
197
|
4 |
|
'targetVersion' => $moduleFile->getVersion(), |
198
|
|
|
)); |
199
|
|
|
|
200
|
2 |
|
if ($this->factoryManager) { |
201
|
1 |
|
$this->factoryManager->autoGenerateFactoryClass(); |
202
|
|
|
} |
203
|
2 |
|
} |
204
|
|
|
|
205
|
57 |
View Code Duplication |
private function loadFile($path, $className, array $options = array()) |
|
|
|
|
206
|
|
|
{ |
207
|
57 |
|
$json = $this->storage->read($path); |
208
|
56 |
|
$jsonData = $this->decode($json, $path); |
209
|
53 |
|
$options['path'] = $path; |
210
|
|
|
|
211
|
|
|
try { |
212
|
53 |
|
return $this->converterProvider->getJsonConverter($className)->fromJson($jsonData, $options); |
213
|
3 |
|
} catch (ConversionFailedException $e) { |
214
|
3 |
|
throw new InvalidConfigException(sprintf( |
215
|
3 |
|
'The JSON in %s could not be converted: %s', |
216
|
|
|
$path, |
217
|
3 |
|
$e->getMessage() |
218
|
3 |
|
), 0, $e); |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|
222
|
11 |
View Code Duplication |
private function saveFile($file, $path, array $options = array()) |
|
|
|
|
223
|
|
|
{ |
224
|
11 |
|
$className = get_class($file); |
225
|
|
|
|
226
|
|
|
try { |
227
|
11 |
|
$jsonData = $this->converterProvider->getJsonConverter($className)->toJson($file, $options); |
228
|
3 |
|
} catch (ConversionFailedException $e) { |
229
|
3 |
|
throw new InvalidConfigException(sprintf( |
230
|
3 |
|
'The data written to %s could not be converted: %s', |
231
|
|
|
$path, |
232
|
3 |
|
$e->getMessage() |
233
|
3 |
|
), 0, $e); |
234
|
|
|
} |
235
|
|
|
|
236
|
8 |
|
$json = $this->encode($jsonData, $path); |
237
|
|
|
|
238
|
5 |
|
$this->storage->write($path, $json); |
239
|
5 |
|
} |
240
|
|
|
|
241
|
8 |
|
private function encode(stdClass $jsonData, $path) |
242
|
|
|
{ |
243
|
|
|
try { |
244
|
8 |
|
return $this->jsonEncoder->encode($jsonData); |
245
|
3 |
|
} catch (EncodingFailedException $e) { |
246
|
3 |
|
throw new InvalidConfigException(sprintf( |
247
|
3 |
|
'The configuration in %s could not be encoded: %s', |
248
|
|
|
$path, |
249
|
3 |
|
$e->getMessage() |
250
|
3 |
|
), 0, $e); |
251
|
|
|
} |
252
|
|
|
} |
253
|
|
|
|
254
|
56 |
|
private function decode($json, $path) |
255
|
|
|
{ |
256
|
|
|
try { |
257
|
56 |
|
return $this->jsonDecoder->decode($json); |
258
|
3 |
|
} catch (DecodingFailedException $e) { |
259
|
3 |
|
throw new InvalidConfigException(sprintf( |
260
|
3 |
|
'The configuration in %s could not be decoded: %s', |
261
|
|
|
$path, |
262
|
3 |
|
$e->getMessage() |
263
|
3 |
|
), 0, $e); |
264
|
|
|
} |
265
|
|
|
} |
266
|
|
|
} |
267
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.