From fd303da77a5e8797271516f9fa4b49f4014ab90d Mon Sep 17 00:00:00 2001 From: whitedot Date: Mon, 7 Sep 2026 05:42:46 +0000 Subject: [PATCH] =?UTF-8?q?chore:=20=EB=A1=9C=EC=BB=AC=20=EC=9A=B4?= =?UTF-8?q?=EC=98=81=20=EB=B0=8F=20=EA=B2=80=EC=A6=9D=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A6=BD=ED=8A=B8=20bin=20=EB=94=94=EB=A0=89=ED=84=B0=EB=A6=AC?= =?UTF-8?q?=EB=A5=BC=20Git=20=EC=B6=94=EC=A0=81=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bin 디렉터리를 무시 목록에 추가하고 기존 추적 파일을 인덱스에서 제거한다. 로컬 스크립트 파일은 유지한다. --- .gitignore | 2 + bin/check-migration-existing.php | 95 ------------ bin/check-migration-runner.php | 249 ------------------------------- bin/check-migration-schema.php | 123 --------------- bin/check-runtime-ddl.php | 71 --------- bin/check-shop-install.php | 210 -------------------------- bin/db-migrate.php | 51 ------- 7 files changed, 2 insertions(+), 799 deletions(-) delete mode 100644 bin/check-migration-existing.php delete mode 100755 bin/check-migration-runner.php delete mode 100755 bin/check-migration-schema.php delete mode 100755 bin/check-runtime-ddl.php delete mode 100755 bin/check-shop-install.php delete mode 100755 bin/db-migrate.php diff --git a/.gitignore b/.gitignore index e5ef42a54..2a5e5886e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ /.htaccess data/ test/ +/tests/ +/bin/ sirgle/ test.php *.key diff --git a/bin/check-migration-existing.php b/bin/check-migration-existing.php deleted file mode 100644 index 63ab57db1..000000000 --- a/bin/check-migration-existing.php +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env php - 'inspection_test_config'); -$records = array(); -$queries = array(); -$columns = array('present'); -$lock_available = true; -$history_readable = true; - -class MigrationInspectionRows -{ - public $rows; - public function __construct($rows) { $this->rows = $rows; } -} -function sql_real_escape_string($value) { return addslashes($value); } -function sql_error_info() { return '검사용 오류'; } -function get_microtime() { return microtime(true); } -function sql_fetch_array($result) { return array_shift($result->rows); } -function sql_num_rows($result) { return count($result->rows); } -function sql_fetch($sql, $error = true) { - $result = sql_query($sql, $error); - return $result ? sql_fetch_array($result) : false; -} -function sql_query($sql, $error = true) { - global $queries, $records, $columns, $lock_available, $history_readable; - $queries[] = $sql; - if (strpos($sql, 'SHOW TABLES') === 0) return new MigrationInspectionRows(array(array('table' => 'inspection_test_migrations'))); - if (strpos($sql, 'SHOW COLUMNS') === 0) { - $rows = array(); - foreach ($columns as $column) $rows[] = array('Field' => $column); - return new MigrationInspectionRows($rows); - } - if (strpos($sql, 'SELECT migration_id') === 0) return $history_readable ? new MigrationInspectionRows(array_values($records)) : false; - if (strpos($sql, 'SELECT COUNT(*)') === 0) return new MigrationInspectionRows(array(array('total' => count($records)))); - if (strpos($sql, 'SELECT GET_LOCK') === 0) return new MigrationInspectionRows(array(array('acquired' => $lock_available ? 1 : 0))); - if (strpos($sql, 'SELECT RELEASE_LOCK') === 0 || strpos($sql, 'CREATE TABLE IF NOT EXISTS `inspection_test_migrations`') === 0) return true; - if (strpos($sql, 'INSERT INTO `inspection_test_migrations`') === 0) { - preg_match("/migration_id = '([^']+)'/", $sql, $id); - preg_match("/checksum = '([^']+)'/", $sql, $checksum); - $records[$id[1]] = array('migration_id' => $id[1], 'checksum' => $checksum[1], 'status' => 'success', 'error_message' => '', 'applied_at' => G5_TIME_YMDHIS); - return true; - } - fwrite(STDERR, '[실패] 허용하지 않은 SQL: ' . $sql . "\n"); - exit(1); -} -function inspection_check($condition, $message) { - if (!$condition) { fwrite(STDERR, '[실패] ' . $message . "\n"); exit(1); } -} -function inspection_cleanup() { - foreach (glob(G5_PATH . '/migrations/*') as $file) unlink($file); - rmdir(G5_PATH . '/migrations'); - rmdir(G5_PATH); -} -register_shutdown_function('inspection_cleanup'); -require dirname(dirname(__FILE__)) . '/lib/migration.lib.php'; -$ids = array('20200101_001_existing', '20200102_001_missing', '20200103_001_later'); -foreach ($ids as $index => $id) { - $column = $index === 1 ? 'missing' : 'present'; - file_put_contents(G5_PATH . '/migrations/' . $id . '.sql', "-- @if-column-missing {{config_table}} $column\nALTER TABLE `{{config_table}}` ADD `$column` int;\n"); -} -$statuses = g5_migration_status(); -inspection_check($statuses[0]['status'] === 'unrecorded' && $statuses[1]['status'] === 'pending' && $statuses[2]['status'] === 'unrecorded', '기존·미적용 상태 구분'); -foreach ($queries as $query) inspection_check(preg_match('/^(SHOW|SELECT) /', $query), '상태 조회에서 DB 쓰기 발생'); -$result = g5_migration_run('', true); -inspection_check($result['success'] && $result['skipped'] === 1 && count($records) === 1 && isset($records[$ids[0]]), '미적용 선행 항목을 넘어 등록함'); -$result = g5_migration_run('', true); -inspection_check($result['skipped'] === 0 && count($records) === 1, '반복 등록'); -$statuses = g5_migration_status(); -inspection_check($statuses[0]['status'] === 'success' && $statuses[2]['status'] === 'unrecorded', '부분 이력 이후 검사 누락'); -$records[$ids[0]]['checksum'] = 'changed'; -$result = g5_migration_run('', true); -inspection_check(!$result['success'] && count($records) === 1, '체크섬 변경 무시'); -$records[$ids[0]]['status'] = 'failed'; -$columns[] = 'missing'; -$result = g5_migration_run('', true); -inspection_check($result['skipped'] === 0 && $records[$ids[0]]['status'] === 'failed', '실패 이력을 덮어씀'); -$records = array(); -$lock_available = false; -$result = g5_migration_run('', true); -inspection_check(!$result['success'] && !$records, '잠금 실패 후 등록'); -$lock_available = true; -$result = g5_migration_run('', true); -inspection_check($result['success'] && $result['skipped'] === 3 && count($records) === 3, '재검사 후 기존 항목 등록 실패'); -$history_readable = false; -$result = g5_migration_run('', true); -inspection_check(!$result['success'] && count($records) === 3, '이력 조회 실패 후 덮어씀'); -echo "기존 상태 조회·순차 이력 등록·재검사·실패/체크섬/잠금 보호 검사가 통과했습니다.\n"; diff --git a/bin/check-migration-runner.php b/bin/check-migration-runner.php deleted file mode 100755 index 03bfe8b34..000000000 --- a/bin/check-migration-runner.php +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/env php - $key) { - $g5[$key] = $test_absent_prefix . $index; -} - -// 복합 ALTER의 첫 컬럼만 없는 역방향 부분 적용 상태에서도 과거 마이그레이션이 중단되지 않아야 한다. -$inspection_cache = array(); -$social_migration = g5_migration_parse_file(G5_PATH . '/migrations/20180330_001_social_login.sql'); -if (g5_migration_needs_no_execution($social_migration, $inspection_cache)) { - g5_migration_test_fail('부분 적용된 소셜 스키마를 실행 불필요로 판단했습니다.'); -} -g5_migration_test_execute(g5_migration_parse_file(G5_PATH . '/migrations/20140331_001_shop_cart_shipping.sql')); -g5_migration_test_execute(g5_migration_parse_file(G5_PATH . '/migrations/20180330_001_social_login.sql')); -g5_migration_test_execute(g5_migration_parse_file(G5_PATH . '/migrations/20260723_001_inicis_pro.sql')); -$inspection_cache = array(); -if (!g5_migration_needs_no_execution($social_migration, $inspection_cache)) { - g5_migration_test_fail('이미 적용된 소셜 스키마를 실행 불필요로 판단하지 못했습니다.'); -} -$write_migration = g5_migration_parse_file(G5_PATH . '/migrations/20191202_002_board_write_seo.sql'); -if (!g5_migration_needs_no_execution($write_migration, $inspection_cache)) { - g5_migration_test_fail('기존 게시판 SEO 컬럼을 확인하지 못했습니다.'); -} -sql_query("ALTER TABLE `{$test_write_table}` DROP COLUMN wr_seo_title", false); -$inspection_cache = array(); -if (g5_migration_needs_no_execution($write_migration, $inspection_cache)) { - g5_migration_test_fail('게시판 SEO 컬럼 누락을 실행 불필요로 판단했습니다.'); -} -g5_migration_test_execute($write_migration); -$inspection_cache = array(); -$unconditional_migration = g5_migration_parse_file(G5_PATH . '/migrations/20260904_002_reconcile_partial_schema.sql'); -$backfill_migration = g5_migration_parse_file(G5_PATH . '/migrations/20260904_003_backfill_legacy_data.sql'); -if (g5_migration_needs_no_execution($unconditional_migration, $inspection_cache) || g5_migration_needs_no_execution($backfill_migration, $inspection_cache)) { - g5_migration_test_fail('조건 없는 스키마 변경 또는 데이터 보정을 완료로 추정했습니다.'); -} -$absent_table = $test_absent_prefix . 'inspection'; -if (g5_migration_inspect_object('column', $absent_table, 'missing', $inspection_cache) !== null) { - g5_migration_test_fail('조회 오류를 객체 없음과 구분하지 못했습니다.'); -} -if (!g5_migration_column_exists($test_cart_table, 'it_sc_type') || - !g5_migration_column_exists($test_config_table, 'cf_social_login_use') || - !g5_migration_column_exists($test_config_table, 'cf_member_img_size') || - !g5_migration_column_exists($test_shop_default_table, 'de_inicis_pro_alert_use')) { - g5_migration_test_fail('역방향 부분 적용 스키마에서 과거 복합 변경의 기준 컬럼을 복구하지 못했습니다.'); -} - -$schema_migration = g5_migration_parse_file(G5_PATH . '/migrations/20260904_002_reconcile_partial_schema.sql'); -g5_migration_test_execute($schema_migration); -if (!g5_migration_column_exists($test_config_table, 'cf_google_secret') || - !g5_migration_column_exists($test_config_table, 'cf_member_img_width') || - !g5_migration_column_exists($test_config_table, 'cf_member_img_height')) { - g5_migration_test_fail('부분 적용 설정 테이블의 누락 컬럼을 복구하지 못했습니다.'); -} -if (!g5_migration_column_exists($test_cart_table, 'it_sc_method') || - !g5_migration_column_exists($test_shop_default_table, 'de_inicis_pro_monitor_message') || - !g5_migration_column_exists($test_inicis_pay_table, 'ip_pg_status') || - !g5_migration_index_exists($test_inicis_pay_table, 'ip_refund_required')) { - g5_migration_test_fail('부분 적용 쇼핑몰 테이블의 누락 컬럼 또는 인덱스를 복구하지 못했습니다.'); -} -$write_column = sql_fetch("SHOW COLUMNS FROM `{$test_write_table}` LIKE 'wr_seo_title'", false); -if (!isset($write_column['Type']) || strtolower($write_column['Type']) !== 'varchar(255)') { - g5_migration_test_fail('게시판 글 SEO 컬럼의 최종 자료형을 보정하지 못했습니다.'); -} - -sql_query("INSERT INTO `{$test_config_table}` SET cf_member_img_size = 0, cf_member_img_width = 0, cf_member_img_height = 0", false); -sql_query("INSERT INTO `{$test_content_table}` SET co_id = 'company', co_subject = '회사 소개', co_seo_title = ''", false); -sql_query("INSERT INTO `{$test_item_table}` SET it_id = 'item-1', it_sc_type = 2, it_sc_method = 1, it_sc_price = 3000, it_sc_minimum = 50000, it_sc_qty = 2", false); -sql_query("INSERT INTO `{$test_cart_table}` SET it_id = 'item-1'", false); - -$data_migration = g5_migration_parse_file(G5_PATH . '/migrations/20260904_003_backfill_legacy_data.sql'); -g5_migration_test_execute($data_migration); - -$config_row = sql_fetch("SELECT cf_member_img_size, cf_member_img_width, cf_member_img_height FROM `{$test_config_table}` LIMIT 1", false); -if ((int) $config_row['cf_member_img_size'] !== 50000 || (int) $config_row['cf_member_img_width'] !== 60 || (int) $config_row['cf_member_img_height'] !== 60) { - g5_migration_test_fail('회원 이미지 설정 기본값을 보정하지 못했습니다.'); -} -$content_row = sql_fetch("SELECT co_seo_title FROM `{$test_content_table}` WHERE co_id = 'company'", false); -if (!isset($content_row['co_seo_title']) || $content_row['co_seo_title'] === '') { - g5_migration_test_fail('콘텐츠 SEO 제목을 보정하지 못했습니다.'); -} -$cart_row = sql_fetch("SELECT it_sc_type, it_sc_method, it_sc_price, it_sc_minimum, it_sc_qty FROM `{$test_cart_table}` WHERE it_id = 'item-1'", false); -if ((int) $cart_row['it_sc_type'] !== 2 || (int) $cart_row['it_sc_method'] !== 1 || (int) $cart_row['it_sc_price'] !== 3000 || (int) $cart_row['it_sc_minimum'] !== 50000 || (int) $cart_row['it_sc_qty'] !== 2) { - g5_migration_test_fail('장바구니 배송비 데이터를 보정하지 못했습니다.'); -} - -$migrations = g5_migration_discover(); -$migration_ids = array(); -$dependency_records = array(); -foreach ($migrations as $migration) { - if (isset($migration['error'])) { - g5_migration_test_fail($migration['error']); - } - $migration_ids[] = $migration['id']; - $dependency_records[$migration['id']] = array('status' => 'success', 'checksum' => $migration['checksum']); -} -$sorted_ids = $migration_ids; -sort($sorted_ids, SORT_STRING); -if ($migration_ids !== $sorted_ids) { - g5_migration_test_fail('전체 마이그레이션이 오래된 순서로 정렬되지 않았습니다.'); -} -$last_migration = $migrations[count($migrations) - 1]; -unset($dependency_records[$migrations[0]['id']]); -if (g5_migration_validate_target_dependencies($migrations, $dependency_records, $last_migration['id']) === '') { - g5_migration_test_fail('누락된 선행 마이그레이션을 차단하지 못했습니다.'); -} - -echo "기존 상태 읽기 전용 검사·실행 순서·선행 조건·부분 적용 스키마·기존 데이터 보정 검사가 통과했습니다.\n"; diff --git a/bin/check-migration-schema.php b/bin/check-migration-schema.php deleted file mode 100755 index dd028aa37..000000000 --- a/bin/check-migration-schema.php +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env php -'g5_auth', '{{board_file_table}}'=>'g5_board_file', '{{config_table}}'=>'g5_config', - '{{content_table}}'=>'g5_content', '{{login_table}}'=>'g5_login', '{{member_table}}'=>'g5_member', - '{{member_auto_login_table}}'=>'g5_member_auto_login', '{{memo_table}}'=>'g5_memo', - '{{qa_config_table}}'=>'g5_qa_config', '{{social_profile_table}}'=>'g5_member_social_profiles', - '{{visit_table}}'=>'g5_visit', '{{write_table}}'=>'__TABLE_NAME__', - '{{g5_shop_cart_table}}'=>'g5_shop_cart', '{{g5_shop_coupon_log_table}}'=>'g5_shop_coupon_log', - '{{g5_shop_coupon_table}}'=>'g5_shop_coupon', '{{g5_shop_coupon_zone_table}}'=>'g5_shop_coupon_zone', - '{{g5_shop_default_table}}'=>'g5_shop_default', '{{g5_shop_inicis_log_table}}'=>'g5_shop_inicis_log', - '{{g5_shop_inicis_pay_event_table}}'=>'g5_shop_inicis_pay_event', - '{{g5_shop_inicis_pay_table}}'=>'g5_shop_inicis_pay', - '{{g5_shop_order_cancel_log_table}}'=>'g5_shop_order_cancel_log', - '{{g5_shop_order_data_table}}'=>'g5_shop_order_data', '{{g5_shop_order_table}}'=>'g5_shop_order', - '{{g5_shop_post_log_table}}'=>'g5_shop_order_post_log' -); - -$install_tables = array(); -if (preg_match_all('/CREATE\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?\s+`([^`]+)`\s*\((.*?)\)\s*ENGINE/is', $install_sql, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) $install_tables[$match[1]] = $match[2]; -} - -function g5_schema_normalize_definition($definition) -{ - $definition = preg_replace('/\s+AFTER\s+`?[a-zA-Z0-9_]+`?\s*$/i', '', trim($definition)); - $definition = preg_replace('/\s+FIRST\s*$/i', '', $definition); - $definition = str_replace('`', '', strtolower($definition)); - return preg_replace('/\s+/', ' ', $definition); -} - -function g5_schema_check_objects($file, $table, $sql, $install_tables, &$errors, &$latest_definitions) -{ - if (!isset($install_tables[$table])) { - $errors[] = $file.': 신규 설치 SQL에 `'.$table.'` 테이블이 없습니다.'; - return; - } - $body = $install_tables[$table]; - if (preg_match_all('/\bADD(?:\s+COLUMN)?\s+`([a-zA-Z0-9_]+)`/i', $sql, $columns)) { - foreach ($columns[1] as $column) { - if (!preg_match('/`'.preg_quote($column, '/').'`\s+[a-z]/i', $body)) - $errors[] = $file.': `'.$table.'`.`'.$column.'` 컬럼이 신규 설치 SQL에 없습니다.'; - } - } - if (preg_match_all('/(?:ADD(?:\s+COLUMN)?|MODIFY(?:\s+COLUMN)?|CHANGE(?:\s+COLUMN)?\s+`[a-zA-Z0-9_]+`)\s+`([a-zA-Z0-9_]+)`\s+(.*?)(?=,\s*(?:ADD|MODIFY|CHANGE|DROP|PRIMARY|UNIQUE|KEY|INDEX)\b|$)/is', $sql, $definitions, PREG_SET_ORDER)) { - foreach ($definitions as $definition) { - $latest_definitions[$table][$definition[1]] = g5_schema_normalize_definition($definition[2]); - } - } - if (preg_match_all('/\bADD\s+(?:UNIQUE\s+)?(?:KEY|INDEX)\s+`([^`]+)`/i', $sql, $indexes)) { - foreach ($indexes[1] as $index) { - if (!preg_match('/(?:UNIQUE\s+)?KEY\s+`'.preg_quote($index, '/').'`/i', $body)) - $errors[] = $file.': `'.$table.'`.`'.$index.'` 인덱스가 신규 설치 SQL에 없습니다.'; - } - } -} - -$errors = array(); -$latest_definitions = array(); -foreach ((array) $migration_files as $migration_file) { - $sql = @file_get_contents($migration_file); - if ($sql === false) { - $errors[] = basename($migration_file).': 파일을 읽을 수 없습니다.'; - continue; - } - if (preg_match_all('/ALTER\s+TABLE\s+`(\{\{[a-zA-Z0-9_]+\}\})`\s+(.*?);/is', $sql, $alters, PREG_SET_ORDER)) { - foreach ($alters as $alter) { - if (!isset($table_map[$alter[1]])) { - $errors[] = basename($migration_file).': 검사할 수 없는 자리표시자입니다: '.$alter[1]; - continue; - } - g5_schema_check_objects(basename($migration_file), $table_map[$alter[1]], $alter[2], $install_tables, $errors, $latest_definitions); - } - } - if (preg_match_all('/CREATE\s+TABLE\s+`(\{\{[a-zA-Z0-9_]+\}\})`\s*\((.*?)\)\s*ENGINE/is', $sql, $creates, PREG_SET_ORDER)) { - foreach ($creates as $create) { - if (!isset($table_map[$create[1]]) || !isset($install_tables[$table_map[$create[1]]])) { - $errors[] = basename($migration_file).': 신규 설치 SQL에서 생성 대상 테이블을 찾을 수 없습니다: '.$create[1]; - continue; - } - $table = $table_map[$create[1]]; - if (preg_match_all('/`([a-zA-Z0-9_]+)`\s+[a-z]/i', $create[2], $columns)) { - foreach ($columns[1] as $column) { - if (!preg_match('/`'.preg_quote($column, '/').'`\s+[a-z]/i', $install_tables[$table])) - $errors[] = basename($migration_file).': `'.$table.'`.`'.$column.'` 컬럼이 신규 설치 SQL에 없습니다.'; - } - } - } - } -} - -foreach ($latest_definitions as $table => $columns) { - foreach ($columns as $column => $definition) { - if (!preg_match('/^\s*`'.preg_quote($column, '/').'`\s+(.+?),?\s*$/mi', $install_tables[$table], $match)) { - continue; - } - $install_definition = g5_schema_normalize_definition($match[1]); - if ($definition !== $install_definition) { - $errors[] = '`'.$table.'`.`'.$column.'` 최종 정의가 신규 설치 SQL과 다릅니다: '.$definition.' != '.$install_definition; - } - } -} - -if ($errors) { - foreach ($errors as $error) fwrite(STDERR, '[실패] '.$error."\n"); - exit(1); -} -echo "마이그레이션의 테이블·컬럼·인덱스와 최종 컬럼 정의가 신규 설치 SQL과 일치합니다.\n"; diff --git a/bin/check-runtime-ddl.php b/bin/check-runtime-ddl.php deleted file mode 100755 index dcd54cd6d..000000000 --- a/bin/check-runtime-ddl.php +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env php -getPathname(); - if (!$file->isFile() || strtolower(pathinfo($pathname, PATHINFO_EXTENSION)) !== 'php') { - continue; - } - - $relative = str_replace($root . '/', '', str_replace('\\', '/', $pathname)); - $skip = false; - foreach ($excluded as $exclude) { - if ($relative === $exclude || strpos($relative, $exclude) === 0) { - $skip = true; - break; - } - } - if ($skip) { - continue; - } - - if (in_array($relative, $lifecycle_ddl_files)) { - continue; - } - - $contents = file_get_contents($pathname); - $contents = preg_replace('/^.*\$schema_create.*$/m', '', $contents); - if (preg_match_all('/\b(?:ALTER\s+TABLE|CREATE\s+TABLE|DROP\s+TABLE|RENAME\s+(?:TABLE|TO)|TRUNCATE\s+TABLE)\b/i', $contents, $matches, PREG_OFFSET_CAPTURE)) { - foreach ($matches[0] as $match) { - $line = substr_count(substr($contents, 0, $match[1]), "\n") + 1; - $errors[] = $relative . ':' . $line . ': ' . preg_replace('/\s+/', ' ', trim($match[0])); - } - } - } -} - -if ($errors) { - foreach ($errors as $error) { - fwrite(STDERR, '[실패] ' . $error . "\n"); - } - exit(1); -} - -echo "일반 실행 경로에 DDL 문이 없습니다.\n"; diff --git a/bin/check-shop-install.php b/bin/check-shop-install.php deleted file mode 100755 index 453ff332e..000000000 --- a/bin/check-shop-install.php +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env php - '160', '$ssimg_height' => '160', '$simg_width' => '215', '$simg_height' => '215', - '$mimg_width' => '230', '$mimg_height' => '230', '$mmimg_width' => '300', '$mmimg_height' => '300', - '$msimg_width' => '80', '$msimg_height' => '80', '$list_img_width' => '225', '$list_img_height' => '225' -); -foreach ($install_values as $field => $value) { - $value = strtr($value, $install_variables); - if (!isset($shop_values[$field]) || $shop_values[$field] !== $value) { - fwrite(STDERR, '[실패] 기본 설치와 후설치의 쇼핑몰 기본 설정값이 다릅니다: ' . $field . "\n"); - exit(1); - } -} - -$suffix = substr(md5(getmypid() . microtime()), 0, 10); -$prefix = G5_TABLE_PREFIX . 'shop_install_test_' . $suffix . '_'; -$data_path = sys_get_temp_dir() . '/g5-shop-install-' . $suffix; -$config_file = $data_path . '/dbconfig.php'; -$tables = g5_shop_install_table_names($prefix); - -function g5_shop_install_test_cleanup() -{ - global $tables, $prefix, $data_path; - foreach ($tables as $name) { - sql_query("DROP TABLE IF EXISTS `" . str_replace('`', '``', $prefix . $name) . "`", false); - } - if (is_dir($data_path)) { - $dirs = array('banner', 'common', 'event', 'item'); - foreach ($dirs as $dir) { - $path = $data_path . '/' . $dir; - if (is_dir($path)) { - $files = scandir($path); - foreach ($files as $file) { - if ($file !== '.' && $file !== '..') { - @unlink($path . '/' . $file); - } - } - @rmdir($path); - } - } - @unlink($data_path . '/dbconfig.php'); - @rmdir($data_path); - } -} - -function g5_shop_install_test_fail($message) -{ - fwrite(STDERR, '[실패] ' . $message . "\n"); - exit(1); -} - -// 모든 마이그레이션의 쇼핑몰 SQL만 격리 접두어로 실행한다. -// 공통 스키마가 섞인 파일에서도 쇼핑몰 문장의 조건을 빠짐없이 검사한다. -function g5_shop_install_test_migrations($prefix, $installed) -{ - $checked = 0; - foreach (g5_migration_discover() as $migration) { - if (isset($migration['error'])) { - g5_shop_install_test_fail($migration['error']); - } - foreach ($migration['statements'] as $statement) { - if (strpos($statement['sql'], '`' . $prefix) === false) { - continue; - } - $checked++; - if (!preg_match('/^(?:CREATE TABLE(?: IF NOT EXISTS)?|ALTER TABLE|UPDATE)\s+`' . preg_quote($prefix, '/') . '/i', $statement['sql'])) { - g5_shop_install_test_fail('격리된 쇼핑몰 테이블을 대상으로 하지 않는 SQL입니다: ' . $migration['id']); - } - $should_run = g5_migration_should_run_statement($statement['conditions']); - if (!$installed && $should_run) { - g5_shop_install_test_fail('쇼핑몰 미설치 상태에서 실행되는 SQL이 있습니다: ' . $migration['id']); - } - if ($should_run) { - $result = g5_migration_execute_statement($statement); - if ($result['error'] !== '') { - g5_shop_install_test_fail($migration['id'] . ': ' . $result['error']); - } - } - } - } - if (!$checked) { - g5_shop_install_test_fail('쇼핑몰 마이그레이션 검사 대상이 없습니다.'); - } - return $checked; -} - -register_shutdown_function('g5_shop_install_test_cleanup'); -g5_shop_install_test_cleanup(); - -// SQL과 조건에서 사용되는 쇼핑몰 자리표시자를 모두 임시 테이블에 연결한다. -$shop_keys = array(); -foreach (glob(G5_PATH . '/migrations/*.sql') as $migration_file) { - preg_match_all('/\{\{(g5_shop_[a-z0-9_]+_table)\}\}/', file_get_contents($migration_file), $matches); - foreach ($matches[1] as $key) { - $shop_keys[$key] = true; - } -} -$source_shop_prefix = defined('G5_SHOP_TABLE_PREFIX') ? G5_SHOP_TABLE_PREFIX : G5_TABLE_PREFIX . 'shop_'; -foreach ($shop_keys as $key => $unused) { - $original = g5_migration_replace_placeholders('{{' . $key . '}}'); - if (strpos($original, $source_shop_prefix) !== 0) { - g5_shop_install_test_fail('쇼핑몰 테이블 접두어를 격리하지 못했습니다: ' . $key); - } - $g5[$key] = $prefix . substr($original, strlen($source_shop_prefix)); -} -$checked_statements = g5_shop_install_test_migrations($prefix, false); -if (g5_shop_install_existing_tables($prefix) !== array()) { - g5_shop_install_test_fail('쇼핑몰 미설치 검사 후 테이블이 생성되었습니다.'); -} -if (!@mkdir($data_path, G5_DIR_PERMISSION) || @file_put_contents($config_file, "") === false) { - g5_shop_install_test_fail('격리 테스트 설정 파일을 준비하지 못했습니다.'); -} - -$result = g5_shop_install_run(array( - 'allow_enabled' => true, - 'prefix' => $prefix, - 'data_path' => $data_path, - 'config_file' => $config_file -)); -if (!$result['success']) { - g5_shop_install_test_fail($result['error']); -} - -foreach ($tables as $name) { - if (!g5_migration_table_exists($prefix . $name)) { - g5_shop_install_test_fail('쇼핑몰 테이블이 누락되었습니다: ' . $prefix . $name); - } -} -// 미설치 업그레이드 뒤 후설치는 과거 이력과 관계없이 최신 설치 SQL을 사용한다. -// 쇼핑몰을 먼저 설치한 경우의 업그레이드 SQL도 최신 스키마에서 정상 처리돼야 한다. -g5_shop_install_test_migrations($prefix, true); -$default = sql_fetch("SELECT de_shop_skin, de_shop_mobile_skin, de_sms_cont1, de_sms_cont2, de_sms_cont3, de_sms_cont4, de_sms_cont5 FROM `{$prefix}default` LIMIT 1", false); -if (!isset($default['de_shop_skin']) || $default['de_shop_skin'] !== 'basic' || $default['de_shop_mobile_skin'] !== 'basic') { - g5_shop_install_test_fail('쇼핑몰 기본 설정이 올바르게 생성되지 않았습니다.'); -} -for ($index = 1; $index <= 5; $index++) { - preg_match_all('/./us', $default['de_sms_cont' . $index], $characters); - $bytes = 0; - foreach ($characters[0] as $character) { - $bytes += strlen($character) > 1 ? 2 : 1; - } - if ($bytes > 80) { - g5_shop_install_test_fail('기본 SMS 문구가 80바이트를 초과합니다: de_sms_cont' . $index); - } -} -$config = @file_get_contents($config_file); -if ($config === false || strpos($config, '기존 설정 보존 표식') === false || strpos($config, "define('G5_USE_SHOP', true)") === false || strpos($config, $prefix) === false) { - g5_shop_install_test_fail('기존 DB 설정을 보존하면서 쇼핑몰 설정을 추가하지 못했습니다.'); -} -$retry = g5_shop_install_run(array( - 'allow_enabled' => true, - 'prefix' => $prefix, - 'data_path' => $data_path, - 'config_file' => $config_file -)); -if ($retry['success']) { - g5_shop_install_test_fail('이미 설치된 쇼핑몰의 중복 설치를 차단하지 못했습니다.'); -} - -echo '쇼핑몰 SQL ' . $checked_statements . "개 미설치 건너뛰기·후설치 후 업그레이드·설정 보존·중복 실행 차단 검사가 통과했습니다.\n"; diff --git a/bin/db-migrate.php b/bin/db-migrate.php deleted file mode 100755 index ad775f121..000000000 --- a/bin/db-migrate.php +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env php -