Test Setup Failed
Push — develop ( 09a6a8...5231a1 )
by Nikolay
10:01 queued 11s
created

Util::restartPHPWorker()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
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, 9 2020
7
 */
8
9
namespace MikoPBX\Core\System;
10
11
use DateTime;
12
use Exception;
13
use MikoPBX\Common\Models\{CallEventsLogs, CustomFiles};
14
use MikoPBX\Core\Asterisk\AsteriskManager;
15
use Phalcon\Di;
16
use ReflectionClass;
17
18
/**
19
 * Universal commands and procedures
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
     * Kills process/daemon by name
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
     * Executes command exec().
72
     *
73
     * @param $command
74
     * @param $outArr
75
     * @param $retVal
76
     *
77
     * @return int
78
     */
79
    public static function mwExec($command, &$outArr = null, &$retVal = null): int
80
    {
81
        $retVal = 0;
82
        $outArr   = [];
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", $outArr, $retVal);
89
        }
90
91
        return $retVal;
92
    }
93
94
    /**
95
     * Executes command exec() as background process.
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
     * Executes command exec() as background process with an execution timeout.
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
     * Executes multiple commands.
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
                        self::sysLogMsg('Util', 'Error on create folder '.$path);
183
                    }
184
                    if ($addWWWRights) {
185
                        self::addRegularWWWRights($path);
186
                    }
187
                }
188
            }
189
        }
190
        return $result;
191
    }
192
193
    /**
194
     * Restart PHP workers
195
     *
196
     * @param string $className
197
     * @param string $param
198
     */
199
    public static function restartPHPWorker(string $className, string $param = 'start'): void
200
    {
201
        $workerPath = self::getFilePathByClassName($className);
202
        if ( ! empty($workerPath)) {
203
            $command = "php -f {$workerPath}";
204
            self::processWorker($command, $param, $className, 'restart');
205
        }
206
    }
207
208
    /**
209
     * Try to find full path to php file by class name
210
     *
211
     * @param $className
212
     *
213
     * @return string|null
214
     */
215
    public static function getFilePathByClassName($className): ?string
216
    {
217
        $filename = null;
218
        try {
219
            $reflection = new ReflectionClass($className);
220
            $filename   = $reflection->getFileName();
221
        } catch (\ReflectionException $exception) {
222
            self::sysLogMsg('Util', 'Error ' . $exception->getMessage());
223
        }
224
225
        return $filename;
226
    }
227
228
    /**
229
     * Add message to Syslog.
230
     *
231
     * @param     $log_name
232
     * @param     $text
233
     * @param ?int $level
234
     * @param ?int $facility
235
     */
236
    public static function sysLogMsg($log_name, $text, $level = null, $facility=LOG_AUTH): void
237
    {
238
        $level = ($level === null) ? LOG_WARNING : $level;
239
        openlog("$log_name", LOG_PID | LOG_PERROR, $facility);
240
        syslog($level, "$text");
241
        closelog();
242
    }
243
244
    /**
245
     * Manages a daemon/worker process
246
     * Returns process statuses by name of it
247
     *
248
     * @param $cmd
249
     * @param $param
250
     * @param $proc_name
251
     * @param $action
252
     * @param $out_file
253
     *
254
     * @return array | bool
255
     */
256
    public static function processWorker($cmd, $param, $proc_name, $action, $out_file = '/dev/null')
257
    {
258
        $path_kill  = self::which('kill');
259
        $path_nohup = self::which('nohup');
260
261
        $WorkerPID = self::getPidOfProcess($proc_name);
262
263
         switch ($action) {
264
             case 'status':
265
                 $status = ($WorkerPID !== '') ? 'Started' : 'Stoped';
266
                 return ['status' => $status, 'app' => $proc_name, 'PID' => $WorkerPID];
267
             case 'restart':
268
                 // Firstly start new process
269
                 self::mwExec("{$path_nohup} {$cmd} {$param}  > {$out_file} 2>&1 &");
270
                 // Then kill the old one
271
                 if ($WorkerPID !== '') {
272
                     self::mwExec("{$path_kill} -9 {$WorkerPID}  > /dev/null 2>&1 &");
273
                 }
274
                 break;
275
             case 'stop':
276
                 if ($WorkerPID !== '') {
277
                     self::mwExec("{$path_kill} -9 {$WorkerPID}  > /dev/null 2>&1 &");
278
                 }
279
                 break;
280
             case 'start':
281
                 if ($WorkerPID === '') {
282
                     self::mwExec("{$path_nohup} {$cmd} {$param}  > {$out_file} 2>&1 &");
283
                 }
284
                 break;
285
             default:
286
         }
287
288
        return true;
289
    }
