1 | <?php |
||
2 | |||
3 | /** |
||
4 | * @author Marwan Al-Soltany <[email protected]> |
||
5 | * @copyright Marwan Al-Soltany 2020 |
||
6 | * For the full copyright and license information, please view |
||
7 | * the LICENSE file that was distributed with this source code. |
||
8 | */ |
||
9 | |||
10 | declare(strict_types=1); |
||
11 | |||
12 | namespace MAKS\AmqpAgent\Helper; |
||
13 | |||
14 | use Exception; |
||
15 | use DateTime; |
||
16 | use DateTimeZone; |
||
17 | |||
18 | /** |
||
19 | * A class containing miscellaneous helper functions. |
||
20 | * @since 1.2.0 |
||
21 | */ |
||
22 | final class Utility |
||
23 | { |
||
24 | /** |
||
25 | * Returns a DateTime object with the right time zone. |
||
26 | * @param string $time A valid php date/time string. |
||
27 | * @param string|null $timezone A valid php timezone string. |
||
28 | * @return DateTime |
||
29 | * @throws Exception |
||
30 | */ |
||
31 | 4 | public static function time(string $time = 'now', ?string $timezone = null): DateTime |
|
32 | { |
||
33 | 4 | $timezone = $timezone |
|
34 | 1 | ? $timezone |
|
35 | 4 | : date_default_timezone_get(); |
|
36 | |||
37 | 4 | $timezoneObject = $timezone |
|
38 | 4 | ? new DateTimeZone($timezone) |
|
39 | 4 | : null; |
|
40 | |||
41 | 4 | return new DateTime($time, $timezoneObject); |
|
42 | } |
||
43 | |||
44 | /** |
||
45 | * Generates a user-level notice, warning, or an error with styling. |
||
46 | * @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). |
||
47 | * @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). |
||
48 | * @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. |
||
49 | * @return bool True if error type is accepted. |
||
50 | */ |
||
51 | 4 | public static function emit($text = null, ?string $color = 'yellow', int $type = E_USER_NOTICE): bool |
|
52 | { |
||
53 | $colors = [ |
||
54 | 4 | 'reset' => 0, |
|
55 | 'black' => 30, |
||
56 | 'red' => 31, |
||
57 | 'green' => 32, |
||
58 | 'yellow' => 33, |
||
59 | 'blue' => 34, |
||
60 | 'magenta' => 35, |
||
61 | 'cyan' => 36, |
||
62 | 'white' => 37, |
||
63 | 'default' => 39, |
||
64 | ]; |
||
65 | |||
66 | $types = [ |
||
67 | 4 | E_USER_NOTICE => E_USER_NOTICE, |
|
68 | 4 | E_USER_WARNING => E_USER_WARNING, |
|
69 | 4 | E_USER_ERROR => E_USER_ERROR, |
|
70 | 4 | E_USER_DEPRECATED => E_USER_DEPRECATED, |
|
71 | ]; |
||
72 | |||
73 | 4 | $cli = php_sapi_name() === 'cli' || php_sapi_name() === 'cli-server' || http_response_code() === false; |
|
74 | |||
75 | 4 | $trim = ' \t\0\x0B'; |
|
76 | 4 | $backspace = chr(8); |
|
77 | 4 | $wrapper = $cli ? "\033[%dm %s\033[0m" : "@COLOR[%d] %s"; |
|
78 | 4 | $color = $colors[$color] ?? 39; |
|
79 | 4 | $type = $types[$type] ?? 1024; |
|
80 | 4 | $message = ''; |
|
81 | |||
82 | 4 | if (is_array($text)) { |
|
83 | 2 | foreach ($text as $segmentColor => $string) { |
|
84 | 2 | $string = trim($string, $trim); |
|
85 | 2 | if (is_string($segmentColor)) { |
|
86 | 2 | $segmentColor = $colors[$segmentColor] ?? $color; |
|
87 | 2 | $message .= !strlen($message) |
|
88 | 2 | ? sprintf($wrapper, $segmentColor, $backspace . $string) |
|
89 | 2 | : sprintf($wrapper, $segmentColor, $string); |
|
90 | 2 | continue; |
|
91 | } |
||
92 | 2 | $message = $message . $string; |
|
93 | } |
||
94 | 2 | } elseif (is_string($text)) { |
|
95 | 1 | $string = $backspace . trim($text, $trim); |
|
96 | 1 | $message = sprintf($wrapper, $color, $string); |
|
97 | } else { |
||
98 | 1 | $string = $backspace . 'From ' . __METHOD__ . ': No message was specified!'; |
|
99 | 1 | $message = sprintf($wrapper, $color, $string); |
|
100 | } |
||
101 | |||
102 | 4 | $message = $cli ? $message : preg_replace('/@COLOR\[\d+\]/', '', $message); |
|
103 | |||
104 | 4 | return trigger_error($message, $type); |
|
105 | } |
||
106 | |||
107 | /** |
||
108 | * Returns the passed key(s) from the backtrace. Note that the backtrace is reversed (last is first). |
||
109 | * @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]. |
||
110 | * @param int $offset [optional] The offset of the backtrace (last executed is index at 0). |
||
111 | * @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. |
||
112 | */ |
||
113 | 11 | public static function backtrace($pluck, int $offset = 0) |
|
114 | { |
||
115 | 11 | $backtrace = array_reverse(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT)); |
|
116 | 11 | $plucked = null; |
|
117 | |||
118 | 11 | if (count($backtrace) < $offset + 1) { |
|
119 | 1 | return null; |
|
120 | 10 | } elseif (is_string($pluck)) { |
|
121 | 1 | $plucked = isset($backtrace[$offset][$pluck]) ? $backtrace[$offset][$pluck] : null; |
|
122 | 9 | } elseif (is_array($pluck)) { |
|
0 ignored issues
–
show
introduced
by
![]() |
|||
123 | 9 | $plucked = []; |
|
124 | 9 | foreach ($pluck as $key) { |
|
125 | 9 | !isset($backtrace[$offset][$key]) ?: $plucked[$key] = $backtrace[$offset][$key]; |
|
126 | } |
||
127 | } |
||
128 | |||
129 | 10 | return is_string($plucked) || is_array($plucked) && count($plucked, COUNT_RECURSIVE) ? $plucked : null; |
|
130 | } |
||
131 | |||
132 | /** |
||
133 | * Executes a CLI command in the specified path synchronously or asynchronous (cross platform). |
||
134 | * @since 2.0.0 |
||
135 | * @param string $command The command to execute. |
||
136 | * @param string|null $path [optional] The path where the command should be executed. |
||
137 | * @param bool $asynchronous [optional] Whether the command should be a background process (asynchronous) or not (synchronous). |
||
138 | * @return string|null The command result (as a string if possible) if synchronous otherwise null. |
||
139 | * @throws Exception |
||
140 | */ |
||
141 | 6 | public static function execute(string $command, string $path = null, bool $asynchronous = false): ?string |
|
142 | { |
||
143 | 6 | if (!strlen($command)) { |
|
144 | 1 | throw new Exception('No valid command is specified!'); |
|
145 | } |
||
146 | |||
147 | 5 | $isWindows = PHP_OS == 'WINNT' || substr(php_uname(), 0, 7) == 'Windows'; |
|
148 | 5 | $apWrapper = $isWindows ? 'start /B %s > NUL' : '/usr/bin/nohup %s >/dev/null 2>&1 &'; |
|
149 | |||
150 | 5 | if ($path && strlen($path) && getcwd() !== $path) { |
|
151 | 5 | chdir(realpath($path)); |
|
152 | } |
||
153 | |||
154 | 5 | if ($asynchronous) { |
|
155 | 5 | $command = sprintf($apWrapper, $command); |
|
156 | } |
||
157 | |||
158 | 5 | if ($isWindows && $asynchronous) { |
|
159 | pclose(popen($command, 'r')); |
||
160 | return null; |
||
161 | } |
||
162 | |||
163 | 5 | return shell_exec($command); |
|
164 | } |
||
165 | |||
166 | /** |
||
167 | * Returns an HTTP Response to the browser and lets blocking code that comes after this function to continue executing in the background. |
||
168 | * This function is useful for example in Controllers that do some long-running tasks, and you just want to inform the client that the job has been started. |
||
169 | * Please note that this function is tested MANUALLY ONLY, it is provided as is, there is no guarantee that it will work as expected nor on all platforms. |
||
170 | * @param string $body The response body. |
||
171 | * @param int $status [optional] The response status code. |
||
172 | * @param array $headers [optional] An associative array of additional response headers `['headerName' => 'headerValue']`. |
||
173 | * @return void |
||
174 | * @since 2.2.0 |
||
175 | * @codeCoverageIgnore |
||
176 | */ |
||
177 | public static function respond(string $body, int $status = 200, array $headers = []): void |
||
178 | { |
||
179 | // client disconnection should not abort script execution |
||
180 | ignore_user_abort(true); |
||
181 | // script execution should not bound by a timeout |
||
182 | set_time_limit(0); |
||
183 | |||
184 | // writing to the session must be closed to prevents subsequent requests from hanging |
||
185 | if (session_id()) { |
||
186 | session_write_close(); |
||
187 | } |
||
188 | |||
189 | // clean the output buffer and turn off output buffering |
||
190 | ob_end_clean(); |
||
191 | // turn on output buffering and buffer all upcoming output |
||
192 | ob_start(); |
||
193 | |||
194 | // echo out response body (message) |
||
195 | echo($body); |
||
196 | |||
197 | // only keep the last buffer if nested and get the length of the output buffer |
||
198 | while (ob_get_level() > 1) { |
||
199 | ob_end_flush(); |
||
200 | } |
||
201 | $length = ob_get_level() ? ob_get_length() : 0; |
||
202 | |||
203 | // reserved headers that must not be overwritten |
||
204 | $reservedHeaders = [ |
||
205 | 'Connection' => 'close', |
||
206 | 'Content-Encoding' => 'none', |
||
207 | 'Content-Length' => $length |
||
208 | ]; |
||
209 | |||
210 | // user headers after filtering out the reserved headers |
||
211 | $filteredHeaders = array_filter($headers, function ($key) use ($reservedHeaders) { |
||
212 | $immutable = array_map('strtolower', array_keys($reservedHeaders)); |
||
213 | $mutable = strtolower($key); |
||
214 | return !in_array($mutable, $immutable); |
||
215 | }, ARRAY_FILTER_USE_KEY); |
||
216 | |||
217 | // final headers for the response |
||
218 | $finalHeaders = array_merge($reservedHeaders, $filteredHeaders); |
||
219 | |||
220 | // send headers to tell the browser to close the connection |
||
221 | foreach ($finalHeaders as $headerName => $headerValue) { |
||
222 | header(sprintf('%s: %s', $headerName, $headerValue)); |
||
223 | } |
||
224 | |||
225 | // set the HTTP response code |
||
226 | http_response_code($status); |
||
227 | |||
228 | // flush the output buffer and turn off output buffering |
||
229 | ob_end_flush(); |
||
230 | |||
231 | // flush all output buffer layers |
||
232 | if (ob_get_level()) { |
||
233 | ob_flush(); |
||
234 | } |
||
235 | |||
236 | // flush system output buffer |
||
237 | flush(); |
||
238 | |||
239 | echo('You should not be seeing this!'); |
||
240 | } |
||
241 | } |
||
242 |