| 1: | <?php
|
| 2: | if(php_sapi_name() !== "cli"){
|
| 3: | echo "This script can only be run from the command line.\n";
|
| 4: | exit;
|
| 5: | }
|
| 6: |
|
| 7: | if(PHP_VERSION_ID < 80000){
|
| 8: | echo "ERROR: PHP Version 8 or newer is required to run PHP-CLI\n";
|
| 9: | sleep(10);
|
| 10: | exit;
|
| 11: | }
|
| 12: | if(PHP_VERSION_ID < 80400){
|
| 13: | echo "Warning: PHP Version 8.4 or newer is recommended\n";
|
| 14: | }
|
| 15: |
|
| 16: | if(!in_array(PHP_OS_FAMILY, ['Windows','Linux'])){
|
| 17: | echo "This can only be run on Windows and some Linux distributions.\n";
|
| 18: | exit;
|
| 19: | }
|
| 20: |
|
| 21: |
|
| 22: | if(!is_dir('logs')){
|
| 23: | if(!mkdir('logs',0777)){
|
| 24: | echo "Error: Unable to create logs directory\n";
|
| 25: | }
|
| 26: | }
|
| 27: | if(is_file('logs/latest.log')){
|
| 28: | if(!unlink('logs/latest.log')){
|
| 29: | echo "Warning: Unable to delete old latest.log\n";
|
| 30: | }
|
| 31: | }
|
| 32: |
|
| 33: | mklog(1,'Starting');
|
| 34: |
|
| 35: | mklog(1,'Loading resources');
|
| 36: | require_once 'resource2.php';
|
| 37: |
|
| 38: | if(is_admin::check()){
|
| 39: | mklog(1,"Starting with administrator permissions");
|
| 40: | }
|
| 41: |
|
| 42: | mklog(1,'Reading start arguments');
|
| 43: |
|
| 44: | if(!isset($fileArguments)){
|
| 45: | $fileArguments = [];
|
| 46: | }
|
| 47: | if(!is_array($fileArguments) || array_is_list($fileArguments)){
|
| 48: | mklog(2,'Unknown fileArguments configuration, ignoring all');
|
| 49: | $fileArguments = [];
|
| 50: | }
|
| 51: |
|
| 52: | $arguments = (function(){
|
| 53: | global $argv;
|
| 54: | global $fileArguments;
|
| 55: |
|
| 56: |
|
| 57: | $defaultArguments = [
|
| 58: | 'verbose-logging' => false,
|
| 59: | 'use-file-as-input' => false,
|
| 60: | 'file-as-input-delay' => 10,
|
| 61: | 'no-loop' => false,
|
| 62: | 'command' => false,
|
| 63: | 'json-read-cache-timeout' => 1,
|
| 64: | 'json-url-read-cache-timeout' => 5,
|
| 65: | 'check-syntax' => false,
|
| 66: | 'sleep-on-error' => 0
|
| 67: | ];
|
| 68: |
|
| 69: | $lineArguments = [];
|
| 70: |
|
| 71: | $argvCount = count($argv);
|
| 72: | if($argvCount %2 === 0){
|
| 73: |
|
| 74: | mklog(2, 'Missmach in arguments provided via commandline, ignoring last item');
|
| 75: |
|
| 76: |
|
| 77: | $argvCount--;
|
| 78: | }
|
| 79: |
|
| 80: |
|
| 81: | for($i = 1; $i < $argvCount; $i++){
|
| 82: |
|
| 83: | $lineArguments[$argv[$i]] = $argv[$i+1];
|
| 84: | $i++;
|
| 85: | }
|
| 86: |
|
| 87: | foreach($lineArguments as $lineArgumentName => $lineArgumentValue){
|
| 88: | $lineArguments[$lineArgumentName] = data_types::convert_string($lineArgumentValue);
|
| 89: | }
|
| 90: |
|
| 91: |
|
| 92: |
|
| 93: | $arguments = [];
|
| 94: |
|
| 95: | foreach($defaultArguments as $defaultArgumentName => $defaultArgumentValue){
|
| 96: |
|
| 97: | $arguments[$defaultArgumentName] = $defaultArgumentValue;
|
| 98: |
|
| 99: | if(isset($fileArguments[$defaultArgumentName])){
|
| 100: |
|
| 101: | $arguments[$defaultArgumentName] = $fileArguments[$defaultArgumentName];
|
| 102: | }
|
| 103: |
|
| 104: | if(isset($lineArguments[$defaultArgumentName])){
|
| 105: |
|
| 106: | $arguments[$defaultArgumentName] = $lineArguments[$defaultArgumentName];
|
| 107: | }
|
| 108: |
|
| 109: |
|
| 110: | if(verboseLogging()){
|
| 111: | if(is_bool($arguments[$defaultArgumentName])){
|
| 112: | $currentArgumentLogValue = $arguments[$defaultArgumentName] ? "true" : "false";
|
| 113: | }
|
| 114: | else{
|
| 115: | $currentArgumentLogValue = $arguments[$defaultArgumentName];
|
| 116: | }
|
| 117: |
|
| 118: | $type = gettype($arguments[$defaultArgumentName]);
|
| 119: |
|
| 120: | $colors = ["boolean"=>"light_blue", "integer"=>"light_green", "double"=>"light_green", "string"=>"light_red"];
|
| 121: | $color = false;
|
| 122: | if(isset($colors[$type])){
|
| 123: | $color = $colors[$type];
|
| 124: | }
|
| 125: |
|
| 126: | $defaultArgumentNameFormatted = cli_formatter::formatLine($defaultArgumentName, "white", false, false);
|
| 127: | $currentArgumentLogValueFormatted = cli_formatter::formatLine($currentArgumentLogValue, $color, false, false);
|
| 128: | $typeFormatted = cli_formatter::formatLine($type, "white", false, false);
|
| 129: |
|
| 130: | mklog(
|
| 131: | 0,
|
| 132: | 'Argument ' . $defaultArgumentName . ' is set to ' . $currentArgumentLogValue . ' with data type of ' . $type,
|
| 133: | $color ? 'Argument ' . $defaultArgumentNameFormatted . ' is set to ' . $currentArgumentLogValueFormatted . ' with data type of ' . $typeFormatted : ''
|
| 134: | );
|
| 135: | }
|
| 136: | }
|
| 137: |
|
| 138: | return $arguments;
|
| 139: | })();
|
| 140: |
|
| 141: | unset($fileArguments);
|
| 142: |
|
| 143: |
|
| 144: |
|
| 145: | echo "\033]0;PHP-CLI: " . getcwd() . "\007";
|
| 146: |
|
| 147: | require_once 'main.php';
|
| 148: |
|
| 149: | |
| 150: | |
| 151: | |
| 152: | |
| 153: | |
| 154: | |
| 155: | |
| 156: | |
| 157: | |
| 158: |
|
| 159: | function mklog(int|string $type, string $message, string|bool $formattedMessage=''):void{
|
| 160: |
|
| 161: | if(!is_int($type)){
|
| 162: | mklog(0, 'The following log used an outdated version of mklog()');
|
| 163: |
|
| 164: | $type = substr(strtolower($type), 0, 1);
|
| 165: |
|
| 166: | if($type === "e"){$type = 3;}
|
| 167: | elseif($type === "w"){$type = 2;}
|
| 168: | else{$type = 1;}
|
| 169: |
|
| 170: | if($formattedMessage === true){
|
| 171: | $type = 0;
|
| 172: | }
|
| 173: | }
|
| 174: |
|
| 175: | if(!is_string($formattedMessage)){
|
| 176: | $formattedMessage = '';
|
| 177: | }
|
| 178: |
|
| 179: |
|
| 180: |
|
| 181: | $type = min(max($type,0),3);
|
| 182: |
|
| 183: | $verboseloggingsetting = verboseLogging();
|
| 184: |
|
| 185: | if($type || $verboseloggingsetting){
|
| 186: |
|
| 187: | $prefix = date("Y-m-d_H:i:s:") . substr(floor(microtime(true)*1000), -3) . ": " . ["Verbose","General","Warning","Error"][$type] . ": ";
|
| 188: |
|
| 189: | $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
| 190: | if(isset($trace[1]['class'])){
|
| 191: | $prefix .= $trace[1]['class'] . ': ';
|
| 192: | }
|
| 193: |
|
| 194: | $cliFormatterExists = class_exists('cli_formatter');
|
| 195: |
|
| 196: |
|
| 197: | $colour = "normal";
|
| 198: | if($type && $verboseloggingsetting){
|
| 199: | $colour = "light_green";
|
| 200: | }
|
| 201: | if($type === 2){$colour = "yellow";}
|
| 202: | if($type === 3){$colour = "red";}
|
| 203: |
|
| 204: | if(!empty($formattedMessage)){
|
| 205: | echo $prefix . $formattedMessage . "\n";
|
| 206: | }
|
| 207: | elseif($colour !== "normal" && $cliFormatterExists){
|
| 208: | echo cli_formatter::formatLine($prefix . $message, $colour);
|
| 209: | }
|
| 210: | else{
|
| 211: | echo $prefix . $message . "\n";
|
| 212: | }
|
| 213: |
|
| 214: |
|
| 215: | $stream = fopen('logs/latest.log','a');
|
| 216: | if(!$stream){
|
| 217: | echo "Error: Unable to open latest.log\n";
|
| 218: | }
|
| 219: | elseif(!fwrite($stream, $prefix . $message . "\n")){
|
| 220: | echo "Error: Unable to write to latest.log\n";
|
| 221: | }
|
| 222: | elseif(!fclose($stream)){
|
| 223: | echo "Error: Unable to save latest.log\n";
|
| 224: | }
|
| 225: |
|
| 226: | $stream = fopen('logs/log-' . date("Y-m") . '.txt','a');
|
| 227: | if(!$stream){
|
| 228: | echo "Error: Unable to open logs file\n";
|
| 229: | }
|
| 230: | elseif(!fwrite($stream, $prefix . $message . "\n")){
|
| 231: | echo "Error: Unable to write to logs file\n";
|
| 232: | }
|
| 233: | elseif(!fclose($stream)){
|
| 234: | echo "Error: Unable to save logs file\n";
|
| 235: | }
|
| 236: | }
|
| 237: |
|
| 238: | if($type === 3){
|
| 239: | if(isset($cliFormatterExists) && $cliFormatterExists){
|
| 240: | cli_formatter::ding();
|
| 241: | }
|
| 242: | if($GLOBALS['arguments']['sleep-on-error'] > 0){
|
| 243: | sleep($GLOBALS['arguments']['sleep-on-error']);
|
| 244: | }
|
| 245: | }
|
| 246: | }
|
| 247: | |
| 248: | |
| 249: | |
| 250: | |
| 251: |
|
| 252: | function verboseLogging():bool{
|
| 253: | if(isset($GLOBALS['arguments']['verbose-logging'])){
|
| 254: | if($GLOBALS['arguments']['verbose-logging']){
|
| 255: | return true;
|
| 256: | }
|
| 257: | }
|
| 258: | if(isset($GLOBALS['fileArguments']['verbose-logging'])){
|
| 259: | if($GLOBALS['fileArguments']['verbose-logging']){
|
| 260: | return true;
|
| 261: | }
|
| 262: | }
|
| 263: | return false;
|
| 264: | }
|
| 265: | |
| 266: | |
| 267: | |
| 268: | |
| 269: |
|
| 270: | function argsString():string{
|
| 271: | global $argv;
|
| 272: | return implode(' ', array_map('escapeshellarg', array_slice($argv, 1)));
|
| 273: | }
|
| 274: |
|
| 275: | |
| 276: | |
| 277: | |
| 278: | |
| 279: |
|
| 280: | function getEnvironment():array{
|
| 281: | $isWindows = PHP_OS_FAMILY === 'Windows';
|
| 282: | $isLinux = PHP_OS_FAMILY === 'Linux';
|
| 283: |
|
| 284: |
|
| 285: | $env = static fn(string $key): string|false => getenv($key);
|
| 286: |
|
| 287: |
|
| 288: | $hasTty = false;
|
| 289: | if(function_exists('stream_isatty')){
|
| 290: | $hasTty = @stream_isatty(STDIN) || @stream_isatty(STDOUT);
|
| 291: | }
|
| 292: | elseif(function_exists('posix_isatty')){
|
| 293: | $hasTty = @posix_isatty(STDIN) || @posix_isatty(STDOUT);
|
| 294: | }
|
| 295: |
|
| 296: |
|
| 297: | $isSSH = !empty($env('SSH_CLIENT')) || !empty($env('SSH_TTY'));
|
| 298: |
|
| 299: |
|
| 300: | $isDesktop = false;
|
| 301: | $isRemoteDesktop = false;
|
| 302: | $isModernTerminal = false;
|
| 303: | $isCompatLayer = false;
|
| 304: |
|
| 305: | if($isWindows){
|
| 306: |
|
| 307: |
|
| 308: |
|
| 309: | $clientName = $env('CLIENTNAME');
|
| 310: | $isRemoteDesktop = !empty($clientName) && strtoupper($clientName) !== 'CONSOLE';
|
| 311: |
|
| 312: |
|
| 313: |
|
| 314: | $isDesktop = $hasTty && !$isSSH;
|
| 315: |
|
| 316: |
|
| 317: | $isModernTerminal =
|
| 318: | !empty($env('WT_SESSION')) ||
|
| 319: | !empty($env('WT_PROFILE_ID')) ||
|
| 320: | !empty($env('ConEmuPID')) ||
|
| 321: | !empty($env('TERM_PROGRAM')) ||
|
| 322: | !empty($env('ANSICON'));
|
| 323: |
|
| 324: |
|
| 325: | $isCompatLayer =
|
| 326: | !empty($env('MSYSTEM')) ||
|
| 327: | !empty($env('CYGWIN'));
|
| 328: |
|
| 329: | }
|
| 330: | elseif($isLinux){
|
| 331: |
|
| 332: | $hasDisplay = !empty($env('DISPLAY')) || !empty($env('WAYLAND_DISPLAY'));
|
| 333: |
|
| 334: | $hasDesktopSession = !empty($env('DESKTOP_SESSION')) || !empty($env('XDG_CURRENT_DESKTOP')) || !empty($env('GNOME_DESKTOP_SESSION_ID')) || !empty($env('KDE_FULL_SESSION'));
|
| 335: |
|
| 336: | $isDesktop = $hasDisplay && $hasDesktopSession && !$isSSH;
|
| 337: |
|
| 338: |
|
| 339: | $isRemoteDesktop = !empty($env('XRDP_SESSION')) || !empty($env('X2GO_SESSION'));
|
| 340: |
|
| 341: |
|
| 342: | $term = $env('TERM');
|
| 343: | $termProgram = $env('TERM_PROGRAM');
|
| 344: |
|
| 345: | $isModernTerminal = !empty($env('VTE_VERSION')) || !empty($termProgram) ||
|
| 346: | in_array(
|
| 347: | $term,
|
| 348: | [
|
| 349: | 'xterm-256color',
|
| 350: | 'screen-256color',
|
| 351: | 'tmux-256color',
|
| 352: | 'alacritty',
|
| 353: | 'wezterm',
|
| 354: | ],
|
| 355: | true
|
| 356: | );
|
| 357: |
|
| 358: |
|
| 359: | $isCompatLayer = !empty($env('WINEPREFIX')) || file_exists('/proc/sys/fs/binfmt_misc/WSLInterop');
|
| 360: |
|
| 361: | }
|
| 362: | else{
|
| 363: | $isDesktop = $hasTty && !$isSSH;
|
| 364: | }
|
| 365: |
|
| 366: | return [
|
| 367: |
|
| 368: |
|
| 369: | 'windows' => $isWindows,
|
| 370: | 'linux' => $isLinux,
|
| 371: |
|
| 372: |
|
| 373: | 'desktop' => $isDesktop,
|
| 374: | 'headless' => !$isDesktop,
|
| 375: | 'remote_desktop' => $isRemoteDesktop,
|
| 376: | 'ssh' => $isSSH,
|
| 377: | 'modern_terminal' => $isModernTerminal,
|
| 378: | 'compat_layer' => $isCompatLayer,
|
| 379: |
|
| 380: |
|
| 381: | 'tty' => $hasTty,
|
| 382: | 'interactive' => $hasTty && !$isSSH,
|
| 383: | 'daemon_or_cron' => !$hasTty && !$isSSH,
|
| 384: | ];
|
| 385: | } |