static-php-cli/src/globals/functions.php

286 lines
7.4 KiB
PHP
Raw Normal View History

2023-03-15 20:40:49 +08:00
<?php
declare(strict_types=1);
use Psr\Log\LoggerInterface;
use SPC\builder\BuilderBase;
use SPC\builder\BuilderProvider;
2025-08-06 20:47:48 +08:00
use SPC\exception\ExecutionException;
2024-12-05 15:48:09 +08:00
use SPC\exception\InterruptException;
2023-03-29 21:39:36 +08:00
use SPC\exception\WrongUsageException;
use SPC\util\shell\UnixShell;
use SPC\util\shell\WindowsCmd;
2023-03-15 20:40:49 +08:00
use ZM\Logger\ConsoleLogger;
/**
* Judge if an array is an associative array
2023-03-15 20:40:49 +08:00
*/
function is_assoc_array(mixed $array): bool
2023-03-15 20:40:49 +08:00
{
return is_array($array) && (!empty($array) && array_keys($array) !== range(0, count($array) - 1));
}
/**
* Judge if an array is a list
*/
function is_list_array(mixed $array): bool
{
return is_array($array) && (empty($array) || array_keys($array) === range(0, count($array) - 1));
2023-03-15 20:40:49 +08:00
}
/**
* Return a logger instance
2023-03-15 20:40:49 +08:00
*/
function logger(): LoggerInterface
{
global $ob_logger;
if ($ob_logger === null) {
return new ConsoleLogger();
}
return $ob_logger;
}
2024-10-01 15:37:37 +08:00
function is_unix(): bool
{
return in_array(PHP_OS_FAMILY, ['Linux', 'Darwin', 'BSD']);
}
2023-03-15 20:40:49 +08:00
/**
* Transfer architecture name to gnu triplet
2023-03-15 20:40:49 +08:00
*/
function arch2gnu(string $arch): string
{
$arch = strtolower($arch);
return match ($arch) {
'x86_64', 'x64', 'amd64' => 'x86_64',
'arm64', 'aarch64' => 'aarch64',
2023-03-29 21:39:36 +08:00
default => throw new WrongUsageException('Not support arch: ' . $arch),
2023-03-15 20:40:49 +08:00
// 'armv7' => 'arm',
};
}
/**
* Match pattern function
* Example: match_pattern('*.txt', 'test.txt') will return true.
*
* @param string $pattern Pattern string
* @param string $subject Subject string
*/
2024-02-19 12:17:03 +08:00
function match_pattern(string $pattern, string $subject): bool
{
$pattern = str_replace(['\*', '\\\.*'], ['.*', '\*'], preg_quote($pattern, '/'));
2024-02-19 12:17:03 +08:00
$pattern = '/^' . $pattern . '$/i';
return preg_match($pattern, $subject) === 1;
}
/**
* Quote a string with a quote character
*
* @param string $str String to quote
* @param string $quote Quote character, default: `"`
*/
2023-03-15 20:40:49 +08:00
function quote(string $str, string $quote = '"'): string
{
return $quote . $str . $quote;
}
2023-03-29 21:39:36 +08:00
/**
* Get Family name of current OS.
2023-03-29 21:39:36 +08:00
*/
2023-03-15 20:40:49 +08:00
function osfamily2dir(): string
{
return match (PHP_OS_FAMILY) {
/* @phpstan-ignore-next-line */
'Windows', 'WINNT', 'Cygwin' => 'windows',
'Darwin' => 'macos',
'Linux' => 'linux',
2023-10-15 13:07:13 +08:00
'BSD' => 'freebsd',
2023-03-29 21:39:36 +08:00
default => throw new WrongUsageException('Not support os: ' . PHP_OS_FAMILY),
2023-03-15 20:40:49 +08:00
};
}
function osfamily2shortname(): string
{
return match (PHP_OS_FAMILY) {
2025-06-18 20:57:14 +08:00
'Windows' => 'win',
'Darwin' => 'macos',
'Linux' => 'linux',
'BSD' => 'bsd',
default => throw new WrongUsageException('Not support os: ' . PHP_OS_FAMILY),
};
}
function shell(?bool $debug = null): UnixShell
{
/* @noinspection PhpUnhandledExceptionInspection */
return new UnixShell($debug);
}
function cmd(?bool $debug = null): WindowsCmd
{
/* @noinspection PhpUnhandledExceptionInspection */
return new WindowsCmd($debug);
}
/**
* Get current builder.
*/
function builder(): BuilderBase
{
return BuilderProvider::getBuilder();
}
/**
* Get current patch point.
*/
function patch_point(): string
{
return BuilderProvider::getBuilder()->getPatchPoint();
}
2024-12-05 15:48:09 +08:00
function patch_point_interrupt(int $retcode, string $msg = ''): InterruptException
{
return new InterruptException(message: $msg, code: $retcode);
}
// ------- function f_* part -------
// f_ means standard function wrapper
2023-03-15 20:40:49 +08:00
/**
* Execute the shell command, and the output will be directly printed in the terminal. If there is an error, an exception will be thrown
2023-03-15 20:40:49 +08:00
*/
function f_passthru(string $cmd): ?bool
{
$danger = false;
foreach (DANGER_CMD as $danger_cmd) {
if (str_starts_with($cmd, $danger_cmd . ' ')) {
$danger = true;
break;
}
}
if ($danger) {
logger()->notice('Running dangerous command: ' . $cmd);
} else {
2024-07-09 00:44:03 +08:00
logger()->debug('[PASSTHRU] ' . $cmd);
2023-03-15 20:40:49 +08:00
}
$ret = passthru($cmd, $code);
if ($code !== 0) {
2025-08-06 20:47:48 +08:00
throw new ExecutionException($cmd, "Direct command run failed with code: {$code}", $code);
2023-03-15 20:40:49 +08:00
}
return $ret;
}
/**
* Execute a command, return the output and result code
*/
function f_exec(string $command, mixed &$output, mixed &$result_code): bool|string
2023-03-15 20:40:49 +08:00
{
logger()->debug('Running command (no output) : ' . $command);
return exec($command, $output, $result_code);
}
function f_mkdir(string $directory, int $permissions = 0777, bool $recursive = false): bool
{
if (file_exists($directory)) {
logger()->debug("Dir {$directory} already exists, ignored");
return true;
}
logger()->debug('Making new directory ' . ($recursive ? 'recursive' : '') . ': ' . $directory);
return mkdir($directory, $permissions, $recursive);
}
2023-10-22 22:14:11 +08:00
function f_putenv(string $env): bool
{
logger()->debug('Setting env: ' . $env);
return putenv($env);
}
/**
* Get the installed CMake version
*
2025-06-04 21:22:05 +07:00
* @return null|string The CMake version or null if it couldn't be determined
*/
function get_cmake_version(): ?string
{
try {
2025-08-06 20:47:48 +08:00
[,$output] = shell(false)->execWithResult('cmake --version', false);
if (preg_match('/cmake version ([\d.]+)/i', $output[0], $matches)) {
return $matches[1];
}
} catch (Exception $e) {
logger()->warning('Failed to get CMake version: ' . $e->getMessage());
}
return null;
}
2025-06-09 00:34:25 +08:00
function cmake_boolean_args(string $arg_name, bool $negative = false): array
{
$res = ["-D{$arg_name}=ON", "-D{$arg_name}=OFF"];
return $negative ? array_reverse($res) : $res;
}
2025-06-09 19:32:31 +08:00
function ac_with_args(string $arg_name, bool $use_value = false): array
{
return $use_value ? ["--with-{$arg_name}=yes", "--with-{$arg_name}=no"] : ["--with-{$arg_name}", "--without-{$arg_name}"];
}
2025-07-23 11:52:25 +08:00
2025-07-23 15:33:26 +08:00
function get_pack_replace(): array
2025-07-23 11:52:25 +08:00
{
return [
BUILD_LIB_PATH => '@build_lib_path@',
BUILD_BIN_PATH => '@build_bin_path@',
BUILD_INCLUDE_PATH => '@build_include_path@',
BUILD_ROOT_PATH => '@build_root_path@',
];
}
/**
2025-07-25 10:04:06 +07:00
* Remove duplicate spaces from a string.
*
* @param string $string Input string that may contain unnecessary spaces (e.g., " -la -lb").
* @return string The trimmed string with only single spaces (e.g., "-la -lb").
*/
2025-07-25 10:04:06 +07:00
function clean_spaces(string $string): string
{
return trim(preg_replace('/\s+/', ' ', $string));
}
2025-08-06 20:47:48 +08:00
/**
* Register a callback function to handle keyboard interrupts (Ctrl+C).
*
* @param callable $callback callback function to handle keyboard interrupts
*/
function keyboard_interrupt_register(callable $callback): void
{
if (PHP_OS_FAMILY === 'Windows') {
sapi_windows_set_ctrl_handler($callback);
} elseif (extension_loaded('pcntl')) {
pcntl_signal(SIGINT, $callback);
}
}
/**
* Unregister the keyboard interrupt handler.
*
* This function is used to remove the previously registered keyboard interrupt handler.
* It should be called when you no longer need to handle keyboard interrupts.
*/
function keyboard_interrupt_unregister(): void
{
if (PHP_OS_FAMILY === 'Windows') {
sapi_windows_set_ctrl_handler(null);
} elseif (extension_loaded('pcntl')) {
pcntl_signal(SIGINT, SIG_IGN);
}
}
/**
* Strip ANSI color codes from a string.
*/
function strip_ansi_colors(string $text): string
{
// Regular expression to match ANSI escape sequences
// Including color codes, cursor control, clear screen and other control sequences
return preg_replace('/\e\[[0-9;]*[a-zA-Z]/', '', $text);
}