290
291
    /**
292
     * Return full path to executable binary
293
     *
294
     * @param string $cmd - name of file
295
     *
296
     * @return string
297
     */
298
    public static function which(string $cmd): string
299
    {
300
        global $_ENV;
301
        if (array_key_exists('PATH', $_ENV)) {
302
            $binaryFolders = $_ENV['PATH'];
303
304
            foreach (explode(':', $binaryFolders) as $path) {
305
                if (is_executable("{$path}/{$cmd}")) {
306
                    return "{$path}/{$cmd}";
307
                }
308
            }
309
        }
310
        $binaryFolders =
311
            [
312
                '/bin',
313
                '/sbin',
314
                '/usr/bin',
315
                '/usr/sbin',
316
                '/usr/local/bin',
317
                '/usr/local/sbin',
318
            ];
319
        foreach ($binaryFolders as $path) {
320
            if (is_executable("{$path}/{$cmd}")) {
321
                return "{$path}/{$cmd}";
322
            }
323
        }
324
325
        return $cmd;
326
    }
327
328
    /**
329
     * Возвращает PID процесса по его имени.
330
     *
331
     * @param        $name
332
     * @param string $exclude
333
     *
334
     * @return string
335
     */
336
    public static function getPidOfProcess($name, $exclude = ''): string
337
    {
338
        $path_ps   = self::which('ps');
339
        $path_grep = self::which('grep');
340
        $path_awk  = self::which('awk');
341
342
        $name       = addslashes($name);
343
        $filter_cmd = '';
344
        if ( ! empty($exclude)) {
345
            $filter_cmd = "| $path_grep -v " . escapeshellarg($exclude);
346
        }
347
        $out = [];
348
        self::mwExec(
349
            "{$path_ps} -A -o 'pid,args' {$filter_cmd} | {$path_grep} '{$name}' | {$path_grep} -v grep | {$path_awk} ' {print $1} '",
350
            $out
351
        );
352
353
        return trim(implode(' ', $out));
354
    }
355
356
    /**
357
     * Инициация телефонного звонка.
358
     * @param $peer_number
359
     * @param $peer_mobile
360
     * @param $dest_number
361
     * @return array
362
     * @throws Exception
363
     */
364
    public static function amiOriginate($peer_number, $peer_mobile, $dest_number): array
365
    {
366
        $am       = self::getAstManager('off');
367
        $channel  = 'Local/' . $peer_number . '@internal-originate';
368
        $context  = 'all_peers';
369
        $IS_ORGNT = self::generateRandomString();
370
        $variable = "_IS_ORGNT={$IS_ORGNT},pt1c_cid={$dest_number},_extenfrom1c={$peer_number},__peer_mobile={$peer_mobile},_FROM_PEER={$peer_number}";
371
372
        return $am->Originate($channel,
373
                              $dest_number,
374
                              $context,
375
                      '1', null, null, null, null, $variable, null, true);
376
    }
377
378
    /**
379
     * Получаем объект менеджер asterisk.
380
     *
381
     * @param string $events
382
     *
383
     * @return AsteriskManager
384
     */
385
    public static function getAstManager($events = 'on'): AsteriskManager
