|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace common\components\maintenance\states; |
|
4
|
|
|
|
|
5
|
|
|
use Yii; |
|
6
|
|
|
use DateTime; |
|
7
|
|
|
use Generator; |
|
8
|
|
|
use Exception; |
|
9
|
|
|
use RuntimeException; |
|
10
|
|
|
use yii\base\BaseObject; |
|
11
|
|
|
use yii\base\InvalidConfigException; |
|
12
|
|
|
use common\components\maintenance\interfaces\StateInterface; |
|
13
|
|
|
use common\components\maintenance\models\SubscribeForm; |
|
14
|
|
|
use yii\helpers\ArrayHelper; |
|
15
|
|
|
|
|
16
|
|
|
/** |
|
17
|
|
|
* Class FileState |
|
18
|
|
|
* @package common\components\maintenance\states |
|
19
|
|
|
* |
|
20
|
|
|
* @property bool|string $filePath |
|
21
|
|
|
* @property array $contentArray |
|
22
|
|
|
* @property array $maintenanceFileLinesParamsArray |
|
23
|
|
|
* @property bool $validDate |
|
24
|
|
|
*/ |
|
25
|
|
|
class FileState extends BaseObject implements StateInterface |
|
26
|
|
|
{ |
|
27
|
|
|
const MAINTENANCE_PARAM_DATE = 'date'; |
|
28
|
|
|
const MAINTENANCE_PARAM_TITLE = 'title'; |
|
29
|
|
|
const MAINTENANCE_PARAM_CONTENT = 'text'; |
|
30
|
|
|
const MAINTENANCE_PARAM_SUBSCRIBE = 'subscribe'; |
|
31
|
|
|
|
|
32
|
|
|
const MAINTENANCE_SUBSCRIBE_ON = 'true'; |
|
33
|
|
|
const MAINTENANCE_SUBSCRIBE_OFF = 'false'; |
|
34
|
|
|
|
|
35
|
|
|
/** |
|
36
|
|
|
* @var string the filename that will determine if the maintenance mode is enabled |
|
37
|
|
|
*/ |
|
38
|
|
|
public $fileName = 'YII_MAINTENANCE_MODE_ENABLED'; |
|
39
|
|
|
|
|
40
|
|
|
/** |
|
41
|
|
|
* Default title |
|
42
|
|
|
* @var string |
|
43
|
|
|
*/ |
|
44
|
|
|
public $defaultTitle = 'Maintenance'; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* Default content |
|
48
|
|
|
* @var string |
|
49
|
|
|
*/ |
|
50
|
|
|
public $defaultContent = 'The site is undergoing technical work. We apologize for any inconvenience caused.'; |
|
51
|
|
|
|
|
52
|
|
|
/** |
|
53
|
|
|
* @var string name of the file where subscribers will be stored |
|
54
|
|
|
*/ |
|
55
|
|
|
public $fileSubscribe = 'YII_MAINTENANCE_MODE_SUBSCRIBE'; |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* Set status subscribe |
|
59
|
|
|
* @var string |
|
60
|
|
|
*/ |
|
61
|
|
|
public $subscribe; |
|
62
|
|
|
|
|
63
|
|
|
/** |
|
64
|
|
|
* @var string the directory in that the file stated in $fileName above is residing |
|
65
|
|
|
*/ |
|
66
|
|
|
public $directory = '@runtime'; |
|
67
|
|
|
|
|
68
|
|
|
/** |
|
69
|
|
|
* @var string the complete path of the file - populated in init |
|
70
|
|
|
*/ |
|
71
|
|
|
public $path; |
|
72
|
|
|
|
|
73
|
|
|
/** |
|
74
|
|
|
* @var string the complete path of the file subscribe - populated in init |
|
75
|
|
|
*/ |
|
76
|
|
|
public $subscribePath; |
|
77
|
|
|
|
|
78
|
|
|
/** |
|
79
|
|
|
* Enter Datetime format |
|
80
|
|
|
* @var string |
|
81
|
|
|
*/ |
|
82
|
|
|
public $dateFormat = 'd-m-Y H:i:s'; |
|
83
|
|
|
|
|
84
|
|
|
/** |
|
85
|
|
|
* Initialization |
|
86
|
|
|
*/ |
|
87
|
|
|
public function init() |
|
88
|
|
|
{ |
|
89
|
|
|
$this->path = $this->getFilePath($this->fileName); |
|
|
|
|
|
|
90
|
|
|
$this->subscribePath = $this->getFilePath($this->fileSubscribe); |
|
|
|
|
|
|
91
|
|
|
$this->subscribe = $this->subscribe ?: self::MAINTENANCE_SUBSCRIBE_ON; |
|
92
|
|
|
} |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* Turn on mode. |
|
96
|
|
|
* |
|
97
|
|
|
* @param string $datetime |
|
98
|
|
|
* @param string $title |
|
99
|
|
|
* @param string $content |
|
100
|
|
|
* @param string $subscribe |
|
101
|
|
|
* @return mixed|void |
|
102
|
|
|
* @throws Exception |
|
103
|
|
|
*/ |
|
104
|
|
|
public function enable($datetime = '', $title = '', $content = '', $subscribe = '') |
|
105
|
|
|
{ |
|
106
|
|
|
$date = new DateTime(date($this->dateFormat)); |
|
107
|
|
|
if ($this->validDate($datetime)) { |
|
108
|
|
|
$date = new DateTime($datetime); |
|
109
|
|
|
} |
|
110
|
|
|
$timestamp = $date->getTimestamp(); |
|
111
|
|
|
|
|
112
|
|
|
$title = $title ?: Yii::t('app', $this->defaultTitle); |
|
113
|
|
|
$content = $content ?: Yii::t('app', $this->defaultContent); |
|
114
|
|
|
$subscribe = $subscribe ?: $this->subscribe; |
|
115
|
|
|
|
|
116
|
|
|
$data = $timestamp . PHP_EOL . $title . PHP_EOL . $content . PHP_EOL . $subscribe . PHP_EOL; |
|
117
|
|
|
$result = file_put_contents($this->path, $data); |
|
118
|
|
|
chmod($this->path, 0765); |
|
119
|
|
|
|
|
120
|
|
|
if ($result === false) { |
|
121
|
|
|
throw new RuntimeException( |
|
122
|
|
|
"Attention: the maintenance mode could not be enabled because {$this->path} could not be created." |
|
123
|
|
|
); |
|
124
|
|
|
} |
|
125
|
|
|
return true; |
|
126
|
|
|
} |
|
127
|
|
|
|
|
128
|
|
|
/** |
|
129
|
|
|
* Update param in maintenance file |
|
130
|
|
|
* @param string $param |
|
131
|
|
|
* @param string $value |
|
132
|
|
|
* @return bool |
|
133
|
|
|
* @throws Exception |
|
134
|
|
|
*/ |
|
135
|
|
|
public function update($param = '', $value = '') |
|
136
|
|
|
{ |
|
137
|
|
|
switch ($param) { |
|
138
|
|
|
case self::MAINTENANCE_PARAM_DATE: |
|
139
|
|
|
if ($this->validDate($value)) { |
|
140
|
|
|
$date = new DateTime($value); |
|
141
|
|
|
$this->replace($date->getTimestamp(), $this->getLine(self::MAINTENANCE_PARAM_DATE)); |
|
142
|
|
|
} |
|
143
|
|
|
break; |
|
144
|
|
|
case self::MAINTENANCE_PARAM_TITLE: |
|
145
|
|
|
$this->replace($value, $this->getLine(self::MAINTENANCE_PARAM_TITLE)); |
|
146
|
|
|
break; |
|
147
|
|
|
case self::MAINTENANCE_PARAM_CONTENT: |
|
148
|
|
|
$this->replace($value, $this->getLine(self::MAINTENANCE_PARAM_CONTENT)); |
|
149
|
|
|
break; |
|
150
|
|
|
case self::MAINTENANCE_PARAM_SUBSCRIBE: |
|
151
|
|
|
$this->replace($value, $this->getLine(self::MAINTENANCE_PARAM_SUBSCRIBE)); |
|
152
|
|
|
break; |
|
153
|
|
|
default: |
|
154
|
|
|
return false; |
|
155
|
|
|
} |
|
156
|
|
|
return true; |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
/** |
|
160
|
|
|
* Turn off mode. |
|
161
|
|
|
* |
|
162
|
|
|
* @return int|mixed |
|
163
|
|
|
*/ |
|
164
|
|
|
public function disable() |
|
165
|
|
|
{ |
|
166
|
|
|
$result = 0; |
|
167
|
|
|
try { |
|
168
|
|
|
if (file_exists($this->path)) { |
|
169
|
|
|
unlink($this->path); |
|
170
|
|
|
$subscribe = new SubscribeForm(); |
|
171
|
|
|
$result = $subscribe->send($this->emails()); |
|
172
|
|
|
if ($result > 0) { |
|
173
|
|
|
unlink($this->subscribePath); |
|
174
|
|
|
} |
|
175
|
|
|
} |
|
176
|
|
|
} catch (RuntimeException $e) { |
|
177
|
|
|
throw new RuntimeException( |
|
178
|
|
|
"Attention: the maintenance mode could not be disabled because {$this->path} could not be removed." |
|
179
|
|
|
); |
|
180
|
|
|
} |
|
181
|
|
|
return $result; |
|
182
|
|
|
} |
|
183
|
|
|
|
|
184
|
|
|
/** |
|
185
|
|
|
* File Line Param |
|
186
|
|
|
* @param $param |
|
187
|
|
|
* @return mixed |
|
188
|
|
|
*/ |
|
189
|
|
|
public function getLine($param) |
|
190
|
|
|
{ |
|
191
|
|
|
return ArrayHelper::getValue(array_flip($this->getMaintenanceFileLinesParamsArray()), $param); |
|
192
|
|
|
} |
|
193
|
|
|
|
|
194
|
|
|
/** |
|
195
|
|
|
* File Lines Params |
|
196
|
|
|
* @return array |
|
197
|
|
|
*/ |
|
198
|
|
|
protected function getMaintenanceFileLinesParamsArray() |
|
199
|
|
|
{ |
|
200
|
|
|
return [ |
|
201
|
|
|
1 => self::MAINTENANCE_PARAM_DATE, |
|
202
|
|
|
2 => self::MAINTENANCE_PARAM_TITLE, |
|
203
|
|
|
3 => self::MAINTENANCE_PARAM_CONTENT, |
|
204
|
|
|
4 => self::MAINTENANCE_PARAM_SUBSCRIBE |
|
205
|
|
|
]; |
|
206
|
|
|
} |
|
207
|
|
|
|
|
208
|
|
|
/** |
|
209
|
|
|
* Update text line to mode file |
|
210
|
|
|
* |
|
211
|
|
|
* @param string $replace |
|
212
|
|
|
* @param int $line |
|
213
|
|
|
* @return mixed|void |
|
214
|
|
|
* @throws Exception |
|
215
|
|
|
*/ |
|
216
|
|
|
public function replace($replace, $line = 1) |
|
217
|
|
|
{ |
|
218
|
|
|
$result = false; |
|
219
|
|
|
if ($replace && file_exists($this->path)) { |
|
220
|
|
|
$file = file($this->path); |
|
221
|
|
|
$file[$line - 1] = $replace . PHP_EOL; |
|
222
|
|
|
$result = file_put_contents($this->path, implode('', $file)); |
|
|
|
|
|
|
223
|
|
|
} |
|
224
|
|
|
if ($result === false) { |
|
225
|
|
|
throw new RuntimeException( |
|
226
|
|
|
"Attention: failed to update the end date of the maintenance mode, because {$this->path} failed to update." |
|
227
|
|
|
); |
|
228
|
|
|
} |
|
229
|
|
|
} |
|
230
|
|
|
|
|
231
|
|
|
/** |
|
232
|
|
|
* Validate datetime |
|
233
|
|
|
* |
|
234
|
|
|
* @param $date |
|
235
|
|
|
* @return bool |
|
236
|
|
|
*/ |
|
237
|
|
|
public function validDate($date) |
|
238
|
|
|
{ |
|
239
|
|
|
$d = DateTime::createFromFormat($this->dateFormat, $date); |
|
240
|
|
|
return $d && $d->format($this->dateFormat) === $date; |
|
241
|
|
|
} |
|
242
|
|
|
|
|
243
|
|
|
/** |
|
244
|
|
|
* Date ant Time |
|
245
|
|
|
* |
|
246
|
|
|
* @param string $format |
|
247
|
|
|
* @param string|integer $timestamp |
|
248
|
|
|
* @return string |
|
249
|
|
|
* @throws InvalidConfigException |
|
250
|
|
|
*/ |
|
251
|
|
|
public function datetime($timestamp = '', $format = '') |
|
252
|
|
|
{ |
|
253
|
|
|
$format = $format ?: $this->dateFormat; |
|
254
|
|
|
$timestamp = $timestamp ?: $this->timestamp(); |
|
255
|
|
|
return Yii::$app->formatter->asDatetime($timestamp, 'php:' . $format); |
|
256
|
|
|
} |
|
257
|
|
|
|
|
258
|
|
|
/** |
|
259
|
|
|
* Timestamp |
|
260
|
|
|
* |
|
261
|
|
|
* @return string |
|
262
|
|
|
*/ |
|
263
|
|
|
public function timestamp() |
|
264
|
|
|
{ |
|
265
|
|
|
return $this->getParams(self::MAINTENANCE_PARAM_DATE); |
|
|
|
|
|
|
266
|
|
|
} |
|
267
|
|
|
|
|
268
|
|
|
/** |
|
269
|
|
|
* Save email in file |
|
270
|
|
|
* |
|
271
|
|
|
* @param string $str |
|
272
|
|
|
* @param string $file |
|
273
|
|
|
* @return bool |
|
274
|
|
|
*/ |
|
275
|
|
|
public function save($str, $file) |
|
276
|
|
|
{ |
|
277
|
|
|
try { |
|
278
|
|
|
if ($str && $file) { |
|
279
|
|
|
$fp = fopen($file, 'ab'); |
|
280
|
|
|
fwrite($fp, $str . PHP_EOL); |
|
|
|
|
|
|
281
|
|
|
fclose($fp); |
|
|
|
|
|
|
282
|
|
|
return chmod($file, 0765); |
|
283
|
|
|
} |
|
284
|
|
|
return false; |
|
285
|
|
|
} catch (RuntimeException $e) { |
|
286
|
|
|
throw new RuntimeException( |
|
287
|
|
|
"Attention: Subscriber cannot be added because {$file} could not be save." |
|
288
|
|
|
); |
|
289
|
|
|
} |
|
290
|
|
|
} |
|
291
|
|
|
|
|
292
|
|
|
/** |
|
293
|
|
|
* Get params this maintenance file |
|
294
|
|
|
* @param string $param |
|
295
|
|
|
* @return array|false|mixed|string |
|
296
|
|
|
*/ |
|
297
|
|
|
public function getParams($param = '') |
|
298
|
|
|
{ |
|
299
|
|
|
$content = $this->getContentArray($this->path); |
|
300
|
|
|
if ($param) { |
|
301
|
|
|
switch ($param) { |
|
302
|
|
|
case self::MAINTENANCE_PARAM_DATE: |
|
303
|
|
|
$value = isset($content[0]) ? $content[0] : time(); |
|
304
|
|
|
break; |
|
305
|
|
|
case self::MAINTENANCE_PARAM_TITLE: |
|
306
|
|
|
$value = isset($content[1]) ? $content[1] : ''; |
|
307
|
|
|
break; |
|
308
|
|
|
case self::MAINTENANCE_PARAM_CONTENT: |
|
309
|
|
|
$value = isset($content[2]) ? $content[2] : ''; |
|
310
|
|
|
break; |
|
311
|
|
|
case self::MAINTENANCE_PARAM_SUBSCRIBE: |
|
312
|
|
|
$value = isset($content[3]) ? $content[3] : ''; |
|
313
|
|
|
break; |
|
314
|
|
|
default: |
|
315
|
|
|
$value = ''; |
|
316
|
|
|
} |
|
317
|
|
|
return $value; |
|
318
|
|
|
} |
|
319
|
|
|
return $content; |
|
320
|
|
|
} |
|
321
|
|
|
|
|
322
|
|
|
/** |
|
323
|
|
|
* Return emails to followers |
|
324
|
|
|
* |
|
325
|
|
|
* @return array |
|
326
|
|
|
*/ |
|
327
|
|
|
public function emails() |
|
328
|
|
|
{ |
|
329
|
|
|
$contents = $this->getContentArray($this->subscribePath); |
|
330
|
|
|
sort($contents); |
|
331
|
|
|
return $contents; |
|
332
|
|
|
} |
|
333
|
|
|
|
|
334
|
|
|
/** |
|
335
|
|
|
* Return content to array this file |
|
336
|
|
|
* |
|
337
|
|
|
* @param $file string |
|
338
|
|
|
* @return array |
|
339
|
|
|
*/ |
|
340
|
|
|
protected function getContentArray($file) |
|
341
|
|
|
{ |
|
342
|
|
|
$contents = $this->readTheFile($file); |
|
343
|
|
|
$items = []; |
|
344
|
|
|
foreach ($contents as $key => $item) { |
|
345
|
|
|
$items[] = $item; |
|
346
|
|
|
} |
|
347
|
|
|
return array_filter($items); |
|
348
|
|
|
} |
|
349
|
|
|
|
|
350
|
|
|
/** |
|
351
|
|
|
* Read file |
|
352
|
|
|
* |
|
353
|
|
|
* @param $file string |
|
354
|
|
|
* @return Generator |
|
355
|
|
|
*/ |
|
356
|
|
|
protected function readTheFile($file) |
|
357
|
|
|
{ |
|
358
|
|
|
try { |
|
359
|
|
|
if (file_exists($file)) { |
|
360
|
|
|
$handle = fopen($file, 'rb'); |
|
361
|
|
|
while (!feof($handle)) { |
|
|
|
|
|
|
362
|
|
|
yield trim(fgets($handle)); |
|
|
|
|
|
|
363
|
|
|
} |
|
364
|
|
|
fclose($handle); |
|
|
|
|
|
|
365
|
|
|
} |
|
366
|
|
|
} catch (RuntimeException $e) { |
|
367
|
|
|
throw new RuntimeException( |
|
368
|
|
|
"Failed to read $file file" |
|
369
|
|
|
); |
|
370
|
|
|
} |
|
371
|
|
|
} |
|
372
|
|
|
|
|
373
|
|
|
/** |
|
374
|
|
|
* Create file |
|
375
|
|
|
* |
|
376
|
|
|
* @param $file string |
|
377
|
|
|
*/ |
|
378
|
|
|
/*protected function createFile($file) |
|
379
|
|
|
{ |
|
380
|
|
|
try { |
|
381
|
|
|
if ($file && !file_exists($file)) { |
|
382
|
|
|
file_put_contents($file, ''); |
|
383
|
|
|
chmod($file, 0765); |
|
384
|
|
|
} |
|
385
|
|
|
} catch (RuntimeException $e) { |
|
386
|
|
|
throw new RuntimeException( |
|
387
|
|
|
"Failed to create $file file." |
|
388
|
|
|
); |
|
389
|
|
|
} |
|
390
|
|
|
}*/ |
|
391
|
|
|
|
|
392
|
|
|
/** |
|
393
|
|
|
* @return bool will return true if on timer |
|
394
|
|
|
*/ |
|
395
|
|
|
public function isTimer() |
|
396
|
|
|
{ |
|
397
|
|
|
return (($date = $this->getParams(self::MAINTENANCE_PARAM_DATE)) && $date > time()); |
|
398
|
|
|
} |
|
399
|
|
|
|
|
400
|
|
|
/** |
|
401
|
|
|
* @return bool will return true if on subscribe |
|
402
|
|
|
*/ |
|
403
|
|
|
public function isSubscribe() |
|
404
|
|
|
{ |
|
405
|
|
|
$param = $this->getParams(self::MAINTENANCE_PARAM_SUBSCRIBE); |
|
406
|
|
|
return $param === 'true'; |
|
407
|
|
|
} |
|
408
|
|
|
|
|
409
|
|
|
/** |
|
410
|
|
|
* @return bool will return true if the file exists |
|
411
|
|
|
*/ |
|
412
|
|
|
public function isEnabled() |
|
413
|
|
|
{ |
|
414
|
|
|
return file_exists($this->path); |
|
415
|
|
|
} |
|
416
|
|
|
|
|
417
|
|
|
/** |
|
418
|
|
|
* Return file path. |
|
419
|
|
|
* |
|
420
|
|
|
* @param $fileName string |
|
421
|
|
|
* @return bool|string |
|
422
|
|
|
*/ |
|
423
|
|
|
protected function getFilePath($fileName) |
|
424
|
|
|
{ |
|
425
|
|
|
return Yii::getAlias($this->directory . '/' . $fileName); |
|
426
|
|
|
} |
|
427
|
|
|
} |
|
428
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.