1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SilverStripe\Core\Manifest; |
4
|
|
|
|
5
|
|
|
use SilverStripe\Core\Config\Config; |
6
|
|
|
use Psr\SimpleCache\CacheInterface; |
7
|
|
|
use SilverStripe\Core\Convert; |
8
|
|
|
use SilverStripe\Core\Injector\Injector; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* The version provider will look up configured modules and examine the composer.lock file |
12
|
|
|
* to find the current version installed for each. This is used for the logo title in the CMS |
13
|
|
|
* via {@link LeftAndMain::CMSVersion()} |
14
|
|
|
* |
15
|
|
|
* Example configuration: |
16
|
|
|
* |
17
|
|
|
* <code> |
18
|
|
|
* SilverStripe\Core\Manifest\VersionProvider: |
19
|
|
|
* modules: |
20
|
|
|
* # package/name: Package Title |
21
|
|
|
* silverstripe/framework: Framework |
22
|
|
|
* silverstripe/cms: CMS |
23
|
|
|
* </code> |
24
|
|
|
*/ |
25
|
|
|
class VersionProvider |
26
|
|
|
{ |
27
|
|
|
/** |
28
|
|
|
* Gets a comma delimited string of package titles and versions |
29
|
|
|
* |
30
|
|
|
* @return string |
31
|
|
|
*/ |
32
|
|
|
public function getVersion() |
33
|
|
|
{ |
34
|
|
|
$modules = $this->getModules(); |
35
|
|
|
$lockModules = $this->getModuleVersionFromComposer(array_keys($modules)); |
36
|
|
|
$output = []; |
37
|
|
|
foreach ($modules as $module => $title) { |
38
|
|
|
$version = isset($lockModules[$module]) |
39
|
|
|
? $lockModules[$module] |
40
|
|
|
: _t(__CLASS__ . '.VERSIONUNKNOWN', 'Unknown'); |
41
|
|
|
$output[] = $title . ': ' . $version; |
42
|
|
|
} |
43
|
|
|
return implode(', ', $output); |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Gets the configured core modules to use for the SilverStripe application version. Filtering |
48
|
|
|
* is used to ensure that modules can turn the result off for other modules, e.g. CMS can disable Framework. |
49
|
|
|
* |
50
|
|
|
* @return array |
51
|
|
|
*/ |
52
|
|
|
public function getModules() |
53
|
|
|
{ |
54
|
|
|
$modules = Config::inst()->get(self::class, 'modules'); |
55
|
|
|
return $modules ? array_filter($modules) : []; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Tries to obtain version number from composer.lock if it exists |
60
|
|
|
* |
61
|
|
|
* @param array $modules |
62
|
|
|
* @return array |
63
|
|
|
*/ |
64
|
|
|
public function getModuleVersionFromComposer($modules = []) |
65
|
|
|
{ |
66
|
|
|
$versions = []; |
67
|
|
|
$lockData = $this->getComposerLock(); |
68
|
|
|
if ($lockData && !empty($lockData['packages'])) { |
|
|
|
|
69
|
|
|
foreach ($lockData['packages'] as $package) { |
70
|
|
|
if (in_array($package['name'], $modules) && isset($package['version'])) { |
71
|
|
|
$versions[$package['name']] = $package['version']; |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
} |
75
|
|
|
return $versions; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Load composer.lock's contents and return it |
80
|
|
|
* |
81
|
|
|
* @param bool $cache |
82
|
|
|
* @return array |
83
|
|
|
*/ |
84
|
|
|
protected function getComposerLock($cache = true) |
85
|
|
|
{ |
86
|
|
|
$composerLockPath = BASE_PATH . '/composer.lock'; |
87
|
|
|
if (!file_exists($composerLockPath)) { |
88
|
|
|
return []; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
$jsonData = file_get_contents($composerLockPath); |
92
|
|
|
|
93
|
|
|
// TODO: The benefit of this cache isn't clear |
94
|
|
|
if ($cache) { |
95
|
|
|
$cache = Injector::inst()->get(CacheInterface::class . '.VersionProvider_composerlock'); |
96
|
|
|
$cacheKey = md5($jsonData); |
97
|
|
|
|
98
|
|
|
if ($versions = $cache->get($cacheKey)) { |
99
|
|
|
$lockData = json_decode($versions, true); |
|
|
|
|
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
$lockData = json_decode($jsonData, true); |
103
|
|
|
|
104
|
|
|
$cache->set($cacheKey, $jsonData); |
105
|
|
|
} elseif ($jsonData) { |
106
|
|
|
$lockData = json_decode($jsonData, true); |
107
|
|
|
} else { |
108
|
|
|
$lockData = []; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
return $lockData; |
112
|
|
|
} |
113
|
|
|
} |
114
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.