386
    {
387
        if($events === 'on'){
388
            $nameService = 'amiListner';
389
        }else{
390
            $nameService = 'amiCommander';
391
        }
392
393
        $di = Di::getDefault();
394
        $am = $di->getShared($nameService);
395
        if (is_resource($am->socket)) {
396
            $res = $am->sendRequestTimeout('Ping');
397
            if (isset($res['Response']) && trim($res['Response']) != '') {
398
                // Уже есть подключенный экземпляр класса.
399
                return $am;
400
            }
401
        }
402
403
        return $di->get($nameService);
404
    }
405
406
    /**
407
     * Генератор произвольной строки.
408
     * @param int $length
409
     * @return string
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
            try {
418
                $randomString .= $characters[random_int(0, $charactersLength - 1)];
419
            }catch (\Exception $e ){
420
                $randomString = '';
421
            }
422
        }
423
424
        return $randomString;
425
    }
426
427
    /**
428
     * Json validate
429
     *
430
     * @param $jsonString
431
     *
432
     * @return bool
433
     */
434
    public static function isJson($jsonString): bool
435
    {
436
        json_decode($jsonString, true);
437
438
        return (json_last_error() === JSON_ERROR_NONE);
439
    }
440
441
    /**
442
     *  Возвращает размер файла в Мб.
443
     *
444
     * @param $filename
445
     *
446
     * @return float|int
447
     */
448
    public static function mFileSize($filename)
449
    {
450
        $size = 0;
451
        if (file_exists($filename)) {
452
            $tmp_size = filesize($filename);
453
            if ($tmp_size !== false) {
454
                // Получим размер в Мб.
455
                $size = $tmp_size;
456
            }
457
        }
458
459
        return $size;
460
    }
461
462
    /**
463
     * Возвращает указанное количество X.
464
     *
465
     * @param $length
466
     *
467
     * @return string
468
     */
469
    public static function getExtensionX($length): string
470
    {
471
        $extension = '';
472
        for ($i = 0; $i < $length; $i++) {
473
            $extension .= 'X';
474
        }
475
476
        return $extension;
477
    }
478
479
    /**
480
     * Проверяет существование файла.
481
     *
482
     * @param $filename
483
     *
484
     * @return bool
485
     */
486
    public static function recFileExists($filename): ?bool
487
    {
488
        return (file_exists($filename) && filesize($filename) > 0);
489
    }
490
491
    /**
492
     * Если переданный параметр - число, то будет возвращена дата.
493
     *
494
     * @param $data
495
     *
496
     * @return string
497
     */
498
    public static function numberToDate($data): string
499
    {
500
        $re_number = '/^\d+.\d+$/';
501
        preg_match_all($re_number, $data, $matches, PREG_SET_ORDER, 0);
502
        if (count($matches) > 0) {
503
            $data = date('Y.m.d-H:i:s', $data);
504
        }
505
506
        return $data;
507
    }
508
509
    /**
510
     * Записывает данные в файл.
511
     *
512
     * @param $filename
513
     * @param $data
514
     */
515
    public static function fileWriteContent($filename, $data): void
516
    {
517
        /** @var CustomFiles $res */
518
        $res = CustomFiles::findFirst("filepath = '{$filename}'");
519
520
        $filename_orgn = "{$filename}.orgn";
521
        if (($res === null || $res->mode === 'none') && file_exists($filename_orgn)) {
522
            unlink($filename_orgn);
523
        } elseif ($res !== null && $res->mode !== 'none') {
524
            // Запишем оригинальный файл.
525
            file_put_contents($filename_orgn, $data);
526
        }
527
528
        if ($res === null) {
529
            // Файл еще не зарегистрирован в базе. Сделаем это.
530
            $res = new CustomFiles();
531
            $res->writeAttribute('filepath', $filename);
532
            $res->writeAttribute('mode', 'none');
533
            $res->save();
534
        } elseif ($res->mode === 'append') {
535
            // Добавить к файлу.
536
            $data .= "\n\n";
537
            $data .= base64_decode($res->content);
538
        } elseif ($res->mode === 'override') {
539
            // Переопределить файл.
540
            $data = base64_decode($res->content);
541
        }
542
        file_put_contents($filename, $data);
543
    }
544
545
546
547
    /**
548
     * Пишем лог в базу данных.
549
     *
550
     * @param $app
551
     * @param $data_obj
552
     */
