Passed
Push — develop ( a76723...2c72a4 )
by Nikolay
05:29
created

Util::isT2SdeLinux()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
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
     * Checks whether the current system is t2 SDE build
537
     *
538
     */
539
    public static function isT2SdeLinux(): bool
540
    {
541
        return !self::isSystemctl();
542
    }
543
544
    /**
545
     * Checks whether the current system has systemctl installed and executable.
546
     * Returns true if systemctl is available, false otherwise.
547
     */
548
    public static function isSystemctl(): bool
549
    {
550
        $pathSystemCtl = self::which('systemctl');
551
        if (!empty($pathSystemCtl) && is_executable($pathSystemCtl)) {
552
            return true;
553
        } else {
554
            return false;
555
        }
556
    }
557
558
   /**
559
    * Checks whether the current process is running inside a Docker container.
560
    * Returns true if the process is inside a container, false otherwise.
561
    *
562
    * @return bool
563
    */
564
    public static function isDocker(): bool
565
    {
566
        return file_exists('/.dockerenv');
567
    }
568
569
    /**
570
     * Вывод в основной teletype.
571
     * @param string $message
572
     * @param string $ttyPath
573
     * @return void
574
     */
575
    public static function teletypeEcho(string $message, string $ttyPath = '/dev/ttyS0'):void
576
    {
577
        $pathBusyBox    = self::which('busybox');
578
        $ttyTittle      = trim(shell_exec("$pathBusyBox setserial -g $ttyPath 2> /dev/null"));
579
        if(strpos($ttyTittle, $ttyPath) !== false && strpos($ttyTittle, 'unknown') === false){
580
            @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

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