Files
Gnuboard7/app/Services/LanguagePack/CustomTranslationUsageScanner.php
T
2026-07-01 10:30:32 +09:00

95 lines
3.4 KiB
PHP

<?php
namespace App\Services\LanguagePack;
/**
* 레이아웃 콘텐츠에서 참조되는 커스텀 다국어 키 스캐너.
*
* 레이아웃 content(JSON 디코드 배열)를 깊이 우선으로 순회하며 모든 문자열
* 값에서 `$t:custom.{layout}.{seq}` 형태로 참조되는 커스텀 키를 수집합니다.
* 노드의 `text` 뿐 아니라 props 값·표현식 내 `$t:custom.` 문자열도 보수적으로
* 전부 스캔합니다(인라인 편집은 `text` 에만 쓰지만, 코드 편집기 경유 props/표현식
* 참조도 보존 — 과소 orphaned 방지).
*
* DB 무접근 순수 함수이므로 단위 테스트가 용이합니다. 좀비(고아) 키 자동 감지
* 리스너(MarkOrphanedCustomTranslations)가 본 스캐너로 "사용 중" 키 집합을 얻어
* 미참조 키를 orphaned 로 전이시킵니다.
*
* @since engine-v1.54.0
*/
class CustomTranslationUsageScanner
{
/**
* `$t:custom.{layoutKey}.{seq}` 매칭 정규식.
*
* - layoutKey: 영숫자/언더스코어/하이픈 (normalizeLayoutKey 출력과 동형)
* - seq: 숫자
*/
private const CUSTOM_KEY_RE = '/\$t:(custom\.[A-Za-z0-9_\-]+\.\d+)/';
/**
* 레이아웃 content 에서 참조되는 커스텀 키 목록을 수집합니다.
*
* @param mixed $content 레이아웃 content (배열/객체/문자열 — 보수적으로 전체 순회)
* @return array<int, string> 참조된 커스텀 키 목록 (중복 제거, 예: ['custom.home.1'])
*/
public function collectReferencedKeys(mixed $content): array
{
$keys = [];
$this->walk($content, $keys);
return array_values(array_unique($keys));
}
/**
* 레이아웃 이름을 키 네임스페이스로 정규화합니다 (커스텀 키 생성/매칭 SSoT).
*
* `board/list` → `board_list`. 슬래시/공백을 언더스코어로 치환하고
* 키에 안전한 문자만 남깁니다. 키 저장(TemplateCustomTranslationService::createKey)과
* 스캐너의 layoutKey 추출이 동일 정규화를 공유하도록 단일 SSoT 로 둡니다.
*
* @param string $layoutName 레이아웃 이름
* @return string 정규화된 키 네임스페이스
*/
public static function normalizeLayoutKey(string $layoutName): string
{
$normalized = preg_replace('/[\/\s]+/', '_', trim($layoutName));
$normalized = preg_replace('/[^A-Za-z0-9_\-]/', '', (string) $normalized);
return $normalized === '' ? 'layout' : $normalized;
}
/**
* 값을 깊이 우선 순회하며 문자열에서 커스텀 키를 누적합니다.
*
* @param mixed $value 순회 대상 값
* @param array<int, string> $keys 누적 키 (참조 전달)
*/
private function walk(mixed $value, array &$keys): void
{
if (is_string($value)) {
if (str_contains($value, '$t:custom.') && preg_match_all(self::CUSTOM_KEY_RE, $value, $matches)) {
foreach ($matches[1] as $key) {
$keys[] = $key;
}
}
return;
}
if (is_array($value)) {
foreach ($value as $item) {
$this->walk($item, $keys);
}
return;
}
if (is_object($value)) {
foreach (get_object_vars($value) as $item) {
$this->walk($item, $keys);
}
}
}
}