유틸리티 라이브러리

모두 정적 메서드이며 외부 의존성이 없습니다. 네임스페이스는 _Flexagon\Libs입니다.

클래스 주요 기능
StringUtil 케이스 변환, UTF-8 정제, hex 인코딩, 랜덤 문자열 생성
ArrayUtil 키 camelCase 변환, 배열 diff, 중첩 배열 키 탐색
ValidUtil 이메일·정수·실수·영숫자·아이디 형식 검증
CryptoUtil AES-256-CBC 암복호화 (문자열 / 배열, IV 랜덤화 지원)
FileUtil 디렉터리 순회, 재귀 생성, 확장자 판별
HttpUtil URL 파싱, 쿼리스트링 분리, 요청 경로 해석
NetUtil GET/POST HTTP 클라이언트, JSON 응답 파싱, 파일 업로드
HtmlUtil 리다이렉트, HTML 태그·속성 제거, 이미지 URL 추출
TimeDateUtil 타임스탬프 변환, 타임존 오프셋, 상대 시간 표기
SqlUtil 파라미터 정규화·확장, SQL 이스케이프, SELECT → COUNT 변환
ClassUtil 클래스명·경로·프로퍼티·호출자 조회
PhpDocUtil PHPDoc 태그 파싱 (어노테이션 기능의 기반)
AssetLoader CSS/JS 관리
TemplateLoader 템플릿 로딩
DataSourceManager 커넥션 풀링 및 전역 트랜잭션
Env 환경변수와 .env.php에서 설정·비밀 값 읽기

전역 헬퍼 함수

Libs/_Util.php가 정의하는 세 함수는 네임스페이스 없이 어디서나 쓸 수 있습니다.

_echo('안녕하세요, {name}님', ['name' => $user->getName()]);   // 치환 후 출력
$msg = _t('{count}건 검색됨', ['count' => 42]);                // 치환 후 반환

___()는 _t()의 별칭입니다. 치환은 한 번만 하므로, 값 안에 {name}이 들어 있어도 다시 치환되지 않습니다.

번역 (I18n)

헬퍼는 치환하기 전에 _Flexagon\I18n\Translator에 등록된 번역기로 문자열을 먼저 번역합니다. 번역기를 등록하지 않으면 원문을 그대로 씁니다.

번역기는 메서드 하나만 구현하면 됩니다.

namespace _Flexagon\I18n;

interface TranslatorInterface
{
    public function translate(string $message): string;
}

요청 초반(public/_entry.php 또는 application/_Config.php)에 한 번 등록합니다.

// public/_entry.php
use _Flexagon\I18n\Translator;

setlocale(LC_MESSAGES, 'ko_KR.UTF-8');
bindtextdomain('messages', LOCALE_ROOT);
textdomain('messages');

Translator::setTranslator(new \I18n\GettextTranslator());

스켈레톤의 application/I18n/에 예제 어댑터 세 개가 들어 있습니다.

클래스 번역 수단 필요한 것
GettextTranslator gettext() ext-gettext
MoTranslator __() __()를 정의하는 라이브러리 (.mo 리더 등)
SymfonyTranslator trans() symfony/translation
Translator::setTranslator(new \I18n\SymfonyTranslator($symfonyTranslator));

LOCALE_ROOT의 기본값은 PROJECT_ROOT . '/locale'입니다. 바꾸려면 프레임워크가 부팅되기 전에 define()하세요.

주의할 점은 다음과 같습니다.

  • 출력은 이스케이프되지 않습니다. _echo()는 번역·치환 결과를 그대로 출력합니다. 사용자 입력 값은 넘기기 전에 htmlspecialchars()로 이스케이프하고, 번역 파일도 HTML 출력원으로 보고 신뢰할 수 있는 것만 쓰세요.
  • 번역기는 프로세스 단위 static입니다. 요청마다 새로 시작하는 PHP-FPM 모델에 맞춘 설계입니다. 상주형 워커(Swoole·RoadRunner·FrankenPHP worker 모드)에서는 요청마다 번역기를 다시 등록하세요. setlocale()은 프로세스 전역이라 스레드 간에 안전하지 않습니다.
  • 비용은 어댑터가 결정합니다. 프레임워크 자체 오버헤드는 호출당 약 0.3µs입니다. gettext는 .mo를 프로세스 단위로 캐시합니다(파일을 바꾸면 PHP-FPM 재시작 필요). .mo 라이브러리 중에는 매 요청 파일을 파싱하는 것도 있고, Symfony Translation은 cache_dir를 설정하지 않으면 매 요청 카탈로그를 다시 읽습니다.

Enum 헬퍼

EnumSupporter 트레이트를 backed enum에 붙이면 편의 메서드가 생깁니다.

use _Flexagon\Supporters\EnumSupporter;

enum UserRole: string {
    use EnumSupporter;

    case ADMIN = 'ADMIN';
    case USER  = 'USER';
}

UserRole::keys();            // ['ADMIN', 'USER']
UserRole::values();          // ['ADMIN', 'USER']
UserRole::toArray();         // ['ADMIN' => 'ADMIN', 'USER' => 'USER']
UserRole::toCommaString();   // 'ADMIN,USER'

ENUM 컬럼 DDL을 만든다면 toQuotedCommaString() 을 쓰세요. 각 값을 따옴표로 감싸고, 값 안의 따옴표는 두 번 겹쳐 이스케이프합니다.

UserRole::toQuotedCommaString();      // "'ADMIN','USER'"
UserRole::toQuotedCommaString('"');   // '"ADMIN","USER"'

// 값에 따옴표나 쉼표가 있어도 안전합니다
// case A = "it's";  case B = 'a,b';
//   toCommaString()        → it's,a,b          (DDL 이 깨짐)
//   toQuotedCommaString()  → 'it''s','a,b'

백킹 값이 없는 순수 enum에서는 keys() 와 toCommaString() 은 케이스 이름을, values() 와 toArray() 는 null 을 돌려줍니다.

enum 타입 프로퍼티는 DAO 매핑에서도 자동으로 처리됩니다. DB의 스칼라 값이 대응하는 케이스로 변환되어 모델에 주입되고, 반대로 저장할 때는 ->value가 바인딩됩니다.