553
    public static function logMsgDb($app, $data_obj): void
554
    {
555
        try {
556
            $data = new CallEventsLogs();
557
            $data->writeAttribute('eventtime', date("Y-m-d H:i:s"));
558
            $data->writeAttribute('app', $app);
559
            $data->writeAttribute('datajson', json_encode($data_obj, JSON_UNESCAPED_SLASHES));
560
561
            if (is_array($data_obj) && isset($data_obj['linkedid'])) {
562
                $data->writeAttribute('linkedid', $data_obj['linkedid']);
563
            }
564
            $data->save();
565
        } catch (Exception $e) {
566
            self::sysLogMsg('logMsgDb', $e->getMessage());
567
        }
568
    }
569
570
    /**
571
     * Возвращает текущую дату в виде строки с точностью до милисекунд.
572
     *
573
     * @return string
574
     */
575
    public static function getNowDate(): ?string
576
    {
577
        $result = null;
578
        try {
579
            $d      = new DateTime();
580
            $result = $d->format("Y-m-d H:i:s.v");
581
        } catch (Exception $e) {
582
            unset($e);
583
        }
584
585
        return $result;
586
    }
587
588
589
590
    /**
591
     * Получает расширение файла.
592
     *
593
     * @param        $filename
594
     * @return mixed
595
     */
596
    public static function getExtensionOfFile($filename)
597
    {
598
        $path_parts = pathinfo($filename);
599
        return $path_parts['extension']??'';
600
    }
601
602
    /**
603
     * Удаляет расширение файла.
604
     *
605
     * @param        $filename
606
     * @param string $delimiter
607
     *
608
     * @return string
609
     */
610
    public static function trimExtensionForFile($filename, $delimiter = '.'): string
611
    {
612
        // Отсечем расширение файла.
613
        $tmp_arr = explode((string)$delimiter, $filename);
614
        if (count($tmp_arr) > 1) {
615
            unset($tmp_arr[count($tmp_arr) - 1]);
616
            $filename = implode((string)$delimiter, $tmp_arr);
617
        }
618
619
        return $filename;
620
    }
621
622
623
624
    /**
625
     * Получаем размер файла / директории.
626
     *
627
     * @param $filename
628
     *
629
     * @return float
630
     */
631
    public static function getSizeOfFile($filename): float
632
    {
633
        $result = 0;
634
        if (file_exists($filename)) {
635
            $duPath  = self::which('du');
636
            $awkPath = self::which('awk');
637
            self::mwExec("{$duPath} -d 0 -k '{$filename}' | {$awkPath}  '{ print $1}'", $out);
638
            $time_str = implode($out);
639
            preg_match_all('/^\d+$/', $time_str, $matches, PREG_SET_ORDER, 0);
640
            if (count($matches) > 0) {
641
                $result = round(1 * $time_str / 1024, 2);
642
            }
643
        }
644
645
        return $result;
646
    }
647
648
    /**
649
     * Устанавливаем шрифт для консоли.
650
     */
651
    public static function setCyrillicFont(): void
652
    {
653
        $setfontPath = self::which('setfont');
654
        self::mwExec("{$setfontPath} /usr/share/consolefonts/Cyr_a8x16.psfu.gz 2>/dev/null");
655
    }
656
657
    /**
658
     * Получить перевод строки текста.
659
     *
660
     * @param $text
661
     *
662
     * @return mixed
663
     */
664
    public static function translate($text)
665
    {
666
        $di = Di::getDefault();
667
        if ($di !== null) {
668
            return $di->getShared('translation')->_($text);
669
        } else {
670
            return $text;
671
        }
672
    }
673
674
    /**
675
     *
676
     * Delete a directory RECURSIVELY
677
     *
678
     * @param string $dir - directory path
679
     *
680
     * @link http://php.net/manual/en/function.rmdir.php
681
     */
682
    public static function rRmDir(string $dir): void
