1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SilverStripe\Core\Manifest; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use Serializable; |
7
|
|
|
use SilverStripe\Core\Injector\Injector; |
8
|
|
|
|
9
|
|
|
class Module implements Serializable |
10
|
|
|
{ |
11
|
|
|
/** |
12
|
|
|
* Directory |
13
|
|
|
* |
14
|
|
|
* @var string |
15
|
|
|
*/ |
16
|
|
|
protected $path = null; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Base folder of application |
20
|
|
|
* |
21
|
|
|
* @var string |
22
|
|
|
*/ |
23
|
|
|
protected $basePath = null; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Cache of composer data |
27
|
|
|
* |
28
|
|
|
* @var array |
29
|
|
|
*/ |
30
|
|
|
protected $composerData = null; |
31
|
|
|
|
32
|
|
|
public function __construct($path, $base) |
33
|
|
|
{ |
34
|
|
|
$this->path = rtrim($path, '/\\'); |
35
|
|
|
$this->basePath = rtrim($base, '/\\'); |
36
|
|
|
$this->loadComposer(); |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Gets name of this module. Used as unique key and identifier for this module. |
41
|
|
|
* |
42
|
|
|
* If installed by composer, this will be the full composer name (vendor/name). |
43
|
|
|
* If not insalled by composer this will default to the basedir() |
44
|
|
|
* |
45
|
|
|
* @return string |
46
|
|
|
*/ |
47
|
|
|
public function getName() |
48
|
|
|
{ |
49
|
|
|
return $this->getComposerName() ?: $this->getShortName(); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Get full composer name. Will be null if no composer.json is available |
54
|
|
|
* |
55
|
|
|
* @return string|null |
56
|
|
|
*/ |
57
|
|
|
public function getComposerName() |
58
|
|
|
{ |
59
|
|
|
if (isset($this->composerData['name'])) { |
60
|
|
|
return $this->composerData['name']; |
61
|
|
|
} |
62
|
|
|
return null; |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Gets "short" name of this module. This is the base directory this module |
67
|
|
|
* is installed in. |
68
|
|
|
* |
69
|
|
|
* If installed in root, this will be generated from the composer name instead |
70
|
|
|
* |
71
|
|
|
* @return string |
72
|
|
|
*/ |
73
|
|
|
public function getShortName() |
74
|
|
|
{ |
75
|
|
|
// If installed in the root directory we need to infer from composer |
76
|
|
|
if ($this->path === $this->basePath && $this->composerData) { |
|
|
|
|
77
|
|
|
// Sometimes we customise installer name |
78
|
|
|
if (isset($this->composerData['extra']['installer-name'])) { |
79
|
|
|
return $this->composerData['extra']['installer-name']; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
// Strip from full composer name |
83
|
|
|
$composerName = $this->getComposerName(); |
84
|
|
|
if ($composerName) { |
|
|
|
|
85
|
|
|
list(, $name) = explode('/', $composerName); |
86
|
|
|
return $name; |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
// Base name of directory |
91
|
|
|
return basename($this->path); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Get base path for this module |
96
|
|
|
* |
97
|
|
|
* @return string |
98
|
|
|
*/ |
99
|
|
|
public function getPath() |
100
|
|
|
{ |
101
|
|
|
return $this->path; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Get path relative to base dir. |
106
|
|
|
* If module path is base this will be empty string |
107
|
|
|
* |
108
|
|
|
* @return string |
109
|
|
|
*/ |
110
|
|
|
public function getRelativePath() |
111
|
|
|
{ |
112
|
|
|
return ltrim(substr($this->path, strlen($this->basePath)), '/\\'); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
public function serialize() |
116
|
|
|
{ |
117
|
|
|
return json_encode([$this->path, $this->basePath, $this->composerData]); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
public function unserialize($serialized) |
121
|
|
|
{ |
122
|
|
|
list($this->path, $this->basePath, $this->composerData) = json_decode($serialized, true); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Activate _config.php for this module, if one exists |
127
|
|
|
*/ |
128
|
|
|
public function activate() |
129
|
|
|
{ |
130
|
|
|
$config = "{$this->path}/_config.php"; |
131
|
|
|
if (file_exists($config)) { |
132
|
|
|
require_once $config; |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @throws Exception |
138
|
|
|
*/ |
139
|
|
|
protected function loadComposer() |
140
|
|
|
{ |
141
|
|
|
// Load composer data |
142
|
|
|
$path = "{$this->path}/composer.json"; |
143
|
|
|
if (file_exists($path)) { |
144
|
|
|
$content = file_get_contents($path); |
145
|
|
|
$result = json_decode($content, true); |
146
|
|
|
if (json_last_error()) { |
147
|
|
|
$errorMessage = json_last_error_msg(); |
148
|
|
|
throw new Exception("$path: $errorMessage"); |
149
|
|
|
} |
150
|
|
|
$this->composerData = $result; |
|
|
|
|
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Gets path to physical file resource relative to base directory. |
156
|
|
|
* Directories included |
157
|
|
|
* |
158
|
|
|
* This method makes no distinction between public / local resources, |
159
|
|
|
* which may change in the near future. |
160
|
|
|
* |
161
|
|
|
* @internal Experimental API and may change |
162
|
|
|
* @param string $path File or directory path relative to module directory |
163
|
|
|
* @return string Path relative to base directory |
164
|
|
|
*/ |
165
|
|
|
public function getRelativeResourcePath($path) |
166
|
|
|
{ |
167
|
|
|
$base = trim($this->getRelativePath(), '/\\'); |
168
|
|
|
$path = trim($path, '/\\'); |
169
|
|
|
return trim("{$base}/{$path}", '/\\'); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Gets path to physical file resource relative to base directory. |
174
|
|
|
* Directories included |
175
|
|
|
* |
176
|
|
|
* This method makes no distinction between public / local resources, |
177
|
|
|
* which may change in the near future. |
178
|
|
|
* |
179
|
|
|
* @internal Experimental API and may change |
180
|
|
|
* @param string $path File or directory path relative to module directory |
181
|
|
|
* @return string Path relative to base directory |
182
|
|
|
*/ |
183
|
|
|
public function getResourcePath($path) |
184
|
|
|
{ |
185
|
|
|
$base = $this->basePath . $this->getRelativeResourcePath($path); |
|
|
|
|
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Gets the URL for a given resource. |
190
|
|
|
* Relies on the ModuleURLGenerator Injector service to do the heavy lifting |
191
|
|
|
* |
192
|
|
|
* @internal Experimental API and may change |
193
|
|
|
* @param string $path File or directory path relative to module directory |
194
|
|
|
* @return string URL, either domain-relative (starting with /) or absolute |
195
|
|
|
*/ |
196
|
|
|
public function getResourceURL($path) |
197
|
|
|
{ |
198
|
|
|
return Injector::inst() |
199
|
|
|
->get(ResourceURLGenerator::class) |
200
|
|
|
->urlForResource($this->getRelativeResourcePath($path)); |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* Check if this module has a given resource |
205
|
|
|
* |
206
|
|
|
* @internal Experimental API and may change |
207
|
|
|
* @param string $path |
208
|
|
|
* @return bool |
209
|
|
|
*/ |
210
|
|
|
public function hasResource($path) |
211
|
|
|
{ |
212
|
|
|
return file_exists($this->getResourcePath($path)); |
213
|
|
|
} |
214
|
|
|
} |
215
|
|
|
|
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.