1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Humbug |
4
|
|
|
* |
5
|
|
|
* @category Humbug |
6
|
|
|
* @package Humbug |
7
|
|
|
* @copyright Copyright (c) 2015 Pádraic Brady (http://blog.astrumfutura.com) |
8
|
|
|
* @license https://github.com/padraic/phar-updater/blob/master/LICENSE New BSD License |
9
|
|
|
* |
10
|
|
|
* This class is partially patterned after Composer's self-update. |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
namespace Humbug\SelfUpdate\Strategy; |
14
|
|
|
|
15
|
|
|
use Humbug\SelfUpdate\Updater; |
16
|
|
|
use Humbug\SelfUpdate\VersionParser; |
17
|
|
|
use Humbug\SelfUpdate\Exception\HttpRequestException; |
18
|
|
|
use Humbug\SelfUpdate\Exception\InvalidArgumentException; |
19
|
|
|
use Humbug\SelfUpdate\Exception\JsonParsingException; |
20
|
|
|
|
21
|
|
|
class GithubStrategy implements StrategyInterface |
22
|
|
|
{ |
23
|
|
|
const API_URL = 'https://packagist.org/packages/%s.json'; |
24
|
|
|
|
25
|
|
|
const STABLE = 'stable'; |
26
|
|
|
|
27
|
|
|
const UNSTABLE = 'unstable'; |
28
|
|
|
|
29
|
|
|
const ANY = 'any'; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var string |
33
|
|
|
*/ |
34
|
|
|
private $localVersion; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var string |
38
|
|
|
*/ |
39
|
|
|
private $remoteVersion; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var string |
43
|
|
|
*/ |
44
|
|
|
private $remoteUrl; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var string |
48
|
|
|
*/ |
49
|
|
|
private $pharName; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var string |
53
|
|
|
*/ |
54
|
|
|
private $packageName; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var string |
58
|
|
|
*/ |
59
|
|
|
private $stability = self::STABLE; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var boolean |
63
|
|
|
*/ |
64
|
|
|
private $allowMajor = false; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Download the remote Phar file. |
68
|
|
|
* |
69
|
|
|
* @param Updater $updater |
70
|
|
|
* @return void |
71
|
|
|
*/ |
72
|
|
View Code Duplication |
public function download(Updater $updater) |
|
|
|
|
73
|
|
|
{ |
74
|
|
|
/** Switch remote request errors to HttpRequestExceptions */ |
75
|
|
|
set_error_handler(array($updater, 'throwHttpRequestException')); |
76
|
|
|
$result = file_get_contents($this->remoteUrl); |
77
|
|
|
restore_error_handler(); |
78
|
|
|
if (false === $result) { |
79
|
|
|
throw new HttpRequestException(sprintf( |
80
|
|
|
'Request to URL failed: %s', $this->remoteUrl |
81
|
|
|
)); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
file_put_contents($updater->getTempPharFile(), $result); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Retrieve the current version available remotely. |
89
|
|
|
* |
90
|
|
|
* @param Updater $updater |
91
|
|
|
* @return string|bool |
92
|
|
|
*/ |
93
|
|
|
public function getCurrentRemoteVersion(Updater $updater) |
94
|
|
|
{ |
95
|
|
|
/** Switch remote request errors to HttpRequestExceptions */ |
96
|
|
|
set_error_handler(array($updater, 'throwHttpRequestException')); |
97
|
|
|
$packageUrl = $this->getApiUrl(); |
98
|
|
|
$package = json_decode(file_get_contents($packageUrl), true); |
99
|
|
|
restore_error_handler(); |
100
|
|
|
|
101
|
|
|
if (null === $package || json_last_error() !== JSON_ERROR_NONE) { |
102
|
|
|
throw new JsonParsingException( |
103
|
|
|
'Error parsing JSON package data' |
104
|
|
|
. (function_exists('json_last_error_msg') ? ': ' . json_last_error_msg() : '') |
105
|
|
|
); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
$versions = array_keys($package['package']['versions']); |
109
|
|
|
|
110
|
|
|
if (false === $this->allowMajor) { |
111
|
|
|
$versions = $this->filterByLocalMajorVersion($versions); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
$versionParser = new VersionParser($versions); |
115
|
|
|
if ($this->getStability() === self::STABLE) { |
116
|
|
|
$this->remoteVersion = $versionParser->getMostRecentStable(); |
117
|
|
|
} elseif ($this->getStability() === self::UNSTABLE) { |
118
|
|
|
$this->remoteVersion = $versionParser->getMostRecentUnstable(); |
119
|
|
|
} else { |
120
|
|
|
$this->remoteVersion = $versionParser->getMostRecentAll(); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Setup remote URL if there's an actual version to download |
125
|
|
|
*/ |
126
|
|
|
if (!empty($this->remoteVersion)) { |
127
|
|
|
$this->remoteUrl = $this->getDownloadUrl($package); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
return $this->remoteVersion; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* Retrieve the current version of the local phar file. |
135
|
|
|
* |
136
|
|
|
* @param Updater $updater |
137
|
|
|
* @return string |
138
|
|
|
*/ |
139
|
|
|
public function getCurrentLocalVersion(Updater $updater) |
140
|
|
|
{ |
141
|
|
|
return $this->localVersion; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Set version string of the local phar |
146
|
|
|
* |
147
|
|
|
* @param string $version |
148
|
|
|
* @return self |
149
|
|
|
*/ |
150
|
|
|
public function setCurrentLocalVersion($version) |
151
|
|
|
{ |
152
|
|
|
$this->localVersion = $version; |
153
|
|
|
return $this; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Set Package name |
158
|
|
|
* |
159
|
|
|
* @param string $name |
160
|
|
|
* @return self |
161
|
|
|
*/ |
162
|
|
|
public function setPackageName($name) |
163
|
|
|
{ |
164
|
|
|
$this->packageName = $name; |
165
|
|
|
return $this; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Get Package name |
170
|
|
|
* |
171
|
|
|
* @return string |
172
|
|
|
*/ |
173
|
|
|
public function getPackageName() |
174
|
|
|
{ |
175
|
|
|
return $this->packageName; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Set phar file's name |
180
|
|
|
* |
181
|
|
|
* @param string $name |
182
|
|
|
* @return self |
183
|
|
|
*/ |
184
|
|
|
public function setPharName($name) |
185
|
|
|
{ |
186
|
|
|
$this->pharName = $name; |
187
|
|
|
return $this; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Get phar file's name |
192
|
|
|
* |
193
|
|
|
* @return string |
194
|
|
|
*/ |
195
|
|
|
public function getPharName() |
196
|
|
|
{ |
197
|
|
|
return $this->pharName; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* Set target stability |
202
|
|
|
* |
203
|
|
|
* @param string $stability |
204
|
|
|
* @return self |
205
|
|
|
*/ |
206
|
|
|
public function setStability($stability) |
207
|
|
|
{ |
208
|
|
|
if ($stability !== self::STABLE && $stability !== self::UNSTABLE && $stability !== self::ANY) { |
209
|
|
|
throw new InvalidArgumentException( |
210
|
|
|
'Invalid stability value. Must be one of "stable", "unstable" or "any".' |
211
|
|
|
); |
212
|
|
|
} |
213
|
|
|
$this->stability = $stability; |
214
|
|
|
return $this; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* Get target stability |
219
|
|
|
* |
220
|
|
|
* @return string |
221
|
|
|
*/ |
222
|
|
|
public function getStability() |
223
|
|
|
{ |
224
|
|
|
return $this->stability; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* @return self |
229
|
|
|
*/ |
230
|
|
|
public function allowMajorVersionUpdates() |
231
|
|
|
{ |
232
|
|
|
$this->allowMajor = true; |
233
|
|
|
return $this; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* @return string |
238
|
|
|
*/ |
239
|
|
|
protected function getApiUrl() |
240
|
|
|
{ |
241
|
|
|
return sprintf(self::API_URL, $this->getPackageName()); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* @param array $package |
246
|
|
|
* @return string |
247
|
|
|
*/ |
248
|
|
|
protected function getDownloadUrl(array $package) |
249
|
|
|
{ |
250
|
|
|
$baseUrl = preg_replace( |
251
|
|
|
'{\.git$}', |
252
|
|
|
'', |
253
|
|
|
$package['package']['versions'][$this->remoteVersion]['source']['url'] |
254
|
|
|
); |
255
|
|
|
$downloadUrl = sprintf( |
256
|
|
|
'%s/releases/download/%s/%s', |
257
|
|
|
$baseUrl, |
258
|
|
|
$this->remoteVersion, |
259
|
|
|
$this->getPharName() |
260
|
|
|
); |
261
|
|
|
return $downloadUrl; |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* Filter a list of versions to those that match the current local version. |
266
|
|
|
* |
267
|
|
|
* @param string[] $versions |
268
|
|
|
* @return string[] |
269
|
|
|
*/ |
270
|
|
|
private function filterByLocalMajorVersion(array $versions) |
271
|
|
|
{ |
272
|
|
|
list($localMajorVersion, ) = explode('.', $this->localVersion, 2); |
273
|
|
|
|
274
|
|
|
return array_filter($versions, function ($version) use ($localMajorVersion) { |
275
|
|
|
list($majorVersion, ) = explode('.', $version, 2); |
276
|
|
|
return $majorVersion === $localMajorVersion; |
277
|
|
|
}); |
278
|
|
|
} |
279
|
|
|
} |
280
|
|
|
|
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.