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\Module; |
13
|
|
|
|
14
|
|
|
use Exception; |
15
|
|
|
use InvalidArgumentException; |
16
|
|
|
use Puli\Manager\Api\Context\ProjectContext; |
17
|
|
|
use Puli\Manager\Api\Module\RootModuleFile; |
18
|
|
|
use Puli\Manager\Api\Module\RootModuleFileManager; |
19
|
|
|
use Puli\Manager\Config\AbstractConfigManager; |
20
|
|
|
use ReflectionClass; |
21
|
|
|
use ReflectionException; |
22
|
|
|
use Webmozart\Expression\Expr; |
23
|
|
|
use Webmozart\Expression\Expression; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Manages changes to the root module file. |
27
|
|
|
* |
28
|
|
|
* Use this class to make persistent changes to the puli.json of a project. |
29
|
|
|
* Whenever you call methods in this class, the changes will be written to disk. |
30
|
|
|
* |
31
|
|
|
* @since 1.0 |
32
|
|
|
* |
33
|
|
|
* @author Bernhard Schussek <[email protected]> |
34
|
|
|
*/ |
35
|
|
|
class RootModuleFileManagerImpl extends AbstractConfigManager implements RootModuleFileManager |
36
|
|
|
{ |
37
|
|
|
/** |
38
|
|
|
* @var ProjectContext |
39
|
|
|
*/ |
40
|
|
|
private $context; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var RootModuleFile |
44
|
|
|
*/ |
45
|
|
|
private $rootModuleFile; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var ModuleFileStorage |
49
|
|
|
*/ |
50
|
|
|
private $moduleFileStorage; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Creates a new module file manager. |
54
|
|
|
* |
55
|
|
|
* @param ProjectContext $context The project context |
56
|
|
|
* @param ModuleFileStorage $moduleFileStorage The module file storage. |
57
|
|
|
*/ |
58
|
80 |
|
public function __construct(ProjectContext $context, ModuleFileStorage $moduleFileStorage) |
59
|
|
|
{ |
60
|
80 |
|
$this->context = $context; |
61
|
80 |
|
$this->rootModuleFile = $context->getRootModuleFile(); |
62
|
80 |
|
$this->moduleFileStorage = $moduleFileStorage; |
63
|
80 |
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* {@inheritdoc} |
67
|
|
|
*/ |
68
|
17 |
|
public function getConfig() |
69
|
|
|
{ |
70
|
17 |
|
return $this->rootModuleFile->getConfig(); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* {@inheritdoc} |
75
|
|
|
*/ |
76
|
1 |
|
public function getContext() |
77
|
|
|
{ |
78
|
1 |
|
return $this->context; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* {@inheritdoc} |
83
|
|
|
*/ |
84
|
|
|
public function getModuleFile() |
85
|
|
|
{ |
86
|
|
|
return $this->rootModuleFile; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* {@inheritdoc} |
91
|
|
|
*/ |
92
|
4 |
|
public function getModuleName() |
93
|
|
|
{ |
94
|
4 |
|
return $this->rootModuleFile->getModuleName(); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* {@inheritdoc} |
99
|
|
|
*/ |
100
|
3 |
View Code Duplication |
public function setModuleName($moduleName) |
|
|
|
|
101
|
|
|
{ |
102
|
3 |
|
if ($moduleName === $this->rootModuleFile->getModuleName()) { |
103
|
1 |
|
return; |
104
|
|
|
} |
105
|
|
|
|
106
|
2 |
|
$previousName = $this->rootModuleFile->getModuleName(); |
107
|
|
|
|
108
|
2 |
|
$this->rootModuleFile->setModuleName($moduleName); |
109
|
|
|
|
110
|
|
|
try { |
111
|
2 |
|
$this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile); |
112
|
1 |
|
} catch (Exception $e) { |
113
|
1 |
|
$this->rootModuleFile->setModuleName($previousName); |
114
|
|
|
|
115
|
1 |
|
throw $e; |
116
|
|
|
} |
117
|
1 |
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* {@inheritdoc} |
121
|
|
|
*/ |
122
|
8 |
View Code Duplication |
public function addPluginClass($pluginClass) |
|
|
|
|
123
|
|
|
{ |
124
|
8 |
|
if ($this->rootModuleFile->hasPluginClass($pluginClass)) { |
125
|
|
|
// Already installed locally |
126
|
1 |
|
return; |
127
|
|
|
} |
128
|
|
|
|
129
|
7 |
|
$this->validatePluginClass($pluginClass); |
130
|
|
|
|
131
|
2 |
|
$previousClasses = $this->rootModuleFile->getPluginClasses(); |
132
|
|
|
|
133
|
2 |
|
$this->rootModuleFile->addPluginClass($pluginClass); |
134
|
|
|
|
135
|
|
|
try { |
136
|
2 |
|
$this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile); |
137
|
1 |
|
} catch (Exception $e) { |
138
|
1 |
|
$this->rootModuleFile->setPluginClasses($previousClasses); |
139
|
|
|
|
140
|
1 |
|
throw $e; |
141
|
|
|
} |
142
|
1 |
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* {@inheritdoc} |
146
|
|
|
*/ |
147
|
3 |
|
public function removePluginClass($pluginClass) |
148
|
|
|
{ |
149
|
3 |
|
if (!$this->rootModuleFile->hasPluginClass($pluginClass)) { |
150
|
1 |
|
return; |
151
|
|
|
} |
152
|
|
|
|
153
|
2 |
|
$previousClasses = $this->rootModuleFile->getPluginClasses(); |
154
|
|
|
|
155
|
2 |
|
$this->rootModuleFile->removePluginClass($pluginClass); |
156
|
|
|
|
157
|
|
|
try { |
158
|
2 |
|
$this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile); |
159
|
1 |
|
} catch (Exception $e) { |
160
|
1 |
|
$this->rootModuleFile->setPluginClasses($previousClasses); |
161
|
|
|
|
162
|
1 |
|
throw $e; |
163
|
|
|
} |
164
|
1 |
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* {@inheritdoc} |
168
|
|
|
*/ |
169
|
7 |
View Code Duplication |
public function removePluginClasses(Expression $expr) |
|
|
|
|
170
|
|
|
{ |
171
|
7 |
|
$save = false; |
172
|
7 |
|
$previousClasses = $this->rootModuleFile->getPluginClasses(); |
173
|
|
|
|
174
|
7 |
|
foreach ($previousClasses as $pluginClass) { |
175
|
6 |
|
if ($expr->evaluate($pluginClass)) { |
176
|
5 |
|
$this->rootModuleFile->removePluginClass($pluginClass); |
177
|
6 |
|
$save = true; |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
|
181
|
7 |
|
if (!$save) { |
182
|
2 |
|
return; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
try { |
186
|
5 |
|
$this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile); |
187
|
2 |
|
} catch (Exception $e) { |
188
|
2 |
|
$this->rootModuleFile->setPluginClasses($previousClasses); |
189
|
|
|
|
190
|
2 |
|
throw $e; |
191
|
|
|
} |
192
|
3 |
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* {@inheritdoc} |
196
|
|
|
*/ |
197
|
3 |
|
public function clearPluginClasses() |
198
|
|
|
{ |
199
|
3 |
|
$this->removePluginClasses(Expr::true()); |
200
|
2 |
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* {@inheritdoc} |
204
|
|
|
*/ |
205
|
1 |
|
public function hasPluginClass($pluginClass) |
206
|
|
|
{ |
207
|
1 |
|
return $this->rootModuleFile->hasPluginClass($pluginClass); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* {@inheritdoc} |
212
|
|
|
*/ |
213
|
1 |
View Code Duplication |
public function hasPluginClasses(Expression $expr = null) |
|
|
|
|
214
|
|
|
{ |
215
|
1 |
|
if (!$expr) { |
216
|
1 |
|
return $this->rootModuleFile->hasPluginClasses(); |
217
|
|
|
} |
218
|
|
|
|
219
|
1 |
|
foreach ($this->rootModuleFile->getPluginClasses() as $pluginClass) { |
220
|
1 |
|
if ($expr->evaluate($pluginClass)) { |
221
|
1 |
|
return true; |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
|
225
|
1 |
|
return false; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* {@inheritdoc} |
230
|
|
|
*/ |
231
|
14 |
|
public function getPluginClasses() |
232
|
|
|
{ |
233
|
14 |
|
return $this->rootModuleFile->getPluginClasses(); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* {@inheritdoc} |
238
|
|
|
*/ |
239
|
1 |
|
public function findPluginClasses(Expression $expr) |
240
|
|
|
{ |
241
|
1 |
|
$pluginClasses = array(); |
242
|
|
|
|
243
|
1 |
|
foreach ($this->rootModuleFile->getPluginClasses() as $pluginClass) { |
244
|
1 |
|
if ($expr->evaluate($pluginClass)) { |
245
|
1 |
|
$pluginClasses[] = $pluginClass; |
246
|
|
|
} |
247
|
|
|
} |
248
|
|
|
|
249
|
1 |
|
return $pluginClasses; |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
/** |
253
|
|
|
* {@inheritdoc} |
254
|
|
|
*/ |
255
|
4 |
|
public function setExtraKey($key, $value) |
256
|
|
|
{ |
257
|
4 |
|
$previouslySet = $this->rootModuleFile->hasExtraKey($key); |
258
|
4 |
|
$previousValue = $this->rootModuleFile->getExtraKey($key); |
259
|
|
|
|
260
|
4 |
|
if ($value === $previousValue) { |
261
|
1 |
|
return; |
262
|
|
|
} |
263
|
|
|
|
264
|
3 |
|
$this->rootModuleFile->setExtraKey($key, $value); |
265
|
|
|
|
266
|
|
|
try { |
267
|
3 |
|
$this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile); |
268
|
2 |
|
} catch (Exception $e) { |
269
|
2 |
|
if ($previouslySet) { |
270
|
1 |
|
$this->rootModuleFile->setExtraKey($key, $previousValue); |
271
|
|
|
} else { |
272
|
1 |
|
$this->rootModuleFile->removeExtraKey($key); |
273
|
|
|
} |
274
|
|
|
|
275
|
2 |
|
throw $e; |
276
|
|
|
} |
277
|
1 |
|
} |
278
|
|
|
|
279
|
|
|
/** |
280
|
|
|
* {@inheritdoc} |
281
|
|
|
*/ |
282
|
3 |
|
public function setExtraKeys(array $values) |
283
|
|
|
{ |
284
|
3 |
|
$previousValues = array(); |
285
|
3 |
|
$previouslyUnset = array(); |
286
|
|
|
|
287
|
3 |
|
foreach ($values as $key => $value) { |
288
|
3 |
|
if ($this->rootModuleFile->hasExtraKey($key)) { |
289
|
2 |
|
if ($value !== $previous = $this->rootModuleFile->getExtraKey($key)) { |
290
|
2 |
|
$previousValues[$key] = $previous; |
291
|
|
|
} |
292
|
|
|
} else { |
293
|
3 |
|
$previouslyUnset[$key] = true; |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
|
|
297
|
3 |
|
if (!$previousValues && !$previouslyUnset) { |
|
|
|
|
298
|
1 |
|
return; |
299
|
|
|
} |
300
|
|
|
|
301
|
2 |
|
$this->rootModuleFile->setExtraKeys($values); |
302
|
|
|
|
303
|
|
|
try { |
304
|
2 |
|
$this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile); |
305
|
1 |
|
} catch (Exception $e) { |
306
|
1 |
|
foreach ($values as $key => $value) { |
307
|
1 |
|
if (isset($previouslyUnset[$key])) { |
308
|
1 |
|
$this->rootModuleFile->removeExtraKey($key); |
309
|
|
|
} else { |
310
|
1 |
|
$this->rootModuleFile->setExtraKey($key, $previousValues[$key]); |
311
|
|
|
} |
312
|
|
|
} |
313
|
|
|
|
314
|
1 |
|
throw $e; |
315
|
|
|
} |
316
|
1 |
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* {@inheritdoc} |
320
|
|
|
*/ |
321
|
3 |
View Code Duplication |
public function removeExtraKey($key) |
|
|
|
|
322
|
|
|
{ |
323
|
3 |
|
if (!$this->rootModuleFile->hasExtraKey($key)) { |
324
|
1 |
|
return; |
325
|
|
|
} |
326
|
|
|
|
327
|
2 |
|
$previousValue = $this->rootModuleFile->getExtraKey($key); |
328
|
|
|
|
329
|
2 |
|
$this->rootModuleFile->removeExtraKey($key); |
330
|
|
|
|
331
|
|
|
try { |
332
|
2 |
|
$this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile); |
333
|
1 |
|
} catch (Exception $e) { |
334
|
1 |
|
$this->rootModuleFile->setExtraKey($key, $previousValue); |
335
|
|
|
|
336
|
1 |
|
throw $e; |
337
|
|
|
} |
338
|
1 |
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* {@inheritdoc} |
342
|
|
|
*/ |
343
|
3 |
View Code Duplication |
public function removeExtraKeys(Expression $expr) |
|
|
|
|
344
|
|
|
{ |
345
|
3 |
|
$previousValues = $this->rootModuleFile->getExtraKeys(); |
346
|
3 |
|
$save = false; |
347
|
|
|
|
348
|
3 |
|
foreach ($this->rootModuleFile->getExtraKeys() as $key => $value) { |
349
|
2 |
|
if ($expr->evaluate($key)) { |
350
|
2 |
|
$this->rootModuleFile->removeExtraKey($key); |
351
|
2 |
|
$save = true; |
352
|
|
|
} |
353
|
|
|
} |
354
|
|
|
|
355
|
3 |
|
if (!$save) { |
356
|
1 |
|
return; |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
try { |
360
|
2 |
|
$this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile); |
361
|
1 |
|
} catch (Exception $e) { |
362
|
1 |
|
$this->rootModuleFile->setExtraKeys($previousValues); |
363
|
|
|
|
364
|
1 |
|
throw $e; |
365
|
|
|
} |
366
|
1 |
|
} |
367
|
|
|
|
368
|
|
|
/** |
369
|
|
|
* {@inheritdoc} |
370
|
|
|
*/ |
371
|
3 |
View Code Duplication |
public function clearExtraKeys() |
|
|
|
|
372
|
|
|
{ |
373
|
3 |
|
$previousValues = $this->rootModuleFile->getExtraKeys(); |
374
|
|
|
|
375
|
3 |
|
if (!$previousValues) { |
|
|
|
|
376
|
1 |
|
return; |
377
|
|
|
} |
378
|
|
|
|
379
|
2 |
|
$this->rootModuleFile->clearExtraKeys(); |
380
|
|
|
|
381
|
|
|
try { |
382
|
2 |
|
$this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile); |
383
|
1 |
|
} catch (Exception $e) { |
384
|
1 |
|
$this->rootModuleFile->setExtraKeys($previousValues); |
385
|
|
|
|
386
|
1 |
|
throw $e; |
387
|
|
|
} |
388
|
1 |
|
} |
389
|
|
|
|
390
|
|
|
/** |
391
|
|
|
* {@inheritdoc} |
392
|
|
|
*/ |
393
|
1 |
|
public function hasExtraKey($key) |
394
|
|
|
{ |
395
|
1 |
|
return $this->rootModuleFile->hasExtraKey($key); |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
/** |
399
|
|
|
* {@inheritdoc} |
400
|
|
|
*/ |
401
|
1 |
View Code Duplication |
public function hasExtraKeys(Expression $expr = null) |
|
|
|
|
402
|
|
|
{ |
403
|
1 |
|
if (!$expr) { |
404
|
1 |
|
return $this->rootModuleFile->hasExtraKeys(); |
405
|
|
|
} |
406
|
|
|
|
407
|
1 |
|
foreach ($this->rootModuleFile->getExtraKeys() as $key => $value) { |
408
|
1 |
|
if ($expr->evaluate($key)) { |
409
|
1 |
|
return true; |
410
|
|
|
} |
411
|
|
|
} |
412
|
|
|
|
413
|
1 |
|
return false; |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* {@inheritdoc} |
418
|
|
|
*/ |
419
|
15 |
|
public function getExtraKey($key, $default = null) |
420
|
|
|
{ |
421
|
15 |
|
return $this->rootModuleFile->getExtraKey($key, $default); |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
/** |
425
|
|
|
* {@inheritdoc} |
426
|
|
|
*/ |
427
|
1 |
|
public function getExtraKeys() |
428
|
|
|
{ |
429
|
1 |
|
return $this->rootModuleFile->getExtraKeys(); |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
/** |
433
|
|
|
* {@inheritdoc} |
434
|
|
|
*/ |
435
|
1 |
|
public function findExtraKeys(Expression $expr) |
436
|
|
|
{ |
437
|
1 |
|
$values = array(); |
438
|
|
|
|
439
|
1 |
|
foreach ($this->rootModuleFile->getExtraKeys() as $key => $value) { |
440
|
1 |
|
if ($expr->evaluate($key)) { |
441
|
1 |
|
$values[$key] = $value; |
442
|
|
|
} |
443
|
|
|
} |
444
|
|
|
|
445
|
1 |
|
return $values; |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
/** |
449
|
|
|
* {@inheritdoc} |
450
|
|
|
*/ |
451
|
2 |
View Code Duplication |
public function migrate($targetVersion) |
|
|
|
|
452
|
|
|
{ |
453
|
2 |
|
$previousVersion = $this->rootModuleFile->getVersion(); |
454
|
|
|
|
455
|
2 |
|
if ($previousVersion === $targetVersion) { |
456
|
|
|
return; |
457
|
|
|
} |
458
|
|
|
|
459
|
2 |
|
$this->rootModuleFile->setVersion($targetVersion); |
460
|
|
|
|
461
|
|
|
try { |
462
|
2 |
|
$this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile); |
463
|
1 |
|
} catch (Exception $e) { |
464
|
1 |
|
$this->rootModuleFile->setVersion($previousVersion); |
465
|
|
|
|
466
|
1 |
|
throw $e; |
467
|
|
|
} |
468
|
1 |
|
} |
469
|
|
|
|
470
|
|
|
/** |
471
|
|
|
* {@inheritdoc} |
472
|
|
|
*/ |
473
|
4 |
|
protected function saveConfigFile() |
474
|
|
|
{ |
475
|
4 |
|
$this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile); |
476
|
4 |
|
} |
477
|
|
|
|
478
|
7 |
|
private function validatePluginClass($pluginClass) |
479
|
|
|
{ |
480
|
|
|
try { |
481
|
7 |
|
$reflClass = new ReflectionClass($pluginClass); |
482
|
1 |
|
} catch (ReflectionException $e) { |
483
|
1 |
|
throw new InvalidArgumentException(sprintf( |
484
|
1 |
|
'The plugin class %s does not exist.', |
485
|
|
|
$pluginClass |
486
|
1 |
|
), 0, $e); |
487
|
|
|
} |
488
|
|
|
|
489
|
6 |
|
if ($reflClass->isInterface()) { |
490
|
1 |
|
throw new InvalidArgumentException(sprintf( |
491
|
1 |
|
'The plugin class %s should be a class, but is an interface.', |
492
|
|
|
$pluginClass |
493
|
|
|
)); |
494
|
|
|
} |
495
|
|
|
|
496
|
5 |
|
if (version_compare(PHP_VERSION, '5.4.0', '>=') && $reflClass->isTrait()) { |
497
|
1 |
|
throw new InvalidArgumentException(sprintf( |
498
|
1 |
|
'The plugin class %s should be a class, but is a trait.', |
499
|
|
|
$pluginClass |
500
|
|
|
)); |
501
|
|
|
} |
502
|
|
|
|
503
|
4 |
|
if (!$reflClass->implementsInterface('\Puli\Manager\Api\PuliPlugin')) { |
504
|
1 |
|
throw new InvalidArgumentException(sprintf( |
505
|
1 |
|
'The plugin class %s must implement PuliPlugin.', |
506
|
|
|
$pluginClass |
507
|
|
|
)); |
508
|
|
|
} |
509
|
|
|
|
510
|
3 |
|
$constructor = $reflClass->getConstructor(); |
511
|
|
|
|
512
|
3 |
|
if (null !== $constructor && $constructor->getNumberOfRequiredParameters() > 0) { |
513
|
1 |
|
throw new InvalidArgumentException(sprintf( |
514
|
|
|
'The constructor of the plugin class %s must not have required '. |
515
|
1 |
|
'parameters.', |
516
|
|
|
$pluginClass |
517
|
|
|
)); |
518
|
|
|
} |
519
|
2 |
|
} |
520
|
|
|
} |
521
|
|
|
|
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.