Passed
Pull Request — master (#16)
by Nikolay
13:10 queued 02:12
created

Util   F

Complexity

Total Complexity 135

Size/Duplication

Total Lines 909
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 135
eloc 345
c 4
b 0
f 0
dl 0
loc 909
rs 2

40 Methods

Rating   Name   Duplication   Size   Complexity  
A killByName() 0 5 1
A mwExecBgWithTimeout() 0 12 3
A mwExecCommands() 0 13 3
A mwExecBg() 0 14 2
B overrideConfigurationArray() 0 24 9
A mwExec() 0 13 3
B mwMkdir() 0 21 9
A getNowDate() 0 11 2
A getExtensionOfFile() 0 4 1
A trimExtensionForFile() 0 10 2
A processPHPWorker() 0 6 2
B fileWriteContent() 0 28 9
A mFileSize() 0 12 3
B processWorker() 0 36 10
A echoGreenDone() 0 3 1
A echoWithSyslog() 0 4 1
A generateRandomString() 0 14 3
A recFileExists() 0 3 2
A getSizeOfFile() 0 15 3
A sysLogMsg() 0 6 2
A createUpdateSymlink() 0 26 6
A flattenArray() 0 10 3
A setCyrillicFont() 0 4 1
A generateSslCert() 0 37 4
A getExtensionX() 0 8 2
A addExecutableRights() 0 6 2
A logMsgDb() 0 14 4
A numberToDate() 0 9 2
C parseIniSettings() 0 48 12
B rRmDir() 0 17 7
A isSystemctl() 0 3 1
A addRegularWWWRights() 0 9 2
A isJson() 0 5 1
A amiOriginate() 0 12 1
A getFilePathByClassName() 0 11 2
A addJobToBeanstalk() 0 4 1
A getPidOfProcess() 0 18 2
A getAstManager() 0 15 3
A translate() 0 7 2
A which() 0 28 6

How to fix   Complexity   

Complex Class

Complex classes like Util often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Util, and based on these observations, apply Extract Interface, too.

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