requestId; } public function getStartTimestamp(): float { return $this->startTimestamp; } public function getPlatform(): string { return $this->platform; } public function getScript(): string { return $this->script; } public function getLogSequence(): int { return $this->logSequence; } public function isPlatformValid(): bool { return in_array($this->platform, self::PLATFORMS); } public function isPlatformAdmin(): bool { return $this->platform == self::PLATFORM_ADMIN; } public function isPlatformUser(): bool { return $this->platform == self::PLATFORM_USER; } public function isScriptAnnounce(): bool { return $this->script == 'announce'; } public function incrementLogSequence(): void { $this->logSequence++; } private function getFirst(string $result): string { if (str_contains($result, ",")) { return strstr($result, ",", true); } return $result; } public function getRequestSchema(): string { $schema = $this->retrieveFromServer(['HTTP_X_FORWARDED_PROTO', 'REQUEST_SCHEME', 'HTTP_SCHEME']); if (empty($schema)) { $https = $this->retrieveFromServer(['HTTPS']); if ($https == 'on') { $schema = 'https'; } } return $this->getFirst($schema); } public function getRequestHost(): string { $host = $this->retrieveFromServer(['HTTP_X_FORWARDED_HOST', 'HTTP_HOST', 'host'], true); return $this->getFirst(strval($host)); } public function getRequestIp(): string { $ip = $this->retrieveFromServer(['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'x-forwarded-for', 'HTTP_REMOTE_ADDR', 'REMOTE_ADDR'], true); return $this->getFirst($ip); } private function retrieveFromServer(array $fields, bool $includeHeader = false) { if ($this->runningInOctane()) { $servers = request()->server(); $headers = request()->header(); } else { $servers = $_SERVER; $headers = getallheaders(); } foreach ($fields as $field) { $result = $servers[$field] ?? null; if ($result !== null && $result !== '') { return $result; } if ($includeHeader) { $result = $headers[$field] ?? null; if (is_array($result)) { $result = Arr::first($result); } if ($result !== null && $result !== '') { return $result; } } } } private function runningInOctane(): bool { if (defined('RUNNING_IN_OCTANE') && RUNNING_IN_OCTANE) { return true; } return false; } private function generateRequestId(): string { $prefix = ($_SERVER['SCRIPT_FILENAME'] ?? '') . implode('', $_SERVER['argv'] ?? []); $prefix = substr(md5($prefix), 0, 4); // 4 + 23 = 27 characters, after replace '.', 26 $requestId = str_replace('.', '', uniqid($prefix, true)); $requestId .= bin2hex(random_bytes(3)); return $requestId; } public static function boot() { if (self::$booted) { // file_put_contents('/tmp/reset.log', "booted\n",FILE_APPEND); return; } // file_put_contents('/tmp/reset.log', "booting\n",FILE_APPEND); $instance = new self(); $instance->setStartTimestamp(); $instance->setRequestId(); $instance->setScript(); $instance->setPlatform(); self::$instance = $instance; self::$booted = true; } public static function flush() { self::$booted = false; } private function setRequestId() { $requestId = $this->retrieveFromServer(['HTTP_X_REQUEST_ID', 'REQUEST_ID', 'Request-Id', 'request-id'], true); if (empty($requestId)) { $requestId = $this->generateRequestId(); } $this->requestId = (string)$requestId; } private function setScript() { $script = $this->retrieveFromServer(['SCRIPT_FILENAME', 'SCRIPT_NAME', 'Script', 'script'], true); if (str_contains($script, '.')) { $script = strstr(basename($script), '.', true); } $this->script = (string)$script; } private function setStartTimestamp() { $this->startTimestamp = microtime(true); } private function setPlatform() { $this->platform = (string)$this->retrieveFromServer(['HTTP_PLATFORM', 'Platform', 'platform'], true); } public static function js(string $js, string $position, bool $isFile, $key = null) { if ($isFile) { $append = sprintf('', $js); } else { $append = sprintf('', $js); } self::appendJsCss($append, $position, $key); } public static function css(string $css, string $position, bool $isFile, $key = null) { if ($isFile) { $append = sprintf('', $css); } else { $append = sprintf('', $css); } self::appendJsCss($append, $position, $key); } private static function appendJsCss($append, $position, $key = null) { $log = "position: $position, key: $key"; if ($key === null) { $key = md5($append); $log .= ", md5 key: $key"; } if ($position == 'header') { if (!isset(self::$appendHeaders[$key])) { self::$appendHeaders[$key] = $append; } else { do_log("$log, [DUPLICATE]"); } } elseif ($position == 'footer') { if (!isset(self::$appendFooters[$key])) { self::$appendFooters[$key] = $append; } else { do_log("$log, [DUPLICATE]"); } } else { throw new \InvalidArgumentException("Invalid position: $position"); } } public static function getAppendHeaders(): array { return self::$appendHeaders; } public static function getAppendFooters(): array { return self::$appendFooters; } public static function addTranslationNamespace($path, $namespace): void { if (empty($namespace)) { throw new \InvalidArgumentException("namespace can not be empty"); } self::$translationNamespaces[$namespace] = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; if (IN_NEXUS) { //只有 Nexus 下需要,Laravel 下是通过 configurePackage 中 hasTranslations() 加载的 self::getTranslator()->addNamespace($namespace, $path); } } public static function trans($key, $replace = [], $locale = null) { if (is_null($locale)) { $locale = get_langfolder_cookie(true); } if (IN_NEXUS) { return self::getTranslator()->trans($key, $replace, $locale); } else { return trans($key, $replace, $locale); } // if (empty(self::$translations)) { // //load from default lang dir // $langDir = ROOT_PATH . 'resources/lang/'; // self::loadTranslations($langDir); // //load from namespace // foreach (self::$translationNamespaces as $namespace => $path) { // self::loadTranslations($path, $namespace); // } // } // return self::getTranslation($key, $replace, $locale ?? get_langfolder_cookie(true)); } private static function loadTranslations($path, $namespace = null) { do_log("path: $path, namespace: $namespace", 'debug'); $files = glob($path . '*/*'); foreach ($files as $file) { if (!is_file($file)) { do_log("file: $file, is not file", 'debug'); continue; } if (!is_readable($file)) { do_log("[TRANSLATION_FILE_NOT_READABLE], $file"); } $values = require $file; $setKey = substr($file, strlen($path)); if (substr($setKey, -4) == '.php') { $setKey = substr($setKey, 0, -4); } $setKey = str_replace('/', '.', $setKey); if ($namespace !== null) { $setKey = "$namespace.$setKey"; } do_log("path: $path, namespace: $namespace, file: $file, setKey: $setKey", 'debug'); arr_set(self::$translations, $setKey, $values); } } private static function getTranslation($key, $replace = [], $locale = null) { if (!$locale) { $lang = get_langfolder_cookie(); $locale = \App\Http\Middleware\Locale::$languageMaps[$lang] ?? 'en'; } $getKey = self::getTranslationGetKey($key, $locale); $result = arr_get(self::$translations, $getKey); if (empty($result) && $locale != 'en') { do_log("original getKey: $getKey can not get any translations", 'error'); $getKey = self::getTranslationGetKey($key, 'en'); $result = arr_get(self::$translations, $getKey); } if (!empty($replace)) { $search = array_map(function ($value) {return ":$value";}, array_keys($replace)); $result = str_replace($search, array_values($replace), $result); } do_log("key: $key, replace: " . nexus_json_encode($replace) . ", locale: $locale, getKey: $getKey, result: $result", 'debug'); return $result; } private static function getTranslationGetKey($key, $locale): string { $namespace = strstr($key, '::', true); if ($namespace !== false) { $getKey = sprintf('%s.%s.%s', $namespace, $locale, substr($key, strlen($namespace) + 2)); } else { $getKey = $locale . "." . $key; } // do_log("key: $key, locale: $locale, namespace: $namespace, getKey: $getKey", 'debug'); return $getKey; } private static function getTranslator(): NexusTranslator { if (is_null(self::$translator)) { self::$translator = new NexusTranslator(Locale::getDefault()); } return self::$translator; } private static function getQueueManager(): Manager { if (is_null(self::$queueManager)) { $container = Container::getInstance(); $redisConfig = nexus_config('nexus.redis'); $redisConnectionName = "my_redis_connection"; $container->singleton('redis', function ($app) use ($redisConfig, $redisConnectionName) { $redisDriver = "phpredis"; // 这里的配置应该匹配 redis.php 配置文件中的 default 连接 $connectionConfig = [ 'client' => $redisDriver, $redisConnectionName => $redisConfig ]; return new RedisManager($app, $redisDriver, $connectionConfig); }); $queueManager = new Manager($container); $queueManager->addConnection([ 'driver' => 'redis', 'host' => $redisConfig['host'], 'password' => $redisConfig['password'], 'queue' => 'nexus_queue', // 队列名称 'connection' => $redisConnectionName, // Redis 连接名称,类似注册的 'redis' 服务中的 'default' ], self::QUEUE_CONNECTION_NAME); // 将这个 queue 连接起个不一样的名字 $queueManager->setAsGlobal(); self::$queueManager = $queueManager; } return self::$queueManager; } public static function dispatchQueueJob(ShouldQueue $job): void { self::getQueueManager()->connection(self::QUEUE_CONNECTION_NAME)->push($job); do_log("dispatchQueueJob: " . nexus_json_encode($job)); } }