This project is licensed under the PHP License v3.01 (inheriting the original license from CUBRID's native PHP extension).
- Commercial Use: Allowed (Permissive License)
- Source Code Closure: You are NOT required to open your application's source code when using or distributing this extension.
- For more details, please refer to the LICENSE and NOTICE files.
Textcube Cubrid 어댑터 점검용. 업스트림 드라이버는 PHP 7.x 까지만 빌드되므로 PHP 8 지원 패치를 직접 작성했다. 대상: PHP 7.4 / 8.2 / 8.4 / 8.5, CUBRID 11.4 서버.
| 경로 | 내용 |
|---|---|
Containerfile |
localhost/php-db:<V> 위에 두 확장을 빌드한 이미지(localhost/php-cubrid:<V>) |
build_ext.sh |
컨테이너 안 빌드 스크립트(패치 적용 → phpize/configure/make → 활성화) |
patches/ |
cubrid-php.patch, cubrid-pdo.patch (기준 커밋은 patches/README.txt) |
src/ |
업스트림 원본(CUBRID/cubrid-php, cubrid-pdo develop + cci-src 서브모듈). 수정하지 않음 |
work/ |
패치 작업본(git 작업 트리) — 패치는 여기서 생성. 빌드·시험 로그 work/log/ |
server_setup.sh |
CUBRID 서버 컨테이너 tccub-db 기동 + SSL 브로커(33100) 추가 |
dev_compile.sh |
작업본 반복 컴파일(-Wall) · 단일 확장 smoke |
run_tests.sh |
이미지별 시험 일괄 실행(native · smoke · persist · close · lob · reuse · adapter native/pdo) |
tests/smoke.php |
네이티브·PDO 기본 동작 |
tests/close.php |
네이티브 cubrid_close_request 뒤 변수 해제(시스템 malloc USE_ZEND_ALLOC=0 으로 실행) |
tests/lob.php |
네이티브 LOB resource 닫기(cubrid_lob_get/cubrid_lob_close·cubrid_lob2_close·LOB 를 가진 요청·연결 닫기, USE_ZEND_ALLOC=0) |
tests/reuse.php |
네이티브 연결(영구 연결 포함)을 닫은 뒤의 요청·LOB·열 정보, prepared 재실행의 열별 상태(USE_ZEND_ALLOC=0) |
tests/native.php |
네이티브 cubrid_* 함수 전반(출력을 원본 7.4 확장과 diff 로 비교) |
tests/persist.php |
PDO 영구 연결의 요청 간 재사용(php -S) |
tests/adapter.php |
Textcube framework/data/Cubrid/Adapter.php 실서버 점검($database['driver'] native · pdo) |
# 1) 서버 (네트워크 tcpgnet, DB tc / 계정 tc / 암호 tcpw, 브로커 33000 · SSL 33100)
sh cubrid_pdo/server_setup.sh
# 2) 이미지 (V = 7.4 | 8.2 | 8.4 | 8.5)
podman build --build-arg V=8.5 -t localhost/php-cubrid:8.5 -f cubrid_pdo/Containerfile cubrid_pdo
# 업스트림 그대로(비교 기준): --build-arg V=7.4 --build-arg PATCH=0 -t localhost/php-cubrid:7.4-orig (PHP 7.4 에서만 빌드됨)
# 3) 시험 (결과: cubrid_pdo/work/log/test-<태그>-<시험>.txt)
sh cubrid_pdo/run_tests.sh # 7.4-orig 7.4 8.2 8.4 8.5 전부
sh cubrid_pdo/run_tests.sh 8.5 # 하나만
# 작업본 수정 후 빠른 컴파일·점검
sh cubrid_pdo/dev_compile.sh 8.5 cubrid-pdo testtests/native.php 는 마지막에 의도적으로 Fatal(없는 클래스로 cubrid_fetch_object)을 내므로 종료코드 255 가 정상.
버전 간 비교는 LOB 파일 이름·CAS 정보를 지우고 diff:
sed -e 's#file:ces_[0-9]*/tc\.tc_nt\.[0-9_]*#file:<lob>#' -e 's#\[CAS INFO-[^]]*\]#[CAS INFO]#g'
공통: config.m4 가 PHP 7 이상에서 *7.c 소스를 쓰도록 변경, TSRMLS 등 PHP 8 에서 없어진 매크로 보완.
네이티브 (cubrid-php)
- PHP 8 API:
fci.named_params,zend_ce_exception,ZSTR_VAL(), 별칭 함수 arginfo 누락 cubrid_put숫자 값 변환이 포인터 주소를 변환하던 문제cubrid_put($c, $oid, array): 빈 배열·정수 키에서 초기화 안 된 메모리 해제·NULL 역참조로 프로세스 종료, 빈 배열 SET 값이 앞 속성의 SET 을 재사용해 이중 해제 → 각각 오류 반환 / NULL 저장true/false값이 NULL 로 저장(PHP 7 에는 단일 bool 형식이 없어_IS_BOOL비교가 항상 거짓) → PHP 5 원본처럼 "Invalid type" 오류- 배열의 참조 원소(
&$x)가 NULL 로 저장(SET 은 "Invalid type") → 참조를 풀어 값 사용 cubrid_put·SET 값·cubrid_get속성 이름 배열·cubrid_db_name이 호출자 배열을 제자리 변환 (스크립트의 리터럴 배열, opcache 공유 메모리의 배열 포함) → 복사본을 변환, 배열 내부 포인터도 건드리지 않음close_cubrid_connect초기화 안 된 포인터 사용- 연결 URL 버퍼 NUL 종료 누락(
strncpy) cubrid_fetch_object클래스 이름 처리(문자열 아닌 값·경고 메시지),fci.object형식 오류cubrid_close_request: PHP 변수가 가진 resource 참조까지 놓아(zend_list_delete), 다른 질의를 실행한 뒤(기본 요청이 아닌 요청) 닫으면 변수가 해제된 메모리를 가리킴 → 기본 요청의 참조만 놓고zend_list_close로 닫음. 요청 resource 가 아니면(이미 닫힘·연결·resource 아님) false- LOB resource:
cubrid_lob_get이 반환 배열을,cubrid_lob_close가 인자 배열을 resource 로 다룸(메모리 손상),cubrid_lob2_close가 resource 를 지워 변수가 해제된 메모리를 가리킴,cubrid_lob_getLOB 해제 함수가 비어 있음(누수), 요청의 LOB 목록·연결을 닫은 뒤의 LOB 가 해제된 LOB·연결을 가리킴 → 각 LOB 를zend_list_close로 닫고(닫힌 resource 형식 'Unknown') 해제 함수에서 CCI LOB 해제, 요청 목록에서 뺌, 연결마다 LOB2 목록을 두고 연결을 닫을 때 떼어냄 - 연결 닫기:
close_cubrid_pconnect가 남은 요청의 연결·핸들을 비우지 않아cubrid_pconnect연결을 닫은 뒤 요청·LOB 가 해제된 연결을 가리킴(목록 노드 누수), 요청의 열 정보가 연결을 끊을 때 CCI 가 해제한 메모리를 가리킴, 연결이 닫힌 요청의cubrid_affected_rows·LOB 형식cubrid_bindNULL 역참조 → 일반·영구 연결이 같은 정리(detach_cubrid_requests, 열 정보는 열 0개)를 거침, 닫힌 연결이면 'Invalid connection handle'(-20002) 오류와 -1 / false - prepared 재실행(
cubrid_execute($req))이 열별 목록(lob2_list·field_lengths)을 이전 결과 크기로 둠(열 수가 늘면 범위 밖 쓰기), 이전 결과 마지막 행 LOB 가 계속 유효 →cubrid_next_result처럼 정리(field_lengths는 있던 경우에만 새 열 수로)
PDO (cubrid-pdo)
- PHP 8.1+ PDO 드라이버/문장 API 대응(handler 시그니처,
get_col새 형식,PDO_PARAM_ZVAL·param_type제거, 스트림ssize_t) - 생성자 정수 키 옵션(
ATTR_ERRMODE등)이 거부되던 문제 - 오류 코드 부호, NULL 오류 메시지, 초기화 안 된 오류 구조체,
cci_get_db_version반환값 미확인 - 연결 URL: 사용자 없을 때 "(null)" 전송, 암호를 URL 에 넣어
:?가 든 암호가 다른 속성으로 해석되던 문제 → 암호·계정은 URL 밖 인자로 exec()·check_liveness요청 핸들 누수, 문장 목록(stmt_list) 무한 증가, 변환 SQL 누수, 연결 실패 시 누수- 영구 연결: 요청 메모리의
stmt_list를 다음 요청에서 사용 → 요청 종료 시 정리 - AUTOCOMMIT 속성: bool 값을 정수로 읽음 / 정수가 아닌 값의
Z_LVAL_P getColumnMeta: NULL 문자열 처리. PHP 8 코어가 빼버린pdo_type·len을 PHP 7 과 같은 규칙·키 순서로 채움- SET 바인딩: 호출자 배열을 변경, 집합 형식 미초기화, BIT 변환 버퍼 크기·해제 대상 오류,
str2bit누수 - LOB 바인딩: 읽기 오류 시 무한 반복, 오류 시 사용자 스트림을 닫음, 실행 실패 시 LOB 누수, 한 문장의 LOB 파라미터가 여럿이면 마지막 것만 보관해 나머지 CCI LOB 핸들이 해제되지 않던 문제 → 목록으로 보관해 실행마다 모두 해제
PDO::quote()는 escape 만 하고 따옴표를 붙이지 않는다(업스트림 규약).'로 직접 감쌀 것.PARAM_LOB에 문자열을 주면 그 문자열을 파일 경로로 열어 내용을 넣는다(업스트림 기능). 사용자 입력을 이렇게 바인딩하면 안 된다.- 네이티브 OID 함수(
cubrid_put·cubrid_get등)는DONT_REUSE_OID로 만든 테이블에서만 동작한다. CUBRID 10 이상의 기본 테이블(REUSE_OID)은 OID 가@0|0|0이라 쓸 수 없다. - OID 갱신은 자동 커밋되지 않는다.
cubrid_commit()을 하지 않으면 행 잠금이 남고, 연결을 끊을 때 롤백된다. - PHP 8.5 에서는
PDO::cubrid_schema()를 호출할 때 E_DEPRECATED 경고가 난다(드라이버 전용 메서드 호출 방식 폐지 예고). - Textcube 어댑터를
$database['driver'] = 'pdo'로 쓸 때: PDO SQL 파서가:이름을 자리표시자로 고쳐 쓸 문장(따옴표 해석이 서버와 다른 경우, 주로 PHP 8.4 미만)은 실행하지 않고 실패로 돌려준다. SSL 은 네이티브와 같이 암호화만(인증서 검증 없음,ssl_verify를 켜면 연결하지 않음).server·database에 DSN 구분자(;=등)가 있으면 연결하지 않는다. - 업스트림 네이티브 확장(패치 없음)은 위
cubrid_close_request결함이 그대로다. Textcube 네이티브 모드(free()·preparedclose())가 이 함수를 쓰므로 패치한 확장이나 PDO 모드를 쓸 것.cubrid_lob_get·cubrid_lob_close·cubrid_lob2_close와 LOB 를 가진 요청·연결 닫기도 해제된 메모리를 건드린다(Textcube 미사용, 패치 확장은 수정). 영구 연결을 닫은 뒤 남은 요청·연결이 닫힌 요청·열 수가 바뀌는 prepared 재실행도 같다(Textcube 어댑터는cubrid_connect·같은 열 수의 LOB 없는 재실행만 써서 해당 없음, 패치 확장은 수정). - 패치 확장 LOB:
cubrid_lob_close는 LOB 가 아닌 원소에서 멈춘다(앞 원소는 닫힘, PHP 5 원본과 같음). 결과를 끝까지 읽은 뒤·cubrid_next_result·prepared 재실행(이전 결과 마지막 행)·연결을 닫은 뒤의 LOB 는 읽기·쓰기·cubrid_lob2_close모두 'Invalid CUBRID-Lob2 resource' 경고와 false(업스트림 규칙, 변수를 놓을 때 해제). 요청만 닫으면 마지막 행 LOB 는 연결을 닫기 전까지 쓸 수 있다. - 패치 확장 연결 닫기: 닫은 연결(영구 포함)의 요청은 CCI 오류 경고와 false(
cubrid_affected_rows는 -1), 열 정보 함수는 열 0개로 답한다.cubrid_pconnect연결도 요청 목록에 등록되어 요청이 끝나면 닫힌다(업스트림 동작). 확장의php_cubrid_persistent_helper(영구 목록 정리)는 영구 목록에 등록된 연결이 없어 실행되지 않는 코드라 그대로 두었다. - PDO 는 BLOB·CLOB 열을 스트림으로 돌려준다(네이티브
cubrid_fetch는 LOB 위치 문자열file:…,CUBRID_LOB를 주면 LOB resource). Textcube Cubrid 스키마에는 LOB 열이 없다.
PARAM_LOB은 항상 BLOB. CLOB 은PARAM_STR문자열로 넣는다.- SET 열 바인딩은
PARAM_INT등으로(PARAM_STR 배열은 PDO 코어가 "Array" 로 바꿈). - PHP 8.4+: PDO SQL 파서가 백슬래시 escape 를 인식하지 않음(CUBRID 기본
no_backslash_escapes=yes와 일치). - CUBRID 11
getColumnMeta()['table']은 소유자 포함 이름(tc.smoke_p). - 네이티브
phpversion('cubrid')는 빈 값(업스트림 모듈 버전 미설정). - 빌드 로그의 libtool
*** Warning: Linking the shared library … against the static library는 CCI 정적 라이브러리 링크 안내(컴파일 경고 아님). - Textcube 어댑터(
framework/data/Cubrid/Adapter.php)는$database['driver']로 드라이버를 고른다:'native'(기본, cubrid_* 확장) ·'pdo'(PDO_CUBRID). prepare(php8.2 이상 단계)는 드라이버 바인딩 — nativecubrid_prepare/cubrid_bind, pdoPDO::prepare/bindValue(이전의?→ 리터럴 치환 방식은 폐기). php7.4 단계는 드라이버 선택만. 이 환경에서 패치한 확장으로 실서버 점검 통과. pdo 결과 행은 네이티브와 같은 키 순서(BOTH 는 열마다 번호 키 → 이름 키, 같은 이름·숫자 이름 열은 네이티브처럼 덮어씀)와 NUL 처리(첫 NUL 앞까지)로 다시 짠다(v3.119). PDO 의FETCH_BOTH는 이름 키를 먼저 넣고, 문자열 값을 NUL 뒤까지 돌려준다.
CUBRID 11.4.6 서버(cubrid/cubrid:11.4), CCI 11.2, sh cubrid_pdo/run_tests.sh (2026-10-11).
| 이미지 | 빌드 경고 | native (원본과 diff) | smoke | persist | close | lob | reuse | adapter native | adapter pdo |
|---|---|---|---|---|---|---|---|---|---|
| 7.4-orig (패치 없음) | 0 / 6 | 기준 | OK 57 / FAIL 16 후 세그폴트(139) | 2/4 | 해제된 메모리 접근으로 중단, 잘못된 인자도 true | 해제된 메모리 접근으로 중단 | 영구 연결을 닫은 뒤 해제된 연결 접근으로 중단 | 원본 트리: cubrid_stat() 없음 Fatal · php7.4 트리 67/67 |
php7.4 트리 72/72 |
| 7.4 | 0 / 0 | 생성자 예외의 클래스 이름 표시만 다름(수정) | 87/87 | 4/4 | done | done | done | 67/67 | 72/72 |
| 8.2 | 0 / 0 | 위 + PHP 코어 Fatal 문구 따옴표 | 87/87 | 4/4 | done | done | done | 81/81 | 86/86 |
| 8.4 | 0 / 0 | 8.2 와 같음 | 87/87 | 4/4 | done | done | done | 81/81 | 86/86 |
| 8.5 | 0 / 0 | 8.2 와 같음 + Fatal 에 스택 추적(8.5 코어) | 87/87 | 4/4 | done | done | done | STAGE4 81/81 · 릴리즈본 81/81 | STAGE4 86/86 · 릴리즈본 86/86 |
- 빌드 경고: cubrid / pdo_cubrid 컴파일러 경고 수(libtool 안내 제외,
build_ext.sh가 이미지 로그### … warnings줄로 집계).dev_compile.sh(-Wall)도 4 버전 모두 0. 7.4-orig pdo_cubrid 6건은 포인터 형식 불일치(스트림 read/write 핸들러의ssize_t, 드라이버 핸들러 시그니처,zend_hash_update인자) — 패치에서 수정. - adapter: 7.4 단계는 prepare API 가 없어 항목 수가 적다. pdo 는 무작위 SELECT 2000건을 네이티브 결과와 비교(다른 결과 0, 결과가 달라질 문장은 거부 —
PHP 8.4 미만 거부 114 중 그대로 실행했으면 실제로 달랐을 문장 6, 8.4 이상 거부 105 중 0).
결과 행 비교(숫자 별칭·같은 이름 열·NUL 값·표 행·
queryCell/queryColumn·fetchAllStmt): 수정 전 8.5 pdo 71/86(FAIL 15), 수정 후 전부 통과. - close:
USE_ZEND_ALLOC=0으로 실행. 7.4-orig 는 업스트림cubrid_close_request()가 변수의 참조까지 놓아free(): invalid pointer로 중단, 닫힌 요청·연결·문자열 인자에도 true. 8.5 패치본은 valgrind(-q, adapter 는-d pcre.jit=0)로 close · lob · reuse · adapter native 모두 오류 0. - lob:
USE_ZEND_ALLOC=0으로 실행. 7.4-orig 는 업스트림 LOB 결함(cubrid_lob_get반환 배열 손상 등)으로 중단 — v3.119 패치본까지 같은 결함, 수정 전후 구간별 결과는_sectest/_work/cubrid_v3121/results/. - reuse:
USE_ZEND_ALLOC=0으로 실행. 7.4-orig 는 영구 연결을 닫은 뒤 요청의 해제된 연결 접근으로 중단 — v3.121 패치본까지 같은 결함 (연결이 닫힌 요청의 NULL 역참조·열 수가 늘어나는 재실행도 중단), 수정 전후 구간별 결과는_sectest/_work/cubrid_v3122/results/. - 7.4-orig smoke FAIL: PDO 생성자 옵션·
:?든 암호·AUTOCOMMIT 2건·SET 호출자 배열, 네이티브 11건(cubrid_put호출자 배열 변경·bool→NULL·빈 배열/정수 키 세그폴트·빈 SET 에 앞 속성 값 저장,cubrid_get·cubrid_db_name호출자 배열 변경,cubrid_fetch_object클래스 이름이 문자열이 아닐 때 — 정수는 세그폴트(139), 배열은 엉뚱한 크기 할당으로 메모리 한도 Fatal). 끝의 세그폴트는 PDO SET 빈 배열 바인딩(업스트림 결함, 패치본은 정상 실행). - 7.4-orig persist: 생성자 옵션 거부로 연결 실패.