|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
declare(strict_types=1); |
|
4
|
|
|
|
|
5
|
|
|
/** |
|
6
|
|
|
* balloon |
|
7
|
|
|
* |
|
8
|
|
|
* @copyright Copryright (c) 2012-2018 gyselroth GmbH (https://gyselroth.com) |
|
9
|
|
|
* @license GPL-3.0 https://opensource.org/licenses/GPL-3.0 |
|
10
|
|
|
*/ |
|
11
|
|
|
|
|
12
|
|
|
namespace Balloon\App\DesktopClient; |
|
13
|
|
|
|
|
14
|
|
|
use Psr\Log\LoggerInterface; |
|
15
|
|
|
|
|
16
|
|
|
class DesktopClient |
|
17
|
|
|
{ |
|
18
|
|
|
/** |
|
19
|
|
|
* Github request url. |
|
20
|
|
|
* |
|
21
|
|
|
* @var string |
|
22
|
|
|
*/ |
|
23
|
|
|
protected $github_request_url = 'https://api.github.com/repos/gyselroth/balloon-client-desktop/releases/latest'; |
|
24
|
|
|
|
|
25
|
|
|
/** |
|
26
|
|
|
* Github request timeout. |
|
27
|
|
|
* |
|
28
|
|
|
* @var int |
|
29
|
|
|
*/ |
|
30
|
|
|
protected $github_request_timeout = 10; |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* Github asset mapping. |
|
34
|
|
|
* |
|
35
|
|
|
* @var array |
|
36
|
|
|
*/ |
|
37
|
|
|
protected $github_asset_mapping = [ |
|
38
|
|
|
'deb' => '#.*\.deb$#', |
|
39
|
|
|
'rpm' => '#.*\.rpm$#', |
|
40
|
|
|
'exe' => '#.*\.exe$#', |
|
41
|
|
|
'zip' => '#.*\.zip$#', |
|
42
|
|
|
'pkg' => '#.*\.pkg$#', |
|
43
|
|
|
]; |
|
44
|
|
|
|
|
45
|
|
|
/** |
|
46
|
|
|
* Github request useragent. |
|
47
|
|
|
* |
|
48
|
|
|
* @var string |
|
49
|
|
|
*/ |
|
50
|
|
|
protected $github_request_useragent = 'balloon server'; |
|
51
|
|
|
|
|
52
|
|
|
/** |
|
53
|
|
|
* Formats. |
|
54
|
|
|
* |
|
55
|
|
|
* @var array |
|
56
|
|
|
*/ |
|
57
|
|
|
protected $formats = []; |
|
58
|
|
|
|
|
59
|
|
|
/** |
|
60
|
|
|
* Logger. |
|
61
|
|
|
* |
|
62
|
|
|
* @var LoggerInterface |
|
63
|
|
|
*/ |
|
64
|
|
|
protected $logger; |
|
65
|
|
|
|
|
66
|
|
|
/** |
|
67
|
|
|
* Constructor. |
|
68
|
|
|
* |
|
69
|
|
|
* @param LoggerInterface $logger |
|
70
|
|
|
* @param iterable $config |
|
71
|
|
|
*/ |
|
72
|
|
|
public function __construct(LoggerInterface $logger, ?Iterable $config = null) |
|
73
|
|
|
{ |
|
74
|
|
|
$this->logger = $logger; |
|
75
|
|
|
$this->setOptions($config); |
|
76
|
|
|
} |
|
77
|
|
|
|
|
78
|
|
|
/** |
|
79
|
|
|
* Set options. |
|
80
|
|
|
* |
|
81
|
|
|
* @param iterable $config |
|
82
|
|
|
* |
|
83
|
|
|
* @return DesktopClient |
|
84
|
|
|
*/ |
|
85
|
|
|
public function setOptions(?Iterable $config = null): self |
|
86
|
|
|
{ |
|
87
|
|
|
if (null === $config) { |
|
88
|
|
|
return $this; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
foreach ($config as $option => $value) { |
|
92
|
|
|
switch ($option) { |
|
93
|
|
|
case 'github_request_url': |
|
|
|
|
|
|
94
|
|
|
case 'github_request_timeout': |
|
95
|
|
|
case 'github_request_useragent': |
|
96
|
|
|
$this->{$option} = (string) $value; |
|
97
|
|
|
|
|
98
|
|
|
break; |
|
99
|
|
|
case 'github_request_timeout': |
|
100
|
|
|
$this->github_request_timeout = (int) $value; |
|
101
|
|
|
|
|
102
|
|
|
break; |
|
103
|
|
|
case 'formats': |
|
104
|
|
|
case 'github_asset_mapping': |
|
105
|
|
|
$this->{$option} = $value; |
|
106
|
|
|
|
|
107
|
|
|
break; |
|
108
|
|
|
default: |
|
109
|
|
|
throw new Exception('invalid option '.$option.' given'); |
|
110
|
|
|
} |
|
111
|
|
|
} |
|
112
|
|
|
|
|
113
|
|
|
return $this; |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
/** |
|
117
|
|
|
* Get url. |
|
118
|
|
|
* |
|
119
|
|
|
* @param string $format |
|
120
|
|
|
* |
|
121
|
|
|
* @return string |
|
122
|
|
|
*/ |
|
123
|
|
|
public function getUrl(string $format): string |
|
124
|
|
|
{ |
|
125
|
|
|
if (isset($this->formats[$format])) { |
|
126
|
|
|
return $this->formats[$format]; |
|
127
|
|
|
} |
|
128
|
|
|
|
|
129
|
|
|
return $this->getGithubUrl($format); |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
|
|
/** |
|
133
|
|
|
* Get github url. |
|
134
|
|
|
* |
|
135
|
|
|
* @param string $format |
|
136
|
|
|
* |
|
137
|
|
|
* @return string |
|
138
|
|
|
*/ |
|
139
|
|
|
protected function getGithubUrl(string $format): string |
|
140
|
|
|
{ |
|
141
|
|
|
if (!isset($this->github_asset_mapping[$format])) { |
|
142
|
|
|
throw new Exception('unknown format '.$format.' requested'); |
|
143
|
|
|
} |
|
144
|
|
|
|
|
145
|
|
|
$ch = curl_init(); |
|
146
|
|
|
curl_setopt($ch, CURLOPT_URL, $this->github_request_url); |
|
147
|
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, $this->github_request_timeout); |
|
148
|
|
|
curl_setopt($ch, CURLOPT_USERAGENT, $this->github_request_useragent); |
|
149
|
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
|
150
|
|
|
$data = curl_exec($ch); |
|
151
|
|
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE); |
|
152
|
|
|
|
|
153
|
|
|
$this->logger->debug('github request to ['.$this->github_request_url.'] resulted in ['.$code.']', [ |
|
154
|
|
|
'category' => get_class($this), |
|
155
|
|
|
]); |
|
156
|
|
|
|
|
157
|
|
|
if (200 !== $code) { |
|
158
|
|
|
throw new Exception('failed query github releases api'); |
|
159
|
|
|
} |
|
160
|
|
|
|
|
161
|
|
|
$data = json_decode($data, true); |
|
162
|
|
|
|
|
163
|
|
|
if (!is_array($data) || !isset($data['assets']) || 0 === count($data['assets'])) { |
|
164
|
|
|
throw new Exception('no github release assets found'); |
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
foreach ($data['assets'] as $asset) { |
|
168
|
|
|
$this->logger->debug('check release asset ['.$asset['name'].'] for ['.$this->github_asset_mapping[$format].']', [ |
|
169
|
|
|
'category' => get_class($this), |
|
170
|
|
|
]); |
|
171
|
|
|
|
|
172
|
|
|
if (preg_match($this->github_asset_mapping[$format], $asset['name'])) { |
|
173
|
|
|
$this->logger->info('github asset ['.$asset['browser_download_url'].'] found', [ |
|
174
|
|
|
'category' => get_class($this), |
|
175
|
|
|
]); |
|
176
|
|
|
|
|
177
|
|
|
return $asset['browser_download_url']; |
|
178
|
|
|
} |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
throw new Exception('no github release asset matches request format'); |
|
182
|
|
|
} |
|
183
|
|
|
} |
|
184
|
|
|
|
As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next
break.There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.