Passed
Push — develop ( 1b12bd...27a549 )
by Nikolay
05:03
created

Util::flattenArray()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 10
rs 10
cc 3
nc 3
nop 1
1
<?php
2
/**
3
 * Copyright © MIKO LLC - All Rights Reserved
4
 * Unauthorized copying of this file, via any medium is strictly prohibited
5
 * Proprietary and confidential
6
 * Written by Alexey Portnov, 6 2020
7
 */
8
9
namespace MikoPBX\Core\System;
10
11
use AGI_AsteriskManager;
12
use DateTime;
13
use Exception;
14
use MikoPBX\Common\Models\{CallEventsLogs, CustomFiles};
15
use Phalcon\Di;
16
use ReflectionClass;
17
18
/**
19
 * Вспомогательные методы.
20
 */
21
class Util
22
{
23
    /**
24
     * @param $options
25
     * @param $manual_attributes
26
     * @param $section
27
     *
28
     * @return string
29
     */
30
    public static function overrideConfigurationArray($options, $manual_attributes, $section): string
31
    {
32
        $result_config = '';
33
        if ($manual_attributes !== null && isset($manual_attributes[$section])) {
34
            foreach ($manual_attributes[$section] as $key => $value) {
35
                if ($key === 'type') {
36
                    continue;
37
                }
38
                $options[$key] = $value;
39
            }
40
        }
41
        foreach ($options as $key => $value) {
42
            if (empty($value) || empty($key)) {
43
                continue;
44
            }
45
            if (is_array($value)) {
46
                array_unshift($value, ' ');
47
                $result_config .= trim(implode("\n{$key} = ", $value)) . "\n";
48
            } else {
49
                $result_config .= "{$key} = {$value}\n";
50
            }
51
        }
52
53
        return "$result_config\n";
54
    }
55
56
    /**
57
     * Завершаем процесс по имени.
58
     *
59
     * @param $procName
60
     *
61
     * @return int|null
62
     */
63
    public static function killByName($procName): ?int
64
    {
65
        $killallPath = self::which('killall');
66
67
        return self::mwExec($killallPath . ' ' . escapeshellarg($procName));
68
    }
69
70
    /**
71
     * Выполняет системную команду exec().
72
     *
73
     * @param      $command
74
     * @param null $oarr
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $oarr is correct as it would always require null to be passed?
Loading history...
75
     * @param null $retval
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $retval is correct as it would always require null to be passed?
Loading history...
76
     *
77
     * @return int
78
     */
79
    public static function mwExec($command, &$oarr = null, &$retval = null): ?int
80
    {
81
        $retval = 0;
82
        $oarr   = [];
83
        $di     = Di::getDefault();
84
85
        if ($di !== null && $di->getShared('config')->path('core.debugMode')) {
86
            echo "mwExec(): $command\n";
87
        } else {
88
            exec("$command 2>&1", $oarr, $retval);
89
        }
90
91
        return $retval;
92
    }
93
94
    /**
95
     * Выполняет системную команду exec() в фоне.
96
     *
97
     * @param $command
98
     * @param $out_file
99
     * @param $sleep_time
100
     */
101
    public static function mwExecBg($command, $out_file = '/dev/null', $sleep_time = 0): void
102
    {
103
        $nohupPath = self::which('nohup');
104
        $shPath    = self::which('sh');
105
        $rmPath    = self::which('rm');
106
        $sleepPath = self::which('sleep');
107
        if ($sleep_time > 0) {
108
            $filename = '/tmp/' . time() . '_noop.sh';
109
            file_put_contents($filename, "{$sleepPath} {$sleep_time}; {$command}; {$rmPath} -rf {$filename}");
110
            $noop_command = "{$nohupPath} {$shPath} {$filename} > {$out_file} 2>&1 &";
111
        } else {
112
            $noop_command = "{$nohupPath} {$command} > {$out_file} 2>&1 &";
113
        }
114
        exec($noop_command);
115
    }
116
117
    /**
118
     * Выполняет системную команду exec() в фоне.
119
     *
120
     * @param        $command
121
     * @param int    $timeout
122
     * @param string $logname
123
     */
124
    public static function mwExecBgWithTimeout($command, $timeout = 4, $logname = '/dev/null'): void
125
    {
126
        $di = Di::getDefault();
127
128
        if ($di !== null && $di->getShared('config')->path('core.debugMode')) {
129
            echo "mwExecBg(): $command\n";
130
131
            return;
132
        }
133
        $nohupPath   = self::which('nohup');
134
        $timeoutPath = self::which('timeout');
135
        exec("{$nohupPath} {$timeoutPath} -t {$timeout} {$command} > {$logname} 2>&1 &");
136
    }
137
138
    /**
139
     * Выполнение нескольких команд.
140
     *
141
     * @param        $arr_cmds
142
     * @param array  $out
143
     * @param string $logname
144
     */
145
    public static function mwExecCommands($arr_cmds, &$out = [], $logname = ''): void
146
    {
147
        $out = [];
148
        foreach ($arr_cmds as $cmd) {
149
            $out[]   = "$cmd;";
150
            $out_cmd = [];
151
            self::mwExec($cmd, $out_cmd);
152
            $out = array_merge($out, $out_cmd);
153
        }
154
155
        if ($logname !== '') {
156
            $result = implode("\n", $out);
157
            file_put_contents("/tmp/{$logname}_commands.log", $result);
158
        }
159
    }
160
161
    /**
162
     * Create folder if it not exist
163
     *
164
     * @param  $parameters string one or multiple paths separated by space
165
     *
166
     * @param bool $addWWWRights
167
     *
168
     * @return bool
169
     */
170
    public static function mwMkdir(string $parameters, bool $addWWWRights=false): bool
171
    {
172
        $result = true;
173
        if (posix_getuid() === 0) {
174
            $arrPaths = explode(' ', $parameters);
175
            if (count($arrPaths) > 0) {
176
                foreach ($arrPaths as $path) {
177
                    if ( ! empty($path)
178
                        && ! file_exists($path)
179
                        && ! mkdir($path, 0755, true)
180
                        && ! is_dir($path)) {
181
                        $result = false;
182
                    }
183
                    if ($addWWWRights) {
184
                        self::addRegularWWWRights($path);
185
                    }
186
                }
187
            }
188
        }
189
        return $result;
190
    }
191
192
    /**
193
     * Restart PHP workers
194
     *
195
     * @param string $className
196
     * @param string $param
197
     */
198
    public static function restartPHPWorker(string $className, string $param = 'start'): void
199
    {
200
        $workerPath = self::getFilePathByClassName($className);
201
        if ( ! empty($workerPath)) {
202
            $command = "php -f {$workerPath}";
203
            self::processWorker($command, $param, $className, 'restart');
204
        }
205
    }
206
207
    /**
208
     * Try to find full path to php file by class name
209
     *
210
     * @param $className
211
     *
212
     * @return string|null
213
     */
214
    public static function getFilePathByClassName($className): ?string
215
    {
216
        $filename = null;
217
        try {
218
            $reflection = new ReflectionClass($className);
219
            $filename   = $reflection->getFileName();
220
        } catch (Exception $exception) {
221
            self::sysLogMsg('Util', 'Error ' . $exception->getMessage());
222
        }
223
224
        return $filename;
225
    }
226
227
    /**
228
     * Добавить сообщение в Syslog.
229
     *
230
     * @param     $log_name
231
     * @param     $text
232
     * @param ?int $level
233
     */
234
    public static function sysLogMsg($log_name, $text, $level = null): void
235
    {
236
        $level = ($level === null) ? LOG_WARNING : $level;
237
        openlog("$log_name", LOG_PID | LOG_PERROR, LOG_AUTH);
238
        syslog($level, "$text");
239
        closelog();
240
    }
241
242
    /**
243
     * Управление процессом / демоном.
244
     * Получние информации по статусу процесса.
245
     *
246
     * @param $cmd
247
     * @param $param
248
     * @param $proc_name
249
     * @param $action
250
     * @param $out_file
251
     *
252
     * @return array | bool
253
     */
254
    public static function processWorker($cmd, $param, $proc_name, $action, $out_file = '/dev/null')
255
    {
256
        $path_kill  = self::which('kill');
257
        $path_nohup = self::which('nohup');
258
259
        $WorkerPID = self::getPidOfProcess($proc_name);
260
261
        if ('status' === $action) {
262
            $status = ($WorkerPID !== '') ? 'Started' : 'Stoped';
263
264
            return ['status' => $status, 'app' => $proc_name, 'PID' => $WorkerPID];
265
        }
266
        $out = [];
267
268
        if ($WorkerPID !== '' && ('stop' === $action || 'restart' === $action)) {
269
            self::mwExec("{$path_kill} -9 {$WorkerPID}  > /dev/null 2>&1 &", $out);
270
            $WorkerPID = '';
271
        }
272
273
        if ($WorkerPID === '' && ('start' === $action || 'restart' === $action)) {
274
            self::mwExec("{$path_nohup} {$cmd} {$param}  > {$out_file} 2>&1 &", $out);
275
            // usleep(500000);
276
        }
277
278
        return true;
279
    }
280
281
    /**
282
     * Return full path to executable binary
283
     *
284
     * @param string $cmd - name of file
285
     *
286
     * @return string
287
     */
288
    public static function which(string $cmd): string
289
    {
290
        global $_ENV;
291
        if (array_key_exists('PATH', $_ENV)) {
292
            $binaryFolders = $_ENV['PATH'];
293
294
            foreach (explode(':', $binaryFolders) as $path) {
295
                if (is_executable("{$path}/{$cmd}")) {
296
                    return "{$path}/{$cmd}";
297
                }
298
            }
299
        }
300
        $binaryFolders =
301
            [
302
                '/bin',
303
                '/sbin',
304
                '/usr/bin',
305
                '/usr/sbin',
306
                '/usr/local/bin',
307
                '/usr/local/sbin',
308
            ];
309
        foreach ($binaryFolders as $path) {
310
            if (is_executable("{$path}/{$cmd}")) {
311
                return "{$path}/{$cmd}";
312
            }
313
        }
314
315
        return $cmd;
316
    }
317
318
    /**
319
     * Возвращает PID процесса по его имени.
320
     *
321
     * @param        $name
322
     * @param string $exclude
323
     *
324
     * @return string
325
     */
326
    public static function getPidOfProcess($name, $exclude = ''): string
327
    {
328
        $path_ps   = self::which('ps');
329
        $path_grep = self::which('grep');
330
        $path_awk  = self::which('awk');
331
332
        $name       = addslashes($name);
333
        $filter_cmd = '';
334
        if ( ! empty($exclude)) {
335
            $filter_cmd = "| $path_grep -v " . escapeshellarg($exclude);
336
        }
337
        $out = [];
338
        self::mwExec(
339
            "{$path_ps} -A -o 'pid,args' {$filter_cmd} | {$path_grep} '{$name}' | {$path_grep} -v grep | {$path_awk} ' {print $1} '",
340
            $out
341
        );
342
343
        return trim(implode(' ', $out));
344
    }
345
346
    /**
347
     * Инициация телефонного звонка.
348
     * @param $peer_number
349
     * @param $peer_mobile
350
     * @param $dest_number
351
     * @return array
352
     * @throws Exception
353
     */
354
    public static function amiOriginate($peer_number, $peer_mobile, $dest_number): array
355
    {
356
        $am       = self::getAstManager('off');
357
        $channel  = 'Local/' . $peer_number . '@internal-originate';
358
        $context  = 'all_peers';
359
        $IS_ORGNT = self::generateRandomString();
360
        $variable = "_IS_ORGNT={$IS_ORGNT},pt1c_cid={$dest_number},_extenfrom1c={$peer_number},__peer_mobile={$peer_mobile},_FROM_PEER={$peer_number}";
361
362
        return $am->Originate($channel,
363
                              $dest_number,
364
                              $context,
365
                      '1', null, null, null, null, $variable, null, true);
366
    }
367
368
    /**
369
     * Получаем объект менеджер asterisk.
370
     *
371
     * @param string $events
372
     *
373
     * @return AGI_AsteriskManager
374
     */
375
    public static function getAstManager($events = 'on'): AGI_AsteriskManager
376
    {
377
        global $g;
378
        require_once 'phpagi.php';
379
        if (isset($g['AGI_AsteriskManager'])) {
380
            /** @var AGI_AsteriskManager $am */
381
            $am = $g['AGI_AsteriskManager'];
382
            // Проверка на разрыв соединения.
383
            if (is_resource($am->socket)) {
384
                $res = $am->sendRequestTimeout('Ping');
385
                if (isset($res['Response']) && trim($res['Response']) != '') {
386
                    // Уже есть подключенный экземпляр класса.
387
                    return $am;
388
                }
389
            } else {
390
                unset($g['AGI_AsteriskManager']);
391
            }
392
        }
393
        $config = new MikoPBXConfig();
394
        $port   = $config->getGeneralSettings('AMIPort');
395
396
        $am  = new AGI_AsteriskManager();
397
        $res = $am->connect("127.0.0.1:{$port}", null, null, $events);
398
        if (true === $res) {
399
            $g['AGI_AsteriskManager'] = $am;
400
        }
401
402
        return $am;
403
    }
404
405
    /**
406
     * Генератор произвольной строки.
407
     * @param int $length
408
     * @return string
409
     * @throws Exception
410
     */
411
    public static function generateRandomString($length = 10): string
412
    {
413
        $characters       = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
414
        $charactersLength = strlen($characters);
415
        $randomString     = '';
416
        for ($i = 0; $i < $length; $i++) {
417
            $randomString .= $characters[random_int(0, $charactersLength - 1)];
418
        }
419
420
        return $randomString;
421
    }
422
423
    /**
424
     * Json validate
425
     *
426
     * @param $jsonString
427
     *
428
     * @return bool
429
     */
430
    public static function isJson($jsonString): bool
431
    {
432
        json_decode($jsonString, true);
433
434
        return (json_last_error() === JSON_ERROR_NONE);
435
    }
436
437
    /**
438
     *  Возвращает размер файла в Мб.
439
     *
440
     * @param $filename
441
     *
442
     * @return float|int
443
     */
444
    public static function mFileSize($filename)
445
    {
446
        $size = 0;
447
        if (file_exists($filename)) {
448
            $tmp_size = filesize($filename);
449
            if ($tmp_size !== false) {
450
                // Получим размер в Мб.
451
                $size = $tmp_size;
452
            }
453
        }
454
455
        return $size;
456
    }
457
458
    /**
459
     * Возвращает указанное количество X.
460
     *
461
     * @param $length
462
     *
463
     * @return string
464
     */
465
    public static function getExtensionX($length): string
466
    {
467
        $extension = '';
468
        for ($i = 0; $i < $length; $i++) {
469
            $extension .= 'X';
470
        }
471
472
        return $extension;
473
    }
474
475
    /**
476
     * Проверяет существование файла.
477
     *
478
     * @param $filename
479
     *
480
     * @return bool
481
     */
482
    public static function recFileExists($filename): ?bool
483
    {
484
        return (file_exists($filename) && filesize($filename) > 0);
485
    }
486
487
    /**
488
     * Если переданный параметр - число, то будет возвращена дата.
489
     *
490
     * @param $data
491
     *
492
     * @return string
493
     */
494
    public static function numberToDate($data): string
495
    {
496
        $re_number = '/^\d+.\d+$/';
497
        preg_match_all($re_number, $data, $matches, PREG_SET_ORDER, 0);
498
        if (count($matches) > 0) {
499
            $data = date('Y.m.d-H:i:s', $data);
500
        }
501
502
        return $data;
503
    }
504
505
    /**
506
     * Записывает данные в файл.
507
     *
508
     * @param $filename
509
     * @param $data
510
     */
511
    public static function fileWriteContent($filename, $data): void
512
    {
513
        /** @var CustomFiles $res */
514
        $res = CustomFiles::findFirst("filepath = '{$filename}'");
515
516
        $filename_orgn = "{$filename}.orgn";
517
        if (($res === null || $res->mode === 'none') && file_exists($filename_orgn)) {
518
            unlink($filename_orgn);
519
        } elseif ($res !== null && $res->mode !== 'none') {
520
            // Запишем оригинальный файл.
521
            file_put_contents($filename_orgn, $data);
522
        }
523
524
        if ( ! $res) {
0 ignored issues
show
introduced by
$res is of type MikoPBX\Common\Models\CustomFiles, thus it always evaluated to true.
Loading history...
525
            // Файл еще не зарегистрирован в базе. Сделаем это.
526
            $res = new CustomFiles();
527
            $res->writeAttribute('filepath', $filename);
528
            $res->writeAttribute('mode', 'none');
529
            $res->save();
530
        } elseif ($res->mode === 'append') {
531
            // Добавить к файлу.
532
            $data .= "\n\n";
533
            $data .= base64_decode($res->content);
534
        } elseif ($res->mode === 'override') {
535
            // Переопределить файл.
536
            $data = base64_decode($res->content);
537
        }
538
        file_put_contents($filename, $data);
539
    }
540
541
542
543
    /**
544
     * Пишем лог в базу данных.
545
     *
546
     * @param $app
547
     * @param $data_obj
548
     */
549
    public static function logMsgDb($app, $data_obj): void
550
    {
551
        try {
552
            $data = new CallEventsLogs();
553
            $data->writeAttribute('eventtime', date("Y-m-d H:i:s"));
554
            $data->writeAttribute('app', $app);
555
            $data->writeAttribute('datajson', json_encode($data_obj, JSON_UNESCAPED_SLASHES));
556
557
            if (is_array($data_obj) && isset($data_obj['linkedid'])) {
558
                $data->writeAttribute('linkedid', $data_obj['linkedid']);
559
            }
560
            $data->save();
561
        } catch (Exception $e) {
562
            self::sysLogMsg('logMsgDb', $e->getMessage());
563
        }
564
    }
565
566
    /**
567
     * Возвращает текущую дату в виде строки с точностью до милисекунд.
568
     *
569
     * @return string
570
     */
571
    public static function getNowDate(): ?string
572
    {
573
        $result = null;
574
        try {
575
            $d      = new DateTime();
576
            $result = $d->format("Y-m-d H:i:s.v");
577
        } catch (Exception $e) {
578
            unset($e);
579
        }
580
581
        return $result;
582
    }
583
584
585
586
    /**
587
     * Получает расширение файла.
588
     *
589
     * @param        $filename
590
     * @return mixed
591
     */
592
    public static function getExtensionOfFile($filename)
593
    {
594
        $path_parts = pathinfo($filename);
595
        return $path_parts['extension'];
596
    }
597
598
    /**
599
     * Удаляет расширение файла.
600
     *
601
     * @param        $filename
602
     * @param string $delimiter
603
     *
604
     * @return string
605
     */
606
    public static function trimExtensionForFile($filename, $delimiter = '.'): string
607
    {
608
        // Отсечем расширение файла.
609
        $tmp_arr = explode((string)$delimiter, $filename);
610
        if (count($tmp_arr) > 1) {
611
            unset($tmp_arr[count($tmp_arr) - 1]);
612
            $filename = implode((string)$delimiter, $tmp_arr);
613
        }
614
615
        return $filename;
616
    }
617
618
619
620
    /**
621
     * Получаем размер файла / директории.
622
     *
623
     * @param $filename
624
     *
625
     * @return float
626
     */
627
    public static function getSizeOfFile($filename): float
628
    {
629
        $result = 0;
630
        if (file_exists($filename)) {
631
            $duPath  = self::which('du');
632
            $awkPath = self::which('awk');
633
            self::mwExec("{$duPath} -d 0 -k '{$filename}' | {$awkPath}  '{ print $1}'", $out);
634
            $time_str = implode($out);
635
            preg_match_all('/^\d+$/', $time_str, $matches, PREG_SET_ORDER, 0);
636
            if (count($matches) > 0) {
637
                $result = round(1 * $time_str / 1024, 2);
638
            }
639
        }
640
641
        return $result;
642
    }
643
644
    /**
645
     * Устанавливаем шрифт для консоли.
646
     */
647
    public static function setCyrillicFont(): void
648
    {
649
        $setfontPath = self::which('setfont');
650
        self::mwExec("{$setfontPath} /usr/share/consolefonts/Cyr_a8x16.psfu.gz 2>/dev/null");
651
    }
652
653
    /**
654
     * Получить перевод строки текста.
655
     *
656
     * @param $text
657
     *
658
     * @return mixed
659
     */
660
    public static function translate($text)
661
    {
662
        $di = Di::getDefault();
663
        if ($di !== null) {
664
            return $di->getShared('translation')->_($text);
665
        } else {
666
            return $text;
667
        }
668
    }
669
670
    /**
671
     * Check if all the parts exist, and
672
     * gather all the parts of the file together
673
     *
674
     * @param string $temp_dir - the temporary directory holding all the parts of the file
675
     * @param string $totalSize - original file size (in bytes)
676
     * @return bool
677
     */
678
    public static function createFileFromChunks(string $temp_dir, string $totalSize): bool {
679
        // count all the parts of this file
680
        $total_files_on_server_size = 0;
681
        foreach (scandir($temp_dir) as $file) {
682
            $temp_total                 = $total_files_on_server_size;
683
            $tempfilesize               = filesize($temp_dir . '/' . $file);
684
            $total_files_on_server_size = $temp_total + $tempfilesize;
685
        }
686
        // check that all the parts are present
687
        // If the Size of all the chunks on the server is equal to the size of the file uploaded.
688
        if ($total_files_on_server_size >= $totalSize) {
689
            // Загрузка завершена.
690
            return true;
691
        }
692
693
        // Загрузка еще не завершена. Часть файла успешно сохранена.
694
        return false;
695
    }
696
697
    /**
698
     * @param        $temp_dir
699
     * @param        $fileName
700
     * @param        $total_files
701
     * @param string $result_file
702
     * @param string $progress_dir
703
     *
704
     * @return bool|string
705
     */
706
    public static function mergeFilesInDirectory(
707
        $temp_dir,
708
        $fileName,
709
        $total_files,
710
        $result_file = '',
711
        $progress_dir = ''
712
    ) {
713
        if (empty($result_file)) {
714
            $result_file = dirname($temp_dir) . '/' . $fileName;
715
        }
716
717
        $show_progress = file_exists($progress_dir);
718
        $progress_file = $progress_dir . '/progress';
719
        if ($show_progress && ! file_exists($progress_file)) {
720
            file_put_contents($progress_file, '0');
721
        }
722
723
        // create the final destination file
724
        if (($fp = fopen($result_file, 'w')) !== false) {
725
            for ($i = 1; $i <= $total_files; $i++) {
726
                $tmp_file = $temp_dir . '/' . $fileName . '.part' . $i;
727
                fwrite($fp, file_get_contents($tmp_file));
728
                // Удаляем временный файл.
729
                unlink($tmp_file);
730
                if ($show_progress) {
731
                    file_put_contents($progress_file, round($i / $total_files * 100), 2);
732
                }
733
            }
734
            fclose($fp);
735
        } else {
736
            self::sysLogMsg('UploadFile', 'cannot create the destination file - ' . $result_file);
737
738
            return false;
739
        }
740
        self::sysLogMsg('UploadFile', 'destination file - ' . $result_file);
741
        // rename the temporary directory (to avoid access from other
742
        // concurrent chunks uploads) and than delete it
743
        if (rename($temp_dir, $temp_dir . '_UNUSED')) {
744
            self::rRmDir($temp_dir . '_UNUSED');
745
        } else {
746
            self::rRmDir($temp_dir);
747
        }
748
749
        if ($show_progress) {
750
            file_put_contents($progress_file, 100);
751
        }
752
753
        // Загрузка завершена. Возвращаем путь к файлу.
754
        return $result_file;
755
    }
756
757
    /**
758
     *
759
     * Delete a directory RECURSIVELY
760
     *
761
     * @param string $dir - directory path
762
     *
763
     * @link http://php.net/manual/en/function.rmdir.php
764
     */
765
    public static function rRmDir(string $dir): void
766
    {
767
        if (is_dir($dir)) {
768
            $objects = scandir($dir);
769
            foreach ($objects as $object) {
770
                if ($object != "." && $object != "..") {
771
                    if (filetype($dir . "/" . $object) == "dir") {
772
                        self::rRmDir($dir . "/" . $object);
773
                    } else {
774
                        unlink($dir . "/" . $object);
775
                    }
776
                }
777
            }
778
            reset($objects);
0 ignored issues
show
Bug introduced by
It seems like $objects can also be of type false; however, parameter $array of reset() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

778
            reset(/** @scrutinizer ignore-type */ $objects);
Loading history...
779
            rmdir($dir);
780
        }
781
    }
782
783
    /**
784
     * Генерация сертификата средствами openssl.
785
     *
786
     * @param ?array $options
787
     * @param ?array $config_args_pkey
788
     * @param ?array $config_args_csr
789
     *
790
     * @return array
791
     */
792
    public static function generateSslCert($options = null, $config_args_pkey = null, $config_args_csr = null): array
793
    {
794
        // Инициализация настроек.
795
        if ( ! $options) {
796
            $options = [
797
                "countryName"            => 'RU',
798
                "stateOrProvinceName"    => 'Moscow',
799
                "localityName"           => 'Zelenograd',
800
                "organizationName"       => 'MIKO LLC',
801
                "organizationalUnitName" => 'Software development',
802
                "commonName"             => 'MIKO PBX',
803
                "emailAddress"           => '[email protected]',
804
            ];
805
        }
806
807
        if ( ! $config_args_csr) {
808
            $config_args_csr = ['digest_alg' => 'sha256'];
809
        }
810
811
        if ( ! $config_args_pkey) {
812
            $config_args_pkey = [
813
                "private_key_bits" => 2048,
814
                "private_key_type" => OPENSSL_KEYTYPE_RSA,
815
            ];
816
        }
817
818
        // Генерация ключей.
819
        $private_key = openssl_pkey_new($config_args_pkey);
820
        $csr         = openssl_csr_new($options, $private_key, $config_args_csr);
821
        $x509        = openssl_csr_sign($csr, null, $private_key, $days = 3650, $config_args_csr);
822
823
        // Экспорт ключей.
824
        openssl_x509_export($x509, $certout);
825
        openssl_pkey_export($private_key, $pkeyout);
826
        // echo $pkeyout; // -> WEBHTTPSPrivateKey
827
        // echo $certout; // -> WEBHTTPSPublicKey
828
        return ['PublicKey' => $certout, 'PrivateKey' => $pkeyout];
829
    }
830
831
    /**
832
     * @return bool
833
     */
834
    public static function isSystemctl(): bool
835
    {
836
        return (stripos(php_uname('v'), 'debian') !== false);
837
    }
838
839
    /**
840
     * Выводить текстовое сообщение "done" подсвечивает зеленым цветом.
841
     */
842
    public static function echoGreenDone(): void
843
    {
844
        echo "\033[32;1mdone\033[0m \n";
845
    }
846
847
    /**
848
     * Создание символической ссылки, если необходимо.
849
     *
850
     * @param $target
851
     * @param $link
852
     *
853
     * @return bool
854
     */
855
    public static function createUpdateSymlink($target, $link): bool
856
    {
857
        $need_create_link = true;
858
        if (is_link($link)) {
859
            $old_target       = readlink($link);
860
            $need_create_link = ($old_target != $target);
861
            // Если необходимо, удаляем старую ссылку.
862
            if ($need_create_link) {
863
                $cpPath = self::which('cp');
864
                self::mwExec("{$cpPath} {$old_target}/* {$target}");
865
                unlink($link);
866
            }
867
        } elseif (is_dir($link)) {
868
            // Это должна быть именно ссылка. Файл удаляем.
869
            rmdir($link);
870
        } elseif (file_exists($link)) {
871
            // Это должна быть именно ссылка. Файл удаляем.
872
            unlink($link);
873
        }
874
        self::mwMkdir($target);
875
        if ($need_create_link) {
876
            $lnPath = self::which('ln');
877
            self::mwExec("{$lnPath} -s {$target}  {$link}");
878
        }
879
880
        return $need_create_link;
881
    }
882
883
    /**
884
     * Print message and write it to syslog
885
     *
886
     * @param $message
887
     */
888
    public static function echoWithSyslog($message): void
889
    {
890
        echo $message;
891
        self::sysLogMsg(static::class, $message, LOG_INFO);
892
    }
893
894
    /**
895
     * Добавляем задачу для уведомлений.
896
     *
897
     * @param string $tube
898
     * @param        $data
899
     */
900
    public function addJobToBeanstalk(string $tube, $data): void
901
    {
902
        $queue = new BeanstalkClient($tube);
903
        $queue->publish(json_encode($data));
904
    }
905
906
    /**
907
     * Apply regular rights for folders and files
908
     *
909
     * @param $folder
910
     */
911
    public static function addRegularWWWRights($folder): void
912
    {
913
        if (posix_getuid() === 0) {
914
            $findPath  = self::which('find');
915
            $chownPath = self::which('chown');
916
            $chmodPath = self::which('chmod');
917
            self::mwExec("{$findPath} {$folder} -type d -exec {$chmodPath} 755 {} \;");
918
            self::mwExec("{$findPath} {$folder} -type f -exec {$chmodPath} 644 {} \;");
919
            self::mwExec("{$chownPath} -R www:www {$folder}");
920
        }
921
    }
922
923
    /**
924
     * Apply executable rights for files
925
     *
926
     * @param $folder
927
     */
928
    public static function addExecutableRights($folder): void
929
    {
930
        if (posix_getuid() === 0) {
931
            $findPath  = self::which('find');
932
            $chmodPath = self::which('chmod');
933
            self::mwExec("{$findPath} {$folder} -type f -exec {$chmodPath} 755 {} \;");
934
        }
935
    }
936
937
    /**
938
     * Разбор INI конфига
939
     *
940
     * @param string $manual_attributes
941
     *
942
     * @return array
943
     */
944
    public static function parseIniSettings(string $manual_attributes): array
945
    {
946
        $tmp_data = base64_decode($manual_attributes);
947
        if (base64_encode($tmp_data) === $manual_attributes) {
948
            $manual_attributes = $tmp_data;
949
        }
950
        unset($tmp_data);
951
        // TRIMMING
952
        $tmp_arr = explode("\n", $manual_attributes);
953
        foreach ($tmp_arr as &$row) {
954
            $row = trim($row);
955
            $pos = strpos($row, ']');
956
            if ($pos !== false && strpos($row, '[') === 0) {
957
                $row = "\n" . substr($row, 0, $pos);
958
            }
959
        }
960
        unset($row);
961
        $manual_attributes = implode("\n", $tmp_arr);
962
        // TRIMMING END
963
964
        $manual_data = [];
965
        $sections    = explode("\n[", str_replace(']', '', $manual_attributes));
966
        foreach ($sections as $section) {
967
            $data_rows    = explode("\n", trim($section));
968
            $section_name = trim($data_rows[0] ?? '');
969
            if ( ! empty($section_name)) {
970
                unset($data_rows[0]);
971
                $manual_data[$section_name] = [];
972
                foreach ($data_rows as $row) {
973
                    if (strpos($row, '=') === false) {
974
                        continue;
975
                    }
976
                    $key = '';
977
                    $arr_value = explode('=', $row);
978
                    if (count($arr_value) > 1) {
979
                        $key = trim($arr_value[0]);
980
                        unset($arr_value[0]);
981
                        $value = trim(implode('=', $arr_value));
982
                    }
983
                    if (empty($value) || empty($key)) {
984
                        continue;
985
                    }
986
                    $manual_data[$section_name][$key] = $value;
987
                }
988
            }
989
        }
990
991
        return $manual_data;
992
    }
993
994
    /**
995
     * Converts multidimensional array into single array
996
     * @param $array
997
     *
998
     * @return array
999
     */
1000
    public static function flattenArray(array $array) {
1001
        $result = [];
1002
        foreach ($array as $value) {
1003
            if (is_array($value)) {
1004
                $result = array_merge($result, self::flattenArray($value));
1005
            } else {
1006
                $result[] = $value;
1007
            }
1008
        }
1009
        return $result;
1010
    }
1011
}