존재 여부). * * @var array */ private static array $staticFileMemo = []; /** * 현재 자산 URL 모드를 반환합니다. * * 설정 조회가 실패해도(설치 전·마이그레이션 전 등) 예외를 던지지 않고 * 기본 모드로 폴백한다 — 이 값은 blade 렌더 경로에서 읽히므로 여기서 * 터지면 화면 전체가 죽는다. * * @return string `extension` 또는 `extensionless` */ public static function mode(): string { if (self::$modeOverride !== null) { return self::$modeOverride; } try { $mode = g7_core_settings('general.asset_url_mode', self::MODE_EXTENSION); } catch (\Throwable $e) { return self::MODE_EXTENSION; } return $mode === self::MODE_EXTENSIONLESS ? self::MODE_EXTENSIONLESS : self::MODE_EXTENSION; } /** * 현재 모드가 확장자 없는 모드인지 여부를 반환합니다. * * @return bool 확장자 없는 모드이면 true */ public static function isExtensionless(): bool { return self::mode() === self::MODE_EXTENSIONLESS; } /** * 모드를 강제로 지정합니다 (테스트 전용). * * @param string|null $mode 강제할 모드. null 이면 오버라이드 해제 */ public static function forceMode(?string $mode): void { self::$modeOverride = $mode; } /** * 정적 게시(bake) 베이스 경로를 반환합니다 (#122). * * 게이트 3조건 — ① 프로덕션 ② `core.static_cache.enabled` ③ 현재 버전 게시 * 완료(manifest 존재) — 을 전부 통과할 때만 `/build/ext/{v}` 를 반환한다. * 아니면 null (종전 API URL 방출). 요청당 1회 판정 후 메모이즈한다. * * 자가 치유 — 프로덕션인데 현재 버전이 미게시면 terminating 게시를 예약한다. * 이번 응답은 종전 API URL 로 나가고(첫 방문자 1회만 종전 속도), 다음 * 렌더부터 정적 fast path 가 적용된다. * * blade 렌더 경로에서 호출되므로 어떤 실패도 예외로 새 나가면 안 된다. * * @return string|null 정적 베이스 경로 또는 null */ public static function staticExtBase(): ?string { if (self::$staticExtBaseResolved) { return self::$staticExtBaseMemo; } self::$staticExtBaseResolved = true; self::$staticExtBaseMemo = null; try { if (! app()->environment('production')) { return null; } if (! (bool) config('core.static_cache.enabled', true)) { return null; } // 트레이트 정적 메서드 직접 호출(ClearsTemplateCaches::)은 PHP 8.1+ E_DEPRECATED // — 트레이트를 사용하는 클래스 경유로 호출한다 (게이트·게시자가 같은 static // 스토어 메모 슬롯을 공유하게 되는 부수 이점도 있다) $version = ExtensionStaticCacheService::getExtensionCacheVersion(); if (! app(ExtensionStaticCacheService::class)->isPublished($version)) { // 자가 치유 — 응답 종료 후 게시 시도 (실패해도 API 폴백으로 정상) ExtensionStaticCacheService::schedulePublishOnTerminate(); return null; } self::$staticExtBaseMemo = '/build/ext/'.$version; } catch (\Throwable) { return null; } return self::$staticExtBaseMemo; } /** * 정적 게시 베이스 메모를 초기화합니다 (테스트 전용). */ public static function resetStaticExtBaseMemo(): void { self::$staticExtBaseResolved = false; self::$staticExtBaseMemo = null; self::$staticFileMemo = []; } /** * 정적 게시본 내 파일 존재 여부를 확인합니다 (태그 계층 파일 단위 게이트). * * ``/`