Completed
Push — master ( 59dd4d...b5d47e )
by Marwan
15s queued 11s
created

Utility::execute()   B

Complexity

Conditions 9
Paths 33

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 9

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 12
c 2
b 0
f 0
dl 0
loc 23
rs 8.0555
ccs 13
cts 13
cp 1
cc 9
nc 33
nop 3
crap 9
1
<?php
2
/**
3
 * @author Marwan Al-Soltany <[email protected]>
4
 * @copyright Marwan Al-Soltany 2020
5
 * For the full copyright and license information, please view
6
 * the LICENSE file that was distributed with this source code.
7
 */
8
9
namespace MAKS\AmqpAgent\Helper;
10
11
use stdClass;
12
use Exception;
13
use ReflectionObject;
14
use DateTime;
15
use DateTimeZone;
16
17
/**
18
 * A class containing miscellaneous helper functions.
19
 * @since 1.2.0
20
 */
21
final class Utility
22
{
23
    /**
24
     * Returns a DateTime object with the right time zone.
25
     * @param string $time A valid php date/time string.
26
     * @param string $timezone A valid php timezone string.
27
     * @return DateTime
28 4
     */
29
    public static function time(string $time = 'now', ?string $timezone = null): DateTime
30 4
    {
31 1
        $timezone = $timezone
32 4
            ? $timezone
33
            : date_default_timezone_get();
34 4
35 4
        $timezoneObject = $timezone
36 4
            ? new DateTimeZone($timezone)
37
            : null;
38 4
39
        return new DateTime($time, $timezoneObject);
40
    }
41
42
    /**
43
     * Generates a user-level notice, warning, or an error with styling.
44
     * @param array|string|null $text [optional] The text wished to be styled (when passing an array, if array key is a valid color it will style this array element value with its key).
45
     * @param string $color [optional] Case sensitive ANSI color name in this list [black, red, green, yellow, magenta, cyan, white, default] (when passing array, this parameter will be the fallback).
46
     * @param int $type [optional] Error type (E_USER family). 1024 E_USER_NOTICE, 512 E_USER_WARNING, 256 E_USER_ERROR, 16384 E_USER_DEPRECATED.
47
     * @return bool True if error type is accepted.
48 4
     */
49
    public static function emit($text = null, ?string $color = 'yellow', int $type = E_USER_NOTICE): bool
50
    {
51 4
        $colors = [
52
            'reset'   => 0,
53
            'black'   => 30,
54
            'red'     => 31,
55
            'green'   => 32,
56
            'yellow'  => 33,
57
            'blue'    => 34,
58
            'magenta' => 35,
59
            'cyan'    => 36,
60
            'white'   => 37,
61
            'default' => 39,
62
        ];
63
64 4
        $types = [
65 4
            E_USER_NOTICE     => E_USER_NOTICE,
66 4
            E_USER_WARNING    => E_USER_WARNING,
67 4
            E_USER_ERROR      => E_USER_ERROR,
68
            E_USER_DEPRECATED => E_USER_DEPRECATED,
69
        ];
70 4
71
        $cli = php_sapi_name() === 'cli' || php_sapi_name() === 'cli-server' || http_response_code() === false;
72 4
73 4
        $trim = ' \t\0\x0B';
74 4
        $backspace = chr(8);
75 4
        $wrapper = $cli ? "\033[%dm %s\033[0m" : "@COLOR[%d] %s";
76 4
        $color = $colors[$color] ?? 39;
77 4
        $type = $types[$type] ?? 1024;
78
        $message = '';
79 4
80 2
        if (is_array($text)) {
81 2
            foreach ($text as $segmentColor => $string) {
82 2
                $string = trim($string, $trim);
83 2
                if (is_string($segmentColor)) {
84 2
                    $segmentColor = $colors[$segmentColor] ?? $color;
85 2
                    $message .= !strlen($message)
86 2
                        ? sprintf($wrapper, $segmentColor, $backspace . $string)
87 2
                        : sprintf($wrapper, $segmentColor, $string);
88
                    continue;
89 1
                }
90
                $message = $message . $string;
91 2
            }
92 1
        } elseif (is_string($text)) {
93 1
            $string = $backspace . trim($text, $trim);
94
            $message = sprintf($wrapper, $color, $string);
95 1
        } else {
96 1
            $string = $backspace . 'From ' . __METHOD__ . ': No message was specified!';
97
            $message = sprintf($wrapper, $color, $string);
98
        }
99 4
100
        $message = $cli ? $message : preg_replace('/@COLOR\[\d+\]/', '', $message);
101 4
102
        return trigger_error($message, $type);
103
    }
104
105
    /**
106
     * Returns the passed key(s) from the backtrace. Note that the backtrace is reversed (last is first).
107
     * @param string|array $pluck The key to to get as a string or an array of strings (keys) from this list [file, line, function, class, type, args].
108
     * @param int $offset [optional] The offset of the backtrace (last executed is index at 0).
109
     * @return string|int|array|null A string or int if a string is passed, an array if an array is passed and null if no match was found.
110 8
     */
111
    public static function backtrace($pluck, int $offset = 0)
112 8
    {
113 8
        $backtrace = array_reverse(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT));
114
        $plucked = null;
115 8
116 1
        if (count($backtrace) < $offset + 1) {
117 7
            return null;
118 1
        } elseif (is_string($pluck)) {
119 6
            $plucked = isset($backtrace[$offset][$pluck]) ? $backtrace[$offset][$pluck] : null;
120 6
        } elseif (is_array($pluck)) {
0 ignored issues
show
introduced by
The condition is_array($pluck) is always true.
Loading history...
121 6
            $plucked = [];
122 6
            foreach ($pluck as $key) {
123
                !isset($backtrace[$offset][$key]) ?: $plucked[$key] = $backtrace[$offset][$key];
124
            }
125
        }
126 7
127
        return is_string($plucked) || is_array($plucked) && count($plucked, COUNT_RECURSIVE) ? $plucked : null;
128
    }
129
130
    /**
131
     * Executes a CLI command in the specified path synchronously or asynchronous (cross platform).
132
     * @since 2.0.0
133
     * @param string $command The command to execute.
134
     * @param string $path [optional] The path where the command should be executed.
135 4
     * @param bool $asynchronous [optional] Wether the command should be a background process (asynchronous) or not (synchronous).
136
     * @return string|null The command result (as a string if possible) if synchronous otherwise null.
137 4
     */
138
    public static function execute(string $command, string $path = null, bool $asynchronous = false): ?string
139 4
    {
140 2
        if (!strlen($command)) {
141 1
            throw new Exception('No valid command is specified!');
142 2
        }
143 1
144 2
        $isWindows = PHP_OS == 'WINNT' || substr(php_uname(), 0, 7) == 'Windows';
145 2
        $apWrapper = $isWindows ? 'start /B %s > NUL' : '/usr/bin/nohup %s >/dev/null 2>&1 &';
146 1
147 1
        if (strlen($path) && getcwd() !== $path) {
148 1
            chdir(realpath($path));
149 1
        }
150
151 1
        if ($asynchronous) {
152
            $command = sprintf($apWrapper, $command);
153
        }
154
155 4
        if ($isWindows && $asynchronous) {
156
            pclose(popen($command, 'r'));
0 ignored issues
show
Bug introduced by
It seems like popen($command, 'r') can also be of type false; however, parameter $handle of pclose() does only seem to accept resource, 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

156
            pclose(/** @scrutinizer ignore-type */ popen($command, 'r'));
Loading history...
157
            return null;
158
        }
159
160
        return shell_exec($command);
161
    }
162
}
163