683
    {
684
        if (is_dir($dir)) {
685
            $objects = scandir($dir);
686
            foreach ($objects as $object) {
687
                if ($object != "." && $object != "..") {
688
                    if (filetype($dir . "/" . $object) == "dir") {
689
                        self::rRmDir($dir . "/" . $object);
690
                    } else {
691
                        unlink($dir . "/" . $object);
692
                    }
693
                }
694
            }
695
            if($objects !== false){
696
                reset($objects);
697
            }
698
            rmdir($dir);
699
        }
700
    }
701
702
    /**
703
     * Генерация сертификата средствами openssl.
704
     *
705
     * @param ?array $options
706
     * @param ?array $config_args_pkey
707
     * @param ?array $config_args_csr
708
     *
709
     * @return array
710
     */
711
    public static function generateSslCert($options = null, $config_args_pkey = null, $config_args_csr = null): array
712
    {
713
        // Инициализация настроек.
714
        if ( ! $options) {
715
            $options = [
716
                "countryName"            => 'RU',
717
                "stateOrProvinceName"    => 'Moscow',
718
                "localityName"           => 'Zelenograd',
719
                "organizationName"       => 'MIKO LLC',
720
                "organizationalUnitName" => 'Software development',
721
                "commonName"             => 'MIKO PBX',
722
                "emailAddress"           => '[email protected]',
723
            ];
724
        }
725
726
        if ( ! $config_args_csr) {
727
            $config_args_csr = ['digest_alg' => 'sha256'];
728
        }
729
730
        if ( ! $config_args_pkey) {
731
            $config_args_pkey = [
732
                "private_key_bits" => 2048,
733
                "private_key_type" => OPENSSL_KEYTYPE_RSA,
734
            ];
735
        }
736
737
        // Генерация ключей.
738
        $private_key = openssl_pkey_new($config_args_pkey);
739
        $csr         = openssl_csr_new($options, $private_key, $config_args_csr);
740
        $x509        = openssl_csr_sign($csr, null, $private_key, $days = 3650, $config_args_csr);
741
742
        // Экспорт ключей.
743
        openssl_x509_export($x509, $certout);
744
        openssl_pkey_export($private_key, $pkeyout);
745
        // echo $pkeyout; // -> WEBHTTPSPrivateKey
746
        // echo $certout; // -> WEBHTTPSPublicKey
747
        return ['PublicKey' => $certout, 'PrivateKey' => $pkeyout];
748
    }
749
750
    /**
751
     * @return bool
752
     */
753
    public static function isSystemctl(): bool
754
    {
755
        return (stripos(php_uname('v'), 'debian') !== false);
756
    }
757
758
    /**
759
     * Выводить текстовое сообщение "done" подсвечивает зеленым цветом.
760
     */
761
    public static function echoGreenDone(): void
762
    {
763
        echo "\033[32;1mdone\033[0m \n";
764
    }
765
766
    /**
767
     * Создание символической ссылки, если необходимо.
768
     *
769
     * @param $target
770
     * @param $link
771
     *
772
     * @return bool
773
     */
774
    public static function createUpdateSymlink($target, $link): bool
775
    {
776
        $need_create_link = true;
777
        if (is_link($link)) {
778
            $old_target       = readlink($link);
779
            $need_create_link = ($old_target != $target);
780
            // Если необходимо, удаляем старую ссылку.
781
            if ($need_create_link) {
782
                $cpPath = self::which('cp');
783
                self::mwExec("{$cpPath} {$old_target}/* {$target}");
784
                unlink($link);
785
            }
786
        } elseif (is_dir($link)) {
787
            // Это должна быть именно ссылка. Файл удаляем.
788
            rmdir($link);
789
        } elseif (file_exists($link)) {
790
            // Это должна быть именно ссылка. Файл удаляем.
791
            unlink($link);
792
        }
793
        self::mwMkdir($target);
794
        if ($need_create_link) {
795
            $lnPath = self::which('ln');
796
            self::mwExec("{$lnPath} -s {$target}  {$link}");
797
        }
798
799
        return $need_create_link;
800
    }
801
802
    /**
803
     * Print message and write it to syslog
804
     *
805
     * @param $message
806
     */
