437 lines
12 KiB
PHP
437 lines
12 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Libs\Config;
|
|
use App\Libs\Container;
|
|
use App\Libs\Entity\StateInterface;
|
|
use App\Libs\Extends\Date;
|
|
use App\Libs\Servers\ServerInterface;
|
|
use Nyholm\Psr7\Response;
|
|
use Nyholm\Psr7\Uri;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
use Psr\Log\LoggerInterface;
|
|
use Psr\SimpleCache\CacheInterface;
|
|
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
|
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
|
|
|
|
if (!function_exists('env')) {
|
|
function env(string $key, mixed $default = null): mixed
|
|
{
|
|
if (false === ($value = $_ENV[$key] ?? getenv($key))) {
|
|
return getValue($default);
|
|
}
|
|
|
|
return match (strtolower($value)) {
|
|
'true', '(true)' => true,
|
|
'false', '(false)' => false,
|
|
'empty', '(empty)' => '',
|
|
'null', '(null)' => null,
|
|
default => $value,
|
|
};
|
|
}
|
|
}
|
|
|
|
if (!function_exists('getValue')) {
|
|
function getValue(mixed $var): mixed
|
|
{
|
|
return ($var instanceof Closure) ? $var() : $var;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('makeDate')) {
|
|
/**
|
|
* Make Date Time Object.
|
|
*
|
|
* @param string|int $date Defaults to now
|
|
* @param string|DateTimeZone|null $tz For given $date, not for display.
|
|
*
|
|
* @return Date
|
|
*/
|
|
function makeDate(string|int $date = 'now', DateTimeZone|string|null $tz = null): Date
|
|
{
|
|
if (ctype_digit((string)$date)) {
|
|
$date = '@' . $date;
|
|
}
|
|
|
|
if (null === $tz) {
|
|
$tz = date_default_timezone_get();
|
|
}
|
|
|
|
if (!($tz instanceof DateTimeZone)) {
|
|
$tz = new DateTimeZone($tz);
|
|
}
|
|
|
|
return (new Date($date))->setTimezone($tz);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('ag')) {
|
|
function ag(array $array, string|null $path, mixed $default = null, string $separator = '.'): mixed
|
|
{
|
|
if (null === $path) {
|
|
return $array;
|
|
}
|
|
|
|
if (array_key_exists($path, $array)) {
|
|
return $array[$path];
|
|
}
|
|
|
|
if (!str_contains($path, $separator)) {
|
|
return $array[$path] ?? getValue($default);
|
|
}
|
|
|
|
foreach (explode($separator, $path) as $segment) {
|
|
if (is_array($array) && array_key_exists($segment, $array)) {
|
|
$array = $array[$segment];
|
|
} else {
|
|
return getValue($default);
|
|
}
|
|
}
|
|
|
|
return $array;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('ag_set')) {
|
|
/**
|
|
* Set an array item to a given value using "dot" notation.
|
|
*
|
|
* If no key is given to the method, the entire array will be replaced.
|
|
*
|
|
* @param array $array
|
|
* @param string $path
|
|
* @param mixed $value
|
|
* @param string $separator
|
|
*
|
|
* @return array return modified array.
|
|
*/
|
|
function ag_set(array $array, string $path, mixed $value, string $separator = '.'): array
|
|
{
|
|
$keys = explode($separator, $path);
|
|
|
|
$at = &$array;
|
|
|
|
while (count($keys) > 0) {
|
|
if (1 === count($keys)) {
|
|
if (is_array($at)) {
|
|
$at[array_shift($keys)] = $value;
|
|
} else {
|
|
throw new RuntimeException("Can not set value at this path ($path) because its not array.");
|
|
}
|
|
} else {
|
|
$path = array_shift($keys);
|
|
if (!isset($at[$path])) {
|
|
$at[$path] = [];
|
|
}
|
|
$at = &$at[$path];
|
|
}
|
|
}
|
|
|
|
return $array;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('ag_exists')) {
|
|
/**
|
|
* Determine if the given key exists in the provided array.
|
|
*
|
|
* @param array $array
|
|
* @param string|int $path
|
|
* @param string $separator
|
|
*
|
|
* @return bool
|
|
*/
|
|
function ag_exists(array $array, string|int $path, string $separator = '.'): bool
|
|
{
|
|
if (is_int($path)) {
|
|
return isset($array[$path]);
|
|
}
|
|
|
|
foreach (explode($separator, $path) as $lookup) {
|
|
if (isset($array[$lookup])) {
|
|
$array = $array[$lookup];
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('ag_delete')) {
|
|
/**
|
|
* Delete given key path.
|
|
*
|
|
* @param array $array
|
|
* @param int|string $path
|
|
* @param string $separator
|
|
* @return array
|
|
*/
|
|
function ag_delete(array $array, string|int $path, string $separator = '.'): array
|
|
{
|
|
if (array_key_exists($path, $array)) {
|
|
unset($array[$path]);
|
|
|
|
return $array;
|
|
}
|
|
|
|
if (is_int($path)) {
|
|
if (isset($array[$path])) {
|
|
unset($array[$path]);
|
|
}
|
|
return $array;
|
|
}
|
|
|
|
$items = &$array;
|
|
|
|
$segments = explode($separator, $path);
|
|
|
|
$lastSegment = array_pop($segments);
|
|
|
|
foreach ($segments as $segment) {
|
|
if (!isset($items[$segment]) || !is_array($items[$segment])) {
|
|
continue;
|
|
}
|
|
|
|
$items = &$items[$segment];
|
|
}
|
|
|
|
if (null !== $lastSegment && array_key_exists($lastSegment, $items)) {
|
|
unset($items[$lastSegment]);
|
|
}
|
|
|
|
return $array;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('fixPath')) {
|
|
function fixPath(string $path): string
|
|
{
|
|
return rtrim(implode(DIRECTORY_SEPARATOR, explode(DIRECTORY_SEPARATOR, $path)), DIRECTORY_SEPARATOR);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('fsize')) {
|
|
function fsize(string|int $bytes = 0, bool $showUnit = true, int $decimals = 2, int $mod = 1000): string
|
|
{
|
|
$sz = 'BKMGTP';
|
|
|
|
$factor = floor((strlen((string)$bytes) - 1) / 3);
|
|
|
|
return sprintf("%.{$decimals}f", (int)($bytes) / ($mod ** $factor)) . ($showUnit ? $sz[(int)$factor] : '');
|
|
}
|
|
}
|
|
|
|
if (!function_exists('saveWebhookPayload')) {
|
|
function saveWebhookPayload(ServerRequestInterface $request, string $name, array $parsed = []): void
|
|
{
|
|
$content = [
|
|
'query' => $request->getQueryParams(),
|
|
'parsed' => $request->getParsedBody(),
|
|
'server' => $request->getServerParams(),
|
|
'body' => (string)$request->getBody(),
|
|
'attributes' => $request->getAttributes(),
|
|
'cParsed' => $parsed,
|
|
];
|
|
|
|
@file_put_contents(
|
|
Config::get('tmpDir') . '/webhooks/' . sprintf(
|
|
'webhook.%s.%d.json',
|
|
$name,
|
|
(string)ag($request->getServerParams(), 'X_REQUEST_ID', time())
|
|
),
|
|
json_encode($content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('saveRequestPayload')) {
|
|
function saveRequestPayload(ServerRequestInterface $request): void
|
|
{
|
|
$content = [
|
|
'query' => $request->getQueryParams(),
|
|
'parsed' => $request->getParsedBody(),
|
|
'server' => $request->getServerParams(),
|
|
'body' => (string)$request->getBody(),
|
|
'attributes' => $request->getAttributes(),
|
|
];
|
|
|
|
@file_put_contents(
|
|
Config::get('tmpDir') . '/webhooks/' . sprintf(
|
|
'request.%s.json',
|
|
(string)ag($request->getServerParams(), 'X_REQUEST_ID', time())
|
|
),
|
|
json_encode($content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('jsonResponse')) {
|
|
function jsonResponse(int $status, array $body, $headers = []): ResponseInterface
|
|
{
|
|
$headers['Content-Type'] = 'application/json';
|
|
|
|
return new Response(
|
|
status: $status,
|
|
headers: $headers,
|
|
body: json_encode(
|
|
$body,
|
|
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_SLASHES
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('httpClientChunks')) {
|
|
/**
|
|
* Handle Response Stream as Chunks
|
|
*
|
|
* @param ResponseStreamInterface $responseStream
|
|
* @return Generator
|
|
*
|
|
* @throws TransportExceptionInterface
|
|
*/
|
|
function httpClientChunks(ResponseStreamInterface $responseStream): Generator
|
|
{
|
|
foreach ($responseStream as $chunk) {
|
|
yield $chunk->getContent();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!function_exists('preServeHttpRequest')) {
|
|
function preServeHttpRequest(ServerRequestInterface $request): ServerRequestInterface
|
|
{
|
|
foreach (Config::get('supported', []) as $server) {
|
|
assert($server instanceof ServerInterface);
|
|
$request = $server::processRequest($request);
|
|
}
|
|
|
|
return $request;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('queuePush')) {
|
|
function queuePush(StateInterface $entity): void
|
|
{
|
|
if (!$entity->hasGuids()) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$cache = Container::get(CacheInterface::class);
|
|
|
|
$list = $cache->get('queue', []);
|
|
|
|
$list[$entity->id] = $entity->getAll();
|
|
|
|
$cache->set('queue', $list);
|
|
} catch (\Psr\SimpleCache\InvalidArgumentException $e) {
|
|
Container::get(LoggerInterface::class)->error($e->getMessage(), $e->getTrace());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!function_exists('afterLast')) {
|
|
function afterLast(string $subject, string $search): string
|
|
{
|
|
if (empty($search)) {
|
|
return $subject;
|
|
}
|
|
|
|
$position = mb_strrpos($subject, $search, 0);
|
|
|
|
if (false === $position) {
|
|
return $subject;
|
|
}
|
|
|
|
return mb_substr($subject, $position + mb_strlen($search));
|
|
}
|
|
}
|
|
|
|
if (!function_exists('makeServer')) {
|
|
/**
|
|
* @param array{name:string|null, type:string, url:string, token:string|int|null, user:string|int|null, persist:array, options:array} $server
|
|
* @param string|null $name server name.
|
|
* @return ServerInterface
|
|
*
|
|
* @throws RuntimeException if configuration is wrong.
|
|
*/
|
|
function makeServer(array $server, string|null $name = null): ServerInterface
|
|
{
|
|
if (null === ($serverType = ag($server, 'type'))) {
|
|
throw new RuntimeException('No server type was selected.');
|
|
}
|
|
|
|
if (null === ag($server, 'url')) {
|
|
throw new RuntimeException('No url was set for server.');
|
|
}
|
|
|
|
if (null === ($class = Config::get("supported.{$serverType}", null))) {
|
|
throw new RuntimeException(
|
|
sprintf(
|
|
'Unexpected server type was given. Was expecting [%s] but got \'%s\' instead.',
|
|
$serverType,
|
|
implode('|', Config::get("supported", []))
|
|
)
|
|
);
|
|
}
|
|
|
|
return Container::getNew($class)->setUp(
|
|
name: $name ?? ag($server, 'name', fn() => md5(ag($server, 'url'))),
|
|
url: new Uri(ag($server, 'url')),
|
|
token: ag($server, 'token', null),
|
|
userId: ag($server, 'user', null),
|
|
uuid: ag($server, 'uuid', null),
|
|
persist: ag($server, 'persist', []),
|
|
options: ag($server, 'options', []),
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('arrayToString')) {
|
|
function arrayToString(array $arr, string $separator = ', '): string
|
|
{
|
|
$list = [];
|
|
|
|
foreach ($arr as $key => $val) {
|
|
if (is_object($val)) {
|
|
if (($val instanceof JsonSerializable)) {
|
|
$val = json_encode($val, flags: JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
|
} elseif (($val instanceof Stringable) || method_exists($val, '__toString')) {
|
|
$val = (string)$val;
|
|
} else {
|
|
$val = [
|
|
spl_object_hash($val) => get_class($val),
|
|
...(array)$val
|
|
];
|
|
}
|
|
}
|
|
|
|
if (is_array($val)) {
|
|
$val = json_encode($val, flags: JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
|
} else {
|
|
$val = $val ?? 'None';
|
|
}
|
|
|
|
$list[] = sprintf("(%s: %s)", $key, $val);
|
|
}
|
|
|
|
return implode($separator, $list);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('commandContext')) {
|
|
function commandContext(): string
|
|
{
|
|
if (env('IN_DOCKER')) {
|
|
return sprintf('docker exec -ti %s console ', env('CONTAINER_NAME', 'watchstate'));
|
|
}
|
|
|
|
return ($_SERVER['argv'][0] ?? 'php console') . ' ';
|
|
}
|
|
}
|