Passed
Push — develop ( cd3e4b...b998ca )
by Портнов
05:16
created

Util::trimExtensionForFile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright (C) 2017-2020 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\System;
21
22
use DateTime;
23
use Exception;
24
use MikoPBX\Common\Models\{CustomFiles};
25
use MikoPBX\Common\Providers\AmiConnectionCommand;
26
use MikoPBX\Common\Providers\AmiConnectionListener;
27
use MikoPBX\Common\Providers\LoggerProvider;
28
use MikoPBX\Common\Providers\TranslationProvider;
29
use MikoPBX\Core\Asterisk\AsteriskManager;
30
use Phalcon\Di;
31
use ReflectionClass;
32
use ReflectionException;
33
use Throwable;
34
35
/**
36
 * Universal commands and procedures
37
 */
38
class Util
39
{
40
41
    /**
42
     * @param $options
43
     * @param $manual_attributes
44
     * @param $section
45
     *
46
     * @return string
47
     */
48
    public static function overrideConfigurationArray($options, $manual_attributes, $section): string
49
    {
50
        $result_config = '';
51
        if ($manual_attributes !== null && isset($manual_attributes[$section])) {
52
            foreach ($manual_attributes[$section] as $key => $value) {
53
                if ($key === 'type') {
54
                    continue;
55
                }
56
                $options[$key] = $value;
57
            }
58
        }
59
        foreach ($options as $key => $value) {
60
            if (empty($value) || empty($key)) {
61
                continue;
62
            }
63
            if (is_array($value)) {
64
                array_unshift($value, ' ');
65
                $result_config .= trim(implode("\n{$key} = ", $value)) . "\n";
66
            } else {
67
                $result_config .= "{$key} = {$value}\n";
68
            }
69
        }
70
71
        return "$result_config\n";
72
    }
73
74
    /**
75
     * Инициация телефонного звонка.
76
     *
77
     * @param $peer_number
78
     * @param $peer_mobile
79
     * @param $dest_number
80
     *
81
     * @return array
82
     * @throws Exception
83
     */
84
    public static function amiOriginate($peer_number, $peer_mobile, $dest_number): array
85
    {
86
        $am       = self::getAstManager('off');
87
        $channel  = 'Local/' . $peer_number . '@internal-originate';
88
        $context  = 'all_peers';
89
        $variable = "pt1c_cid={$dest_number},__peer_mobile={$peer_mobile}";
90
91
        return $am->Originate(
92
            $channel,
93
            $dest_number,
94
            $context,
95
            '1',
96
            null,
97
            null,
98
            null,
99
            null,
100
            $variable,
101
            null,
102
            true
103
        );
104
    }
105
106
    /**
107
     * Получаем объект менеджер asterisk.
108
     * @param string $events
109
     * @return AsteriskManager
110
     * @throws \Phalcon\Exception
111
     */
112
    public static function getAstManager(string $events = 'on'): AsteriskManager
113
    {
114
        if ($events === 'on') {
115
            $nameService = AmiConnectionListener::SERVICE_NAME;
116
        } else {
117
            $nameService = AmiConnectionCommand::SERVICE_NAME;
118
        }
119
        $di = Di::getDefault();
120
        if($di === null) {
121
            throw new \Phalcon\Exception("di not found");
122
        }
123
        $am = $di->getShared($nameService);
124
        if (is_resource($am->socket)) {
125
            return $am;
126
        }
127
128
        return $di->get($nameService);
129
    }
130
131
    /**
132
     * Генератор произвольной строки.
133
     *
134
     * @param int $length
135
     *
136
     * @return string
137
     */
138
    public static function generateRandomString($length = 10): string
139
    {
140
        $characters       = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
141
        $charactersLength = strlen($characters);
142
        $randomString     = '';
143
        for ($i = 0; $i < $length; $i++) {
144
            try {
145
                $randomString .= $characters[random_int(0, $charactersLength - 1)];
146
            } catch (Throwable $e) {
147
                $randomString = '';
148
            }
149
        }
150
151
        return $randomString;
152
    }
153
154
    /**
155
     * Json validate
156
     *
157
     * @param $jsonString
158
     *
159
     * @return bool
160
     */
161
    public static function isJson($jsonString): bool
162
    {
163
        json_decode($jsonString, true);
164
165
        return (json_last_error() === JSON_ERROR_NONE);
166
    }
167
168
    /**
169
     *  Возвращает размер файла в Мб.
170
     *
171
     * @param $filename
172
     *
173
     * @return float|int
174
     */
175
    public static function mFileSize($filename)
176
    {
177
        $size = 0;
178
        if (file_exists($filename)) {
179
            $tmp_size = filesize($filename);
180
            if ($tmp_size !== false) {
181
                // Получим размер в Мб.
182
                $size = $tmp_size;
183
            }
184
        }
185
186
        return $size;
187
    }
188
189
    /**
190
     * Возвращает указанное количество X.
191
     *
192
     * @param $length
193
     *
194
     * @return string
195
     */
196
    public static function getExtensionX($length): string
197
    {
198
        $extension = '';
199
        for ($i = 0; $i < $length; $i++) {
200
            $extension .= 'X';
201
        }
202
203
        return $extension;
204
    }
205
206
    /**
207
     * Проверяет существование файла.
208
     *
209
     * @param $filename
210
     *
211
     * @return bool
212
     */
213
    public static function recFileExists($filename): ?bool
214
    {
215
        return (file_exists($filename) && filesize($filename) > 0);
216
    }
217
218
    /**
219
     * Если переданный параметр - число, то будет возвращена дата.
220
     *
221
     * @param $data
222
     *
223
     * @return string
224
     */
225
    public static function numberToDate($data): string
226
    {
227
        $re_number = '/^\d+.\d+$/';
228
        preg_match_all($re_number, $data, $matches, PREG_SET_ORDER, 0);
229
        if (count($matches) > 0) {
230
            $data = date('Y.m.d-H:i:s', $data);
231
        }
232
233
        return $data;
234
    }
235
236
    /**
237
     * Записывает данные в файл.
238
     *
239
     * @param $filename
240
     * @param $data
241
     */
242
    public static function fileWriteContent($filename, $data): void
243
    {
244
        /** @var CustomFiles $res */
245
        $res = CustomFiles::findFirst("filepath = '{$filename}'");
246
247
        $filename_orgn = "{$filename}.orgn";
248
        if (($res === null || $res->mode === 'none') && file_exists($filename_orgn)) {
249
            unlink($filename_orgn);
250
        } elseif ($res !== null && $res->mode !== 'none') {
251
            // Запишем оригинальный файл.
252
            file_put_contents($filename_orgn, $data);
253
        }
254
255
        if ($res === null) {
256
            // Файл еще не зарегистрирован в базе. Сделаем это.
257
            $res = new CustomFiles();
258
            $res->writeAttribute('filepath', $filename);
259
            $res->writeAttribute('mode', 'none');
260
            $res->save();
261
        } elseif ($res->mode === 'append') {
262
            // Добавить к файлу.
263
            $data .= "\n\n";
264
            $data .= base64_decode((string)$res->content);
265
        } elseif ($res->mode === 'override') {
266
            // Переопределить файл.
267
            $data = base64_decode((string)$res->content);
268
        }
269
        file_put_contents($filename, $data);
270
    }
271
272
    /**
273
     * Adds messages to Syslog.
274
     *
275
     * @param string $ident category, class, method
276
     * @param string $message log message
277
     * @param int $level log level https://docs.phalcon.io/4.0/en/logger#constants
278
     *
279
     */
280
    public static function sysLogMsg(string $ident, string $message, $level = LOG_WARNING): void
281
    {
282
        /** @var \Phalcon\Logger $logger */
283
        $logger = Di::getDefault()->getShared(LoggerProvider::SERVICE_NAME);
284
        $logger->log($level, "{$message} on {$ident}" );
285
    }
286
287
    /**
288
     * Возвращает текущую дату в виде строки с точностью до милисекунд.
289
     *
290
     * @return string
291
     */
292
    public static function getNowDate(): ?string
293
    {
294
        $result = null;
295
        try {
296
            $d      = new DateTime();
297
            $result = $d->format("Y-m-d H:i:s.v");
298
        } catch (Exception $e) {
299
            unset($e);
300
        }
301
302
        return $result;
303
    }
304
305
    /**
306
     * Получает расширение файла.
307
     *
308
     * @param        $filename
309
     *
310
     * @return mixed
311
     */
312
    public static function getExtensionOfFile($filename)
313
    {
314
        $path_parts = pathinfo($filename);
315
316
        return $path_parts['extension'] ?? '';
317
    }
318
319
    /**
320
     * Удаляет расширение файла.
321
     *
322
     * @param        $filename
323
     * @param string $delimiter
324
     *
325
     * @return string
326
     */
327
    public static function trimExtensionForFile($filename, $delimiter = '.'): string
328
    {
329
        // Отсечем расширение файла.
330
        $tmp_arr = explode((string)$delimiter, $filename);
331
        if (count($tmp_arr) > 1) {
332
            unset($tmp_arr[count($tmp_arr) - 1]);
333
            $filename = implode((string)$delimiter, $tmp_arr);
334
        }
335
336
        return $filename;
337
    }
338
339
    /**
340
     * Получаем размер файла / директории.
341
     *
342
     * @param $filename
343
     *
344
     * @return float
345
     */
346
    public static function getSizeOfFile($filename): float
347
    {
348
        $result = 0;
349
        if (file_exists($filename)) {
350
            $duPath  = self::which('du');
351
            $awkPath = self::which('awk');
352
            Processes::mwExec("{$duPath} -d 0 -k '{$filename}' | {$awkPath}  '{ print $1}'", $out);
353
            $time_str = implode($out);
354
            preg_match_all('/^\d+$/', $time_str, $matches, PREG_SET_ORDER, 0);
355
            if (count($matches) > 0) {
356
                $result = round(1 * $time_str / 1024, 2);
357
            }
358
        }
359
360
        return $result;
361
    }
362
363
    /**
364
     * Return full path to executable binary
365
     *
366
     * @param string $cmd - name of file
367
     *
368
     * @return string
369
     */
370
    public static function which(string $cmd): string
371
    {
372
        global $_ENV;
373
        if (array_key_exists('PATH', $_ENV)) {
374
            $binaryFolders = $_ENV['PATH'];
375
376
            foreach (explode(':', $binaryFolders) as $path) {
377
                if (is_executable("{$path}/{$cmd}")) {
378
                    return "{$path}/{$cmd}";
379
                }
380
            }
381
        }
382
        $binaryFolders =
383
            [
384
                '/bin',
385
                '/sbin',
386
                '/usr/bin',
387
                '/usr/sbin',
388
                '/usr/local/bin',
389
                '/usr/local/sbin',
390
            ];
391
        foreach ($binaryFolders as $path) {
392
            if (is_executable("{$path}/{$cmd}")) {
393
                return "{$path}/{$cmd}";
394
            }
395
        }
396
397
        return $cmd;
398
    }
399
400
    /**
401
     * DEPRICATED
402
     * Executes command exec().
403
     *
404
     * @param $command
405
     * @param $outArr
406
     * @param $retVal
407
     *
408
     * @return int
409
     */
410
    public static function mwExec($command, &$outArr = null, &$retVal = null): int
411
    {
412
        self::sysLogMsg('Util', 'Deprecated call ' . __METHOD__ . ' from ' . static::class, LOG_DEBUG);
413
414
        return Processes::mwExec($command, $outArr, $retVal);
415
    }
416
417
    /**
418
     * Проверка сложности пароля по словарю.
419
     * @param $value
420
     * @return bool
421
     */
422
    public static function isSimplePassword($value): bool
423
    {
424
        $passwords = [];
425
        Processes::mwExec('/bin/zcat /usr/share/wordlists/rockyou.txt.gz', $passwords);
426
        return in_array($value, $passwords, true);
427
    }
428
429
    /**
430
     * Устанавливаем шрифт для консоли.
431
     */
432
    public static function setCyrillicFont(): void
433
    {
434
        $setfontPath = self::which('setfont');
435
        Processes::mwExec("{$setfontPath} /usr/share/consolefonts/Cyr_a8x16.psfu.gz 2>/dev/null");
436
    }
437
438
    /**
439
     * Получить перевод строки текста.
440
     * @param string $text
441
     * @param bool   $cliLang
442
     * @return string
443
     */
444
    public static function translate(string $text, bool $cliLang = true):string
445
    {
446
        $di = Di::getDefault();
447
        if ($di !== null) {
448
            if(!$cliLang){
449
                $di->setShared('PREFERRED_LANG_WEB', true);
450
            }
451
            $text = $di->getShared(TranslationProvider::SERVICE_NAME)->_($text);
452
            if(!$cliLang){
453
                $di->remove('PREFERRED_LANG_WEB');
454
            }
455
        }
456
        return $text;
457
    }
458
459
    /**
460
     *
461
     * Delete a directory RECURSIVELY
462
     *
463
     * @param string $dir - directory path
464
     *
465
     * @link http://php.net/manual/en/function.rmdir.php
466
     */
467
    public static function rRmDir(string $dir): void
468
    {
469
        if (is_dir($dir)) {
470
            $objects = scandir($dir);
471
            foreach ($objects as $object) {
472
                if ($object != "." && $object != "..") {
473
                    if (filetype($dir . "/" . $object) == "dir") {
474
                        self::rRmDir($dir . "/" . $object);
475
                    } else {
476
                        unlink($dir . "/" . $object);
477
                    }
478
                }
479
            }
480
            if ($objects !== false) {
481
                reset($objects);
482
            }
483
            rmdir($dir);
484
        }
485
    }
486
487
    /**
488
     * Генерация сертификата средствами openssl.
489
     *
490
     * @param ?array $options
491
     * @param ?array $config_args_pkey
492
     * @param ?array $config_args_csr
493
     *
494
     * @return array
495
     */
496
    public static function generateSslCert($options = null, $config_args_pkey = null, $config_args_csr = null): array
497
    {
498
        // Инициализация настроек.
499
        if ( ! $options) {
500
            $options = [
501
                "countryName"            => 'RU',
502
                "stateOrProvinceName"    => 'Moscow',
503
                "localityName"           => 'Zelenograd',
504
                "organizationName"       => 'MIKO LLC',
505
                "organizationalUnitName" => 'Software development',
506
                "commonName"             => 'MIKO PBX',
507
                "emailAddress"           => '[email protected]',
508
            ];
509
        }
510
511
        if ( ! $config_args_csr) {
512
            $config_args_csr = ['digest_alg' => 'sha256'];
513
        }
514
515
        if ( ! $config_args_pkey) {
516
            $config_args_pkey = [
517
                "private_key_bits" => 2048,
518
                "private_key_type" => OPENSSL_KEYTYPE_RSA,
519
            ];
520
        }
521
522
        // Генерация ключей.
523
        $private_key = openssl_pkey_new($config_args_pkey);
524
        $csr         = openssl_csr_new($options, $private_key, $config_args_csr);
0 ignored issues
show
Bug introduced by
It seems like $private_key can also be of type resource; however, parameter $private_key of openssl_csr_new() does only seem to accept OpenSSLAsymmetricKey, 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

524
        $csr         = openssl_csr_new($options, /** @scrutinizer ignore-type */ $private_key, $config_args_csr);
Loading history...
525
        $x509        = openssl_csr_sign($csr, null, $private_key, $days = 3650, $config_args_csr);
526
527
        // Экспорт ключей.
528
        openssl_x509_export($x509, $certout);
529
        openssl_pkey_export($private_key, $pkeyout);
530
        // echo $pkeyout; // -> WEBHTTPSPrivateKey
531
        // echo $certout; // -> WEBHTTPSPublicKey
532
        return ['PublicKey' => $certout, 'PrivateKey' => $pkeyout];
533
    }
534
535
    /**
536
     * @return bool
537
     */
538
    public static function isSystemctl(): bool
539
    {
540
        return (stripos(php_uname('v'), 'debian') !== false);
541
    }
542
543
    /**
544
     * @return bool
545
     */
546
    public static function isDocker(): bool
547
    {
548
        return file_exists('/.dockerenv');
549
    }
550
551
    /**
552
     * Вывод в основной teletype.
553
     * @param string $message
554
     * @param string $ttyPath
555
     * @return void
556
     */
557
    public static function teletypeEcho(string $message, string $ttyPath = '/dev/ttyS0'):void
558
    {
559
        $pathBusyBox    = self::which('busybox');
560
        $ttyTittle      = trim(shell_exec("$pathBusyBox setserial -g $ttyPath 2> /dev/null"));
561
        if(strpos($ttyTittle, $ttyPath) !== false && strpos($ttyTittle, 'unknown') === false){
562
            @file_put_contents($ttyPath, $message, FILE_APPEND);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for file_put_contents(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

562
            /** @scrutinizer ignore-unhandled */ @file_put_contents($ttyPath, $message, FILE_APPEND);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
563
        }
564
    }
565
566
    /**
567
     * Вывод DONE / FAIL основной teletype.
568
     * @param $result
569
     * @return void
570
     */
571
    public static function teletypeEchoDone(string $message, $result):void
572
    {
573
        $len    = max(0, 80 - strlen($message) - 9);
574
        $spaces = str_repeat('.', $len);
575
        if($result === false){
576
            $message = " \033[31;1mFAIL\033[0m \n";
577
        }else{
578
            $message = " \033[32;1mDONE\033[0m \n";
579
        }
580
        self::teletypeEcho($spaces.$message);
581
    }
582
583
    /**
584
     * Выводить текстовое сообщение "done" подсвечивает зеленым цветом.
585
     */
586
    public static function echoDone(bool $result=true): void
587
    {
588
        if($result === false){
589
            echo "\033[31;1mFAIL\033[0m \n";
590
        }else{
591
            echo "\033[32;1mDONE\033[0m \n";
592
        }
593
    }
594
595
    public static function echoResult(string $message, bool $result = true):void
596
    {
597
        $cols   = self::getCountCols();
598
        if(!is_numeric($cols)){
599
            // Не удалось получить ширину экрана.
600
            return;
601
        }
602
        $len = $cols - strlen($message) - 8;
603
        if($len < 2){
604
            // Не корректная ширина экрана.
605
            return;
606
        }
607
608
        $spaces = str_repeat('.', $len);
609
        echo "\r".$message.$spaces;
610
        self::echoDone($result);
611
    }
612
613
    public static function getCountCols():string
614
    {
615
        $len = 1*trim(shell_exec('tput cols'));
616
        if($len === 0){
617
            $len = 80;
618
        }else{
619
            $len = min($len, 80);
620
        }
621
        return $len;
622
    }
623
624
    /**
625
     * Создание символической ссылки, если необходимо.
626
     *
627
     * @param $target
628
     * @param $link
629
     * @param bool $isFile
630
     *
631
     * @return bool
632
     */
633
    public static function createUpdateSymlink($target, $link, $isFile=false): bool
634
    {
635
        $need_create_link = true;
636
        if (is_link($link)) {
637
            $old_target       = readlink($link);
638
            $need_create_link = ($old_target != $target);
639
            // Если необходимо, удаляем старую ссылку.
640
            if ($need_create_link) {
641
                $cpPath = self::which('cp');
642
                Processes::mwExec("{$cpPath} {$old_target}/* {$target}");
643
                unlink($link);
644
            }
645
        } elseif (is_dir($link)) {
646
            // Это должна быть именно ссылка. Файл удаляем.
647
            rmdir($link);
648
        } elseif (file_exists($link)) {
649
            // Это должна быть именно ссылка. Файл удаляем.
650
            unlink($link);
651
        }
652
        if($isFile === false){
653
            self::mwMkdir($target);
654
        }
655
        if ($need_create_link) {
656
            $lnPath = self::which('ln');
657
            Processes::mwExec("{$lnPath} -s {$target}  {$link}");
658
        }
659
660
        return $need_create_link;
661
    }
662
663
    /**
664
     * Create folder if it not exist.
665
     *
666
     * @param      $parameters string one or multiple paths separated by space
667
     *
668
     * @param bool $addWWWRights
669
     *
670
     * @return bool
671
     */
672
    public static function mwMkdir(string $parameters, bool $addWWWRights = false): bool
673
    {
674
        $result = true;
675
        if (posix_getuid() === 0) {
676
            $arrPaths = explode(' ', $parameters);
677
            if (count($arrPaths) > 0) {
678
                foreach ($arrPaths as $path) {
679
                    if ( ! empty($path)
680
                        && ! file_exists($path)
681
                        && ! mkdir($path, 0755, true)
682
                        && ! is_dir($path)) {
683
                        $result = false;
684
                        self::sysLogMsg('Util', 'Error on create folder ' . $path, LOG_ERR);
685
                    }
686
                    if ($addWWWRights) {
687
                        self::addRegularWWWRights($path);
688
                    }
689
                }
690
            }
691
        }
692
693
        return $result;
694
    }
695
696
    /**
697
     * Apply regular rights for folders and files
698
     *
699
     * @param $folder
700
     */
701
    public static function addRegularWWWRights($folder): void
702
    {
703
        if (posix_getuid() === 0) {
704
            $findPath  = self::which('find');
705
            $chownPath = self::which('chown');
706
            $chmodPath = self::which('chmod');
707
            Processes::mwExec("{$findPath} {$folder} -type d -exec {$chmodPath} 755 {} \;");
708
            Processes::mwExec("{$findPath} {$folder} -type f -exec {$chmodPath} 644 {} \;");
709
            Processes::mwExec("{$chownPath} -R www:www {$folder}");
710
        }
711
    }
712
713
    /**
714
     * Print message and write it to syslog
715
     *
716
     * @param $message
717
     */
718
    public static function echoWithSyslog($message): void
719
    {
720
        echo $message;
721
        self::sysLogMsg(static::class, $message, LOG_INFO);
722
    }
723
724
    /**
725
     * Apply executable rights for files
726
     *
727
     * @param $folder
728
     */
729
    public static function addExecutableRights($folder): void
730
    {
731
        if (posix_getuid() === 0) {
732
            $findPath  = self::which('find');
733
            $chmodPath = self::which('chmod');
734
            Processes::mwExec("{$findPath} {$folder} -type f -exec {$chmodPath} 755 {} \;");
735
        }
736
    }
737
738
    /**
739
     * Разбор INI конфига
740
     *
741
     * @param string $manual_attributes
742
     *
743
     * @return array
744
     */
745
    public static function parseIniSettings(string $manual_attributes): array
746
    {
747
        $tmp_data = base64_decode($manual_attributes);
748
        if (base64_encode($tmp_data) === $manual_attributes) {
749
            $manual_attributes = $tmp_data;
750
        }
751
        unset($tmp_data);
752
        // TRIMMING
753
        $tmp_arr = explode("\n", $manual_attributes);
754
        foreach ($tmp_arr as &$row) {
755
            $row = trim($row);
756
            $pos = strpos($row, ']');
757
            if ($pos !== false && strpos($row, '[') === 0) {
758
                $row = "\n" . substr($row, 0, $pos);
759
            }
760
        }
761
        unset($row);
762
        $manual_attributes = implode("\n", $tmp_arr);
763
        // TRIMMING END
764
765
        $manual_data = [];
766
        $sections    = explode("\n[", str_replace(']', '', $manual_attributes));
767
        foreach ($sections as $section) {
768
            $data_rows    = explode("\n", trim($section));
769
            $section_name = trim($data_rows[0] ?? '');
770
            if ( ! empty($section_name)) {
771
                unset($data_rows[0]);
772
                $manual_data[$section_name] = [];
773
                foreach ($data_rows as $row) {
774
                    $value = '';
775
                    if (strpos($row, '=') === false) {
776
                        continue;
777
                    }
778
                    $key       = '';
779
                    $arr_value = explode('=', $row);
780
                    if (count($arr_value) > 1) {
781
                        $key = trim($arr_value[0]);
782
                        unset($arr_value[0]);
783
                        $value = trim(implode('=', $arr_value));
784
                    }
785
                    if ( ($value !== '0' && empty($value)) || empty($key)) {
786
                        continue;
787
                    }
788
                    $manual_data[$section_name][$key] = $value;
789
                }
790
            }
791
        }
792
793
        return $manual_data;
794
    }
795
796
    /**
797
     * Converts multidimensional array into single array
798
     *
799
     * @param $array
800
     *
801
     * @return array
802
     */
803
    public static function flattenArray(array $array):array
804
    {
805
        $result = [];
806
        foreach ($array as $value) {
807
            if (is_array($value)) {
808
                $result = array_merge($result, self::flattenArray($value));
809
            } else {
810
                $result[] = $value;
811
            }
812
        }
813
814
        return $result;
815
    }
816
817
    /**
818
     * Try to find full path to php file by class name
819
     *
820
     * @param $className
821
     *
822
     * @return string|null
823
     */
824
    public static function getFilePathByClassName($className): ?string
825
    {
826
        $filename = null;
827
        try {
828
            $reflection = new ReflectionClass($className);
829
            $filename   = $reflection->getFileName();
830
        } catch (ReflectionException $exception) {
831
            self::sysLogMsg(__METHOD__, 'ReflectionException ' . $exception->getMessage(), LOG_ERR);
832
        }
833
834
        return $filename;
835
    }
836
837
    /**
838
     * DEPRICATED
839
     * Возвращает PID процесса по его имени.
840
     *
841
     * @param        $name
842
     * @param string $exclude
843
     *
844
     * @return string
845
     */
846
    public static function getPidOfProcess($name, $exclude = ''): string
847
    {
848
        self::sysLogMsg('Util', 'Deprecated call ' . __METHOD__ . ' from ' . static::class, LOG_DEBUG);
849
850
        return Processes::getPidOfProcess($name, $exclude);
851
    }
852
853
    /**
854
     * DEPRICATED
855
     * Manages a daemon/worker process
856
     * Returns process statuses by name of it
857
     *
858
     * @param $cmd
859
     * @param $param
860
     * @param $proc_name
861
     * @param $action
862
     * @param $out_file
863
     *
864
     * @return array | bool
865
     */
866
    public static function processWorker($cmd, $param, $proc_name, $action, $out_file = '/dev/null')
867
    {
868
        self::sysLogMsg('Util', 'Deprecated call ' . __METHOD__ . ' from ' . static::class, LOG_DEBUG);
869
870
        return Processes::processWorker($cmd, $param, $proc_name, $action, $out_file);
871
    }
872
873
    /**
874
     * DEPRICATED
875
     * Process PHP workers
876
     *
877
     * @param string $className
878
     * @param string $param
879
     * @param string $action
880
     */
881
    public static function processPHPWorker(
882
        string $className,
883
        string $param = 'start',
884
        string $action = 'restart'
885
    ): void {
886
        self::sysLogMsg('Util', 'Deprecated call ' . __METHOD__ . ' from ' . static::class, LOG_DEBUG);
887
        Processes::processPHPWorker($className, $param, $action);
888
    }
889
890
    /**
891
     * DEPRICATED
892
     * Kills process/daemon by name
893
     *
894
     * @param $procName
895
     *
896
     * @return int|null
897
     */
898
    public static function killByName($procName): ?int
899
    {
900
        self::sysLogMsg('Util', 'Deprecated call ' . __METHOD__ . ' from ' . static::class, LOG_DEBUG);
901
902
        return Processes::killByName($procName);
903
    }
904
905
    /**
906
     * DEPRICATED
907
     * Executes command exec() as background process.
908
     *
909
     * @param $command
910
     * @param $out_file
911
     * @param $sleep_time
912
     */
913
    public static function mwExecBg($command, $out_file = '/dev/null', $sleep_time = 0): void
914
    {
915
        self::sysLogMsg('Util', 'Deprecated call ' . __METHOD__ . ' from ' . static::class, LOG_DEBUG);
916
        Processes::mwExecBg($command, $out_file, $sleep_time);
917
    }
918
919
    /**
920
     * DEPRICATED
921
     * Executes command exec() as background process with an execution timeout.
922
     *
923
     * @param        $command
924
     * @param int    $timeout
925
     * @param string $logname
926
     */
927
    public static function mwExecBgWithTimeout($command, $timeout = 4, $logname = '/dev/null'): void
928
    {
929
        self::sysLogMsg('Util', 'Deprecated call ' . __METHOD__ . ' from ' . static::class, LOG_DEBUG);
930
        Processes::mwExecBgWithTimeout($command, $timeout, $logname);
931
    }
932
933
    /**
934
     * DEPRICATED
935
     * Executes multiple commands.
936
     *
937
     * @param        $arr_cmds
938
     * @param array  $out
939
     * @param string $logname
940
     */
941
    public static function mwExecCommands($arr_cmds, &$out = [], $logname = ''): void
942
    {
943
        self::sysLogMsg('Util', 'Deprecated call ' . __METHOD__ . ' from ' . static::class, LOG_DEBUG);
944
        Processes::mwExecCommands($arr_cmds, $out, $logname);
945
    }
946
947
    /**
948
     * Добавляем задачу для уведомлений.
949
     *
950
     * @param string $tube
951
     * @param        $data
952
     */
953
    public function addJobToBeanstalk(string $tube, $data): void
954
    {
955
        $queue = new BeanstalkClient($tube);
956
        $queue->publish(json_encode($data));
957
    }
958
959
960
}