807
    public static function echoWithSyslog($message): void
808
    {
809
        echo $message;
810
        self::sysLogMsg(static::class, $message, LOG_INFO);
811
    }
812
813
    /**
814
     * Добавляем задачу для уведомлений.
815
     *
816
     * @param string $tube
817
     * @param        $data
818
     */
819
    public function addJobToBeanstalk(string $tube, $data): void
820
    {
821
        $queue = new BeanstalkClient($tube);
822
        $queue->publish(json_encode($data));
823
    }
824
825
    /**
826
     * Apply regular rights for folders and files
827
     *
828
     * @param $folder
829
     */
830
    public static function addRegularWWWRights($folder): void
831
    {
832
        if (posix_getuid() === 0) {
833
            $findPath  = self::which('find');
834
            $chownPath = self::which('chown');
835
            $chmodPath = self::which('chmod');
836
            self::mwExec("{$findPath} {$folder} -type d -exec {$chmodPath} 755 {} \;");
837
            self::mwExec("{$findPath} {$folder} -type f -exec {$chmodPath} 644 {} \;");
838
            self::mwExec("{$chownPath} -R www:www {$folder}");
839
        }
840
    }
841
842
    /**
843
     * Apply executable rights for files
844
     *
845
     * @param $folder
846
     */
847
    public static function addExecutableRights($folder): void
848
    {
849
        if (posix_getuid() === 0) {
850
            $findPath  = self::which('find');
851
            $chmodPath = self::which('chmod');
852
            self::mwExec("{$findPath} {$folder} -type f -exec {$chmodPath} 755 {} \;");
853
        }
854
    }
855
856
    /**
857
     * Разбор INI конфига
858
     *
859
     * @param string $manual_attributes
860
     *
861
     * @return array
862
     */
863
    public static function parseIniSettings(string $manual_attributes): array
864
    {
865
        $tmp_data = base64_decode($manual_attributes);
866
        if (base64_encode($tmp_data) === $manual_attributes) {
867
            $manual_attributes = $tmp_data;
868
        }
869
        unset($tmp_data);
870
        // TRIMMING
871
        $tmp_arr = explode("\n", $manual_attributes);
872
        foreach ($tmp_arr as &$row) {
873
            $row = trim($row);
874
            $pos = strpos($row, ']');
875
            if ($pos !== false && strpos($row, '[') === 0) {
876
                $row = "\n" . substr($row, 0, $pos);
877
            }
878
        }
879
        unset($row);
880
        $manual_attributes = implode("\n", $tmp_arr);
881
        // TRIMMING END
882
883
        $manual_data = [];
884
        $sections    = explode("\n[", str_replace(']', '', $manual_attributes));
885
        foreach ($sections as $section) {
886
            $data_rows    = explode("\n", trim($section));
887
            $section_name = trim($data_rows[0] ?? '');
888
            if ( ! empty($section_name)) {
889
                unset($data_rows[0]);
890
                $manual_data[$section_name] = [];
891
                foreach ($data_rows as $row) {
892
                    if (strpos($row, '=') === false) {
893
                        continue;
894
                    }
895
                    $key = '';
896
                    $arr_value = explode('=', $row);
897
                    if (count($arr_value) > 1) {
898
                        $key = trim($arr_value[0]);
899
                        unset($arr_value[0]);
900
                        $value = trim(implode('=', $arr_value));
901
                    }
902
                    if (empty($value) || empty($key)) {
903
                        continue;
904
                    }
905
                    $manual_data[$section_name][$key] = $value;
906
                }
907
            }
908
        }
909
910
        return $manual_data;
911
    }
912
913
    /**
914
     * Converts multidimensional array into single array
915
     * @param $array
916
     *
917
     * @return array
918
     */
919
    public static function flattenArray(array $array) {
920
        $result = [];
921
        foreach ($array as $value) {
922
            if (is_array($value)) {
923
                $result = array_merge($result, self::flattenArray($value));
924
            } else {
925
                $result[] = $value;
926
            }
927
        }
928
        return $result;
929
    }
930
}