public function start($args) { Postcodify_Utility::print_message('Postcodify Indexer ' . POSTCODIFY_VERSION); Postcodify_Utility::print_newline(); if (in_array('--no-old-postcodes', $args->options)) { $this->_no_old_postcodes = true; } if (!($db = Postcodify_Utility::get_db())) { echo '[ERROR] MySQL DB에 접속할 수 없습니다.' . PHP_EOL; exit(1); } $this->_schema = (include POSTCODIFY_LIB_DIR . '/resources/schema.php'); $pass = true; echo '테이블 확인 중...' . PHP_EOL; $pass = $this->check_tables($db) && $pass; echo '인덱스 확인 중...' . PHP_EOL; $pass = $this->check_indexes($db) && $pass; if ($pass) { echo '데이터 확인 중...' . PHP_EOL; $pass = $pass && $this->check_data_count($db); $pass = $pass && $this->check_missing_postcodes($db); } else { echo 'DB 스키마에 문제가 있으므로 데이터 확인은 시도하지 않습니다.' . PHP_EOL; } if ($pass) { echo 'DB에 문제가 없습니다.' . PHP_EOL; } else { echo 'DB에 문제가 있습니다.' . PHP_EOL; exit(1); } }
public function start($args) { // 주어진 옵션을 확인한다. $valid_options = false; if (count($args->args) === 2 && ctype_digit($args->args[0]) && ctype_digit($args->args[1]) && (strlen($args->args[1]) === 5 || strlen($args->args[1]) === 6)) { $address_id = $args->args[0]; $postcode = $args->args[1]; } else { echo 'Usage: php indexer.php set-postcode <address-id> <postcode>' . PHP_EOL; exit(1); } // DB 연결을 확인한다. Postcodify_Utility::print_message('Postcodify Indexer ' . POSTCODIFY_VERSION); Postcodify_Utility::print_newline(); if (!($db = Postcodify_Utility::get_db())) { echo '[ERROR] MySQL DB에 접속할 수 없습니다.' . PHP_EOL; exit(1); } // 주어진 주소가 존재하는지 확인한다. $ps_select = $db->prepare('SELECT pa.*, pr.* FROM postcodify_addresses pa JOIN postcodify_roads pr ON pa.road_id = pr.road_id WHERE pa.id = ? ORDER BY id LIMIT 1'); $ps_select->execute(array($address_id)); $entry = $ps_select->fetchObject(); if (!$entry) { echo '[ERROR] "' . $address_id . '" 레코드를 찾을 수 없습니다.' . PHP_EOL; exit(1); } // 우편번호를 업데이트한다. $column_name = strlen($postcode) === 6 ? 'postcode6' : 'postcode5'; $ps_update = $db->prepare('UPDATE postcodify_addresses SET ' . $column_name . ' = ? WHERE id = ?'); $ps_update->execute(array($postcode, $entry->id)); $entry->{$column_name} = $postcode; // 변경 내역을 표시한다. echo ' #' . $entry->id . ' ' . str_pad($entry->postcode6, 6, ' ') . ' ' . str_pad($entry->postcode5, 5, ' ') . ' ' . $this->format_address($entry) . PHP_EOL; }
Postcodify_Utility::print_usage_instructions(); } $args = Postcodify_Utility::get_terminal_args(); if ($args->command === null) { Postcodify_Utility::print_usage_instructions(); } if (in_array('--dry-run', $args->options)) { Postcodify_Utility::print_usage_instructions(); } // 필요한 클래스를 호출한다. $start_time = time(); $class_name = 'Postcodify_Indexer_' . ucfirst(str_replace('-', '_', $argv[1])); $obj = new $class_name(); $obj->start($args); // 소요된 시간을 출력한다. echo str_repeat('-', Postcodify_Utility::get_terminal_width()) . PHP_EOL; $elapsed = time() - $start_time; $elapsed_hours = floor($elapsed / 3600); $elapsed = $elapsed - $elapsed_hours * 3600; $elapsed_minutes = floor($elapsed / 60); $elapsed_seconds = $elapsed % 60; echo '작업을 모두 마쳤습니다. 경과 시간 : '; if ($elapsed_hours) { echo $elapsed_hours . '시간 '; } if ($elapsed_hours || $elapsed_minutes) { echo $elapsed_minutes . '분 '; } echo $elapsed_seconds . '초'; echo PHP_EOL; exit(0);
public static function progress($ch, $fd = null, $size = null) { if ($size <= 0) { return; } Postcodify_Utility::print_progress($size); }
public function save_english_keywords() { Postcodify_Utility::print_message('영문 검색 키워드를 저장하는 중...'); // DB를 준비한다. $db = Postcodify_Utility::get_db(); $db->beginTransaction(); $ps = $db->prepare('INSERT INTO postcodify_english (ko, ko_crc32, en, en_crc32) VALUES (?, ?, ?, ?)'); // 카운터를 초기화한다. $count = 0; // 각 영문 키워드와 거기에 해당하는 한글 키워드를 DB에 입력한다. foreach (Postcodify_Utility::$english_cache as $ko => $en) { // 영문 키워드에서 불필요한 문자를 제거한다. $en_canonical = preg_replace('/[^a-z0-9]/', '', strtolower($en)); // 양쪽 모두 CRC32 처리하여 저장한다. $ps->execute(array($ko, Postcodify_Utility::crc32_x64($ko), $en, Postcodify_Utility::crc32_x64($en_canonical))); // 카운터를 표시한다. if (++$count % 512 === 0) { Postcodify_Utility::print_progress($count); } } // 뒷정리. $db->commit(); unset($db); Postcodify_Utility::print_ok($count); }
public function load_updates($after_date) { // DB를 준비한다. $db = Postcodify_Utility::get_db(); $db->beginTransaction(); // 도로명코드 테이블 관련 쿼리들. $ps_road_select = $db->prepare('SELECT * FROM postcodify_roads WHERE road_id = ?'); $ps_road_insert = $db->prepare('INSERT INTO postcodify_roads (road_id, road_name_ko, road_name_en, ' . 'sido_ko, sido_en, sigungu_ko, sigungu_en, ilbangu_ko, ilbangu_en, eupmyeon_ko, eupmyeon_en, updated) ' . 'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); $ps_road_update = $db->prepare('UPDATE postcodify_roads SET road_name_ko = ?, road_name_en = ?, ' . 'sido_ko = ?, sido_en = ?, sigungu_ko = ?, sigungu_en = ?, ilbangu_ko = ?, ilbangu_en = ?, ' . 'eupmyeon_ko = ?, eupmyeon_en = ?, updated = ? WHERE road_id = ?'); // 주소 테이블 관련 쿼리들. $ps_addr_select = $db->prepare('SELECT * FROM postcodify_addresses WHERE road_id >= ? AND road_id <= ? AND ' . 'num_major = ? AND (num_minor = ? OR (? IS NULL AND num_minor IS NULL))' . 'AND is_basement = ? ORDER BY id LIMIT 1'); $ps_addr_insert = $db->prepare('INSERT INTO postcodify_addresses (postcode5, postcode6, ' . 'road_id, num_major, num_minor, is_basement, dongri_ko, dongri_en, jibeon_major, jibeon_minor, is_mountain, ' . 'building_id, building_name, building_nums, other_addresses, updated) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); $ps_addr_update = $db->prepare('UPDATE postcodify_addresses SET postcode5 = ?, postcode6 = ?, ' . 'road_id = ?, num_major = ?, num_minor = ?, is_basement = ?, dongri_ko = ?, dongri_en = ?, ' . 'jibeon_major = ?, jibeon_minor = ?, is_mountain = ?, building_id = ?, building_name = ?, building_nums = ?, ' . 'other_addresses = ?, updated = ? WHERE id = ?'); $ps_addr_update_other = $db->prepare('UPDATE postcodify_addresses SET other_addresses = ? WHERE id = ?'); // 도로명 및 동·리 키워드 관련 쿼리들. $ps_kwd_select = $db->prepare('SELECT keyword_crc32 FROM postcodify_keywords WHERE address_id = ?'); $ps_kwd_insert = $db->prepare('INSERT INTO postcodify_keywords (address_id, keyword_crc32) VALUES (?, ?)'); // 건물번호 및 지번 키워드 관련 쿼리들. $ps_num_select = $db->prepare('SELECT num_major, num_minor FROM postcodify_numbers WHERE address_id = ?'); $ps_num_insert = $db->prepare('INSERT INTO postcodify_numbers (address_id, num_major, num_minor) VALUES (?, ?, ?)'); // 건물명 키워드 관련 쿼리들. $ps_building_select = $db->prepare('SELECT keyword FROM postcodify_buildings WHERE address_id = ?'); $ps_building_insert = $db->prepare('INSERT INTO postcodify_buildings (address_id, keyword) VALUES (?, ?)'); $ps_building_update = $db->prepare('UPDATE postcodify_buildings SET keyword = ? WHERE address_id = ?'); $ps_building_delete = $db->prepare('DELETE FROM postcodify_buildings WHERE address_id = ?'); // 데이터 파일 목록을 구한다. $files = glob($this->_data_dir . '/????????_dailynoticedata.zip'); $last_date = $after_date; // 각 파일을 순서대로 파싱한다. foreach ($files as $filename) { // 데이터 기준일 이전의 파일은 무시한다. $file_date = substr(basename($filename), 0, 8); if (strcmp($file_date, $after_date) <= 0) { continue; } if (strcmp($file_date, $last_date) > 0) { $last_date = $file_date; } Postcodify_Utility::print_message('업데이트 파일: ' . $file_date . '_dailynoticedata.zip'); // 도로정보 파일을 연다. Postcodify_Utility::print_message(' - 도로명코드'); $zip = new Postcodify_Parser_Road_List(); $zip->open_archive($filename); $open_status = $zip->open_named_file('TI_SPRD_STRET'); if (!$open_status) { continue; } // 카운터를 초기화한다. $count = 0; // 데이터를 한 줄씩 읽는다. while ($entry = $zip->read_line()) { // 카운터를 표시한다. if (++$count % 32 === 0) { Postcodify_Utility::print_progress($count); } // 폐지된 도로는 무시한다. if ($entry->change_reason === 1) { unset($entry); continue; } // 이미 존재하는 도로인지 확인한다. $ps_road_select->execute(array($entry->road_id . $entry->road_section)); $road_exists = $ps_road_select->fetchColumn(); $ps_road_select->closeCursor(); // 신규 또는 변경된 도로 정보를 DB에 저장한다. if (!$road_exists) { $ps_road_insert->execute(array($entry->road_id . $entry->road_section, $entry->road_name_ko, $entry->road_name_en, $entry->sido_ko, $entry->sido_en, $entry->sigungu_ko, $entry->sigungu_en, $entry->ilbangu_ko, $entry->ilbangu_en, $entry->eupmyeon_ko, $entry->eupmyeon_en, $entry->updated)); } else { $ps_road_update->execute(array($entry->road_name_ko, $entry->road_name_en, $entry->sido_ko, $entry->sido_en, $entry->sigungu_ko, $entry->sigungu_en, $entry->ilbangu_ko, $entry->ilbangu_en, $entry->eupmyeon_ko, $entry->eupmyeon_en, $entry->updated, $entry->road_id . $entry->road_section)); } // 뒷정리. unset($entry); } // 파일을 닫는다. $zip->close(); unset($zip); Postcodify_Utility::print_ok($count); // 건물정보 파일을 연다. Postcodify_Utility::print_message(' - 건물정보'); $zip = new Postcodify_Parser_NewAddress(); $zip->open_archive($filename); $open_status = $zip->open_named_file('TI_SPBD_BULD'); if (!$open_status) { continue; } // 카운터를 초기화한다. $count = 0; // 이전 주소를 초기화한다. $is_first_entry = true; $last_entry = null; $last_nums = array(); // 데이터를 한 줄씩 읽어 처리한다. while (true) { // 읽어온 줄을 분석한다. $entry = $zip->read_line(); if ($is_first_entry && $entry === false) { break; } $is_first_entry = false; // 이전 주소가 없다면 방금 읽어온 줄을 이전 주소로 설정한다. if ($last_entry === null) { $last_entry = $entry; if (($entry->has_detail || $entry->is_common_residence) && preg_match('/.+동$/u', $entry->building_detail)) { $last_nums = array(preg_replace('/동$/u', '', $entry->building_detail)); } elseif ($entry->building_detail) { $last_entry->building_names[] = $last_entry->building_detail; $last_nums = array(); } else { $last_nums = array(); } } elseif ($entry === false || $last_entry->road_id !== $entry->road_id || $last_entry->road_section !== $entry->road_section || $last_entry->num_major !== $entry->num_major || $last_entry->num_minor !== $entry->num_minor || $last_entry->is_basement !== $entry->is_basement) { // 디버깅을 위한 변수들. $update_type = false; $postcode6_is_guess = false; $postcode5_is_guess = false; // 이미 존재하는 주소인지 확인한다. $ps_addr_select->execute(array($last_entry->road_id . '00', $last_entry->road_id . '99', $last_entry->num_major, $last_entry->num_minor, $last_entry->num_minor, $last_entry->is_basement)); $address_info = $ps_addr_select->fetchObject(); $ps_addr_select->closeCursor(); // 도로 정보를 파악한다. $ps_road_select->execute(array($last_entry->road_id . $last_entry->road_section)); $road_info = $ps_road_select->fetchObject(); $ps_road_select->closeCursor(); // 도로 정보가 없는 경우, 더미 레코드를 생성한다. if (!$road_info) { // 더미 레코드를 DB에 입력한다. $ps_road_insert->execute(array($last_entry->road_id . $last_entry->road_section, $last_entry->road_name, $this->find_english_name($db, $last_entry->road_name), $last_entry->sido, $this->find_english_name($db, $last_entry->sido), $last_entry->sigungu, $this->find_english_name($db, $last_entry->sigungu), $last_entry->ilbangu, $this->find_english_name($db, $last_entry->ilbangu), $last_entry->eupmyeon, $this->find_english_name($db, $last_entry->eupmyeon), '99999999')); // 입력한 더미 레코드를 DB에서 다시 불러온다. $ps_road_select->execute(array($last_entry->road_id . $last_entry->road_section)); $road_info = $ps_road_select->fetchObject(); $ps_road_select->closeCursor(); } // 이미 존재하는 주소인 경우, 기존의 검색 키워드와 번호들을 가져온다. $existing_keywords = $existing_numbers = $existing_buildings = array(); if ($address_info) { $ps_kwd_select->execute(array($address_info->id)); while ($row = $ps_kwd_select->fetchColumn()) { $existing_keywords[$row] = true; } $ps_kwd_select->closeCursor(); $ps_num_select->execute(array($address_info->id)); while ($row = $ps_num_select->fetch(PDO::FETCH_NUM)) { $existing_numbers[implode('-', $row)] = true; } $ps_num_select->closeCursor(); $ps_building_select->execute(array($address_info->id)); while ($row = $ps_building_select->fetchColumn()) { $existing_buildings[] = $row; } $ps_building_select->closeCursor(); } // 신설과 변경 주소 구분은 변경코드에 의존하지 않고, // 로컬 DB에 해당 주소가 이미 존재하는지에 따라 판단한다. if ($last_entry->change_reason !== self::ADDR_DELETED) { // 우편번호가 누락된 경우, 범위 데이터를 사용하여 찾거나 기존 주소의 우편번호를 그대로 사용한다. if ($last_entry->postcode6 === null || $last_entry->postcode6 === '000000') { if ($address_info && $address_info->postcode6 !== null) { $last_entry->postcode6 = $address_info->postcode6; } elseif ($this->_add_old_postcodes) { $last_entry->postcode6 = $this->find_postcode6($db, $road_info, $last_entry->dongri, $last_entry->admin_dongri, $last_entry->jibeon_major, $last_entry->jibeon_minor); $postcode6_is_guess = true; } } if ($last_entry->postcode5 === null || $last_entry->postcode5 === '00000') { if ($address_info && $address_info->postcode5 !== null) { $last_entry->postcode5 = $address_info->postcode5; } else { $last_entry->postcode5 = $this->find_postcode5($db, $road_info, $last_entry->num_major, $last_entry->num_minor, $last_entry->dongri, $last_entry->admin_dongri, $last_entry->jibeon_major, $last_entry->jibeon_minor, $last_entry->postcode6); $postcode5_is_guess = true; } } // 영문 동·리를 구한다. if ($address_info && $last_entry->dongri === $address_info->dongri_ko) { $dongri_en = $address_info->dongri_en; } else { $dongri_en = $this->find_english_name($db, $last_entry->dongri); } // 상세건물명과 기타 주소를 정리한다. $building_nums = Postcodify_Utility::consolidate_building_nums($last_nums); if ($building_nums === '') { $building_nums = null; } $other_addresses = array(); if ($last_entry->admin_dongri && $last_entry->admin_dongri !== $last_entry->dongri) { $other_addresses[] = $last_entry->admin_dongri; } $last_entry->building_names = array_unique($last_entry->building_names); $last_entry->building_names = Postcodify_Utility::consolidate_building_names($last_entry->building_names, $last_entry->common_residence_name); natcasesort($last_entry->building_names); foreach ($last_entry->building_names as $building_name) { $other_addresses[] = str_replace(';', ':', $building_name); } // 더미 레코드에 관련지번이 먼저 입력되어 있는 경우, 다시 추가한다. if ($address_info && strval($address_info->updated) === '99999999' && strval($address_info->other_addresses) !== '') { $other_addresses[] = $address_info->other_addresses; } $other_addresses = implode('; ', $other_addresses); if ($other_addresses === '') { $other_addresses = null; } // 공동주택인데 공동주택명이 없는 경우 다른 건물명을 이용한다. if ($last_entry->is_common_residence && $last_entry->common_residence_name === null) { if (strpos($other_addresses, '; ') === false) { $last_entry->common_residence_name = $other_addresses; $other_addresses = null; } } // 신설 주소인 경우... if (!$address_info) { $ps_addr_insert->execute(array($last_entry->postcode5, $last_entry->postcode6, $last_entry->road_id . $last_entry->road_section, $last_entry->num_major, $last_entry->num_minor, $last_entry->is_basement, $last_entry->dongri, $dongri_en, $last_entry->jibeon_major, $last_entry->jibeon_minor, $last_entry->is_mountain, $last_entry->building_id, $last_entry->common_residence_name, $building_nums, $other_addresses, $last_entry->updated)); $proxy_id = $db->lastInsertId(); $update_type = 'C'; } else { $ps_addr_update->execute(array($last_entry->postcode5, $last_entry->postcode6, $last_entry->road_id . $last_entry->road_section, $last_entry->num_major, $last_entry->num_minor, $last_entry->is_basement, $last_entry->dongri, $dongri_en, $last_entry->jibeon_major, $last_entry->jibeon_minor, $last_entry->is_mountain, $last_entry->building_id, $last_entry->common_residence_name, $building_nums, $other_addresses, $last_entry->updated, $address_info->id)); $proxy_id = $address_info->id; $update_type = 'M'; } // 검색 키워드를 정리하여 저장한다. $keywords = array(); $keywords = array_merge($keywords, Postcodify_Utility::get_variations_of_road_name($road_info->road_name_ko)); $keywords = array_merge($keywords, Postcodify_Utility::get_variations_of_dongri($last_entry->dongri)); $keywords = array_merge($keywords, Postcodify_Utility::get_variations_of_dongri($last_entry->admin_dongri)); $keywords = array_unique($keywords); foreach ($keywords as $keyword) { $keyword_crc32 = Postcodify_Utility::crc32_x64($keyword); if (isset($existing_keywords[$keyword_crc32])) { continue; } $ps_kwd_insert->execute(array($proxy_id, $keyword_crc32)); } // 번호들을 정리하여 저장한다. $numbers = array(array($last_entry->num_major, $last_entry->num_minor), array($last_entry->jibeon_major, $last_entry->jibeon_minor)); /* if (preg_match('/([0-9]+)번?길$/u', $road_info->road_name_ko, $road_name_matches)) { $numbers[] = array(intval($road_name_matches[1]), null); } */ foreach ($numbers as $number) { $number_key = implode('-', $number); if (isset($existing_numbers[$number_key])) { continue; } $ps_num_insert->execute(array($proxy_id, $number[0], $number[1])); } // 건물명을 정리하여 저장한다. $building_names = array_merge($existing_buildings, $last_entry->building_names); $building_names_str = Postcodify_Utility::compress_building_names($building_names); if ($building_names_str !== '' && !in_array($building_names_str, $existing_buildings)) { if (count($existing_buildings)) { $ps_building_update->execute(array($building_names_str, $proxy_id)); } else { $ps_building_insert->execute(array($proxy_id, $building_names_str)); } } } // 폐지된 주소인 경우... if ($last_entry->change_reason === self::ADDR_DELETED) { // 행자부에서 멀쩡한 주소를 삭제했다가 며칠 후 다시 추가하는 경우가 종종 있다. // 이걸 너무 열심히 따라하면 애꿎은 사용자들이 불편을 겪게 되므로 // 주소가 폐지된 것으로 나오더라도 DB에는 그대로 두는 것이 좋다. // 나중에 다시 추가될 경우 위의 코드에 따라 업데이트로 처리하면 그만이다. $update_type = 'D'; } // 카운터를 표시한다. if (++$count % 32 === 0) { Postcodify_Utility::print_progress($count); } // 불필요한 변수들을 unset한다. unset($address_info, $road_info, $road_name_matches, $existing_keywords, $existing_numbers, $existing_buildings); unset($keywords, $numbers, $building_names, $building_names_str); unset($last_entry, $last_nums); // 방금 읽어온 줄을 새로운 이전 주소로 설정한다. if ($entry !== false) { $last_entry = $entry; if (($entry->has_detail || $entry->is_common_residence) && preg_match('/.+동$/u', $entry->building_detail)) { $last_nums = array(preg_replace('/동$/u', '', $entry->building_detail)); } elseif ($entry->building_detail) { $last_entry->building_names[] = $last_entry->building_detail; $last_nums = array(); } else { $last_nums = array(); } } } else { if (count($entry->building_names)) { $last_entry->building_names = array_merge($last_entry->building_names, $entry->building_names); } if (($entry->has_detail || $entry->is_common_residence) && preg_match('/.+동$/u', $entry->building_detail)) { $last_nums[] = preg_replace('/동$/u', '', $entry->building_detail); } elseif ($entry->building_detail) { $last_entry->building_names[] = $entry->building_detail; } } // 더이상 데이터가 없는 경우 루프를 탈출한다. if ($entry === false) { break; } // 메모리 누수를 방지하기 위해 모든 배열을 unset한다. unset($entry); } // 건물정보 파일을 닫는다. $zip->close(); unset($zip); Postcodify_utility::print_ok($count); // 관련지번 파일을 연다. Postcodify_Utility::print_message(' - 관련지번'); $zip = new Postcodify_Parser_NewJibeon(); $zip->open_archive($filename); $open_status = $zip->open_named_file('TI_SCCO_MVMN'); if (!$open_status) { continue; } // 카운터를 초기화한다. $count = 0; // 관련지번 업데이트는 건물정보 파일처럼 도로명주소 단위로 연속되어 나온다는 보장이 없다. // 따라서 $last_entry를 사용하는 방식은 적절하지 않고, 1천 건 단위로 분류하여 사용한다. // 1천 건의 경계선에 걸리는 주소는 두 번에 걸쳐 업데이트되는 비효율성이 있으나, // 현실적으로 하루에 1천 건 이상 업데이트되는 경우는 드물다. while (true) { // 1천 건 단위로 읽어와서 도로명주소 단위로 분류한다. $entries = array(); for ($i = 0; $i < 1000; $i++) { $entry = $zip->read_line(); if ($entry === false) { break; } $key = $entry->road_id . '|' . $entry->num_major . '|' . $entry->num_minor . '|' . $entry->is_basement; $entries[$key][] = array($entry->dongri, $entry->jibeon_major, $entry->jibeon_minor, $entry->is_mountain); unset($entry); } // 더이상 데이터가 없는 경우 루프를 탈출한다. if (!count($entries)) { break; } // 분류한 데이터를 처리한다. foreach ($entries as $key => $jibeons) { // 분류에 사용했던 키를 분해하여 원래의 도로명주소를 구한다. list($road_id, $num_major, $num_minor, $is_basement) = explode('|', $key); $num_major = intval($num_major); $num_minor = intval($num_minor); if (!$num_minor) { $num_minor = null; } $is_basement = intval($is_basement); // 이 주소에 해당하는 도로명주소 레코드를 가져온다. $ps_addr_select->execute(array($road_id . '00', $road_id . '99', $num_major, $num_minor, $num_minor, $is_basement)); $address_info = $ps_addr_select->fetchObject(); $ps_addr_select->closeCursor(); // 레코드를 찾은 경우, 기존 정보를 가져온다. if ($address_info) { // 기존의 건물명 및 지번 목록을 파싱한다. $other_addresses = array('b' => array(), 'j' => array()); $other_addresses_raw = explode('; ', $address_info->other_addresses); foreach ($other_addresses_raw as $i => $other_address) { if (preg_match('/^(.+[동리로가])\\s(산?[0-9-]+(?:,\\s산?[0-9-]+)*)$/u', $other_address, $matches)) { $dongri = $matches[1]; $nums = explode(', ', $matches[2]); $other_addresses[$dongri] = $nums; } elseif (strlen($other_address)) { $other_addresses['b'][] = $other_address; } } // 기존의 검색 키워드와 번호들을 가져온다. $existing_keywords = array(); $ps_kwd_select->execute(array($address_info->id)); while ($row = $ps_kwd_select->fetchColumn()) { $existing_keywords[$row] = true; } $ps_kwd_select->closeCursor(); $existing_numbers = array(); $ps_num_select->execute(array($address_info->id)); while ($row = $ps_num_select->fetch(PDO::FETCH_NUM)) { $existing_numbers[implode('-', $row)] = true; } $ps_num_select->closeCursor(); } else { // 더미 레코드를 DB에 입력한다. $ps_addr_insert->execute(array(null, null, $road_id . '00', $num_major, $num_minor, $is_basement, $jibeons[0][0], $this->find_english_name($db, $jibeons[0][0]), $jibeons[0][1], $jibeons[0][2], $jibeons[0][3], null, null, null, null, '99999999')); // 입력한 더미 레코드를 DB에서 다시 불러온다. $ps_addr_select->execute(array($road_id . '00', $road_id . '99', $num_major, $num_minor, $num_minor, $is_basement)); $address_info = $ps_addr_select->fetchObject(); $ps_addr_select->closeCursor(); // 기존의 건물명, 지번 목록, 검색 키워드와 번호들은 빈 배열로 초기화한다. $other_addresses = array('b' => array(), 'j' => array()); $existing_keywords = array(); $existing_numbers = array(); } // 업데이트된 지번 목록을 추가하고, 중복을 제거한다. foreach ($jibeons as $last_num) { $numtext = ($last_num[3] ? '산' : '') . $last_num[1] . ($last_num[2] ? '-' . $last_num[2] : ''); $other_addresses['j'][$last_num[0]][] = $numtext; } foreach ($other_addresses['j'] as $dongri => $nums) { $other_addresses['j'][$dongri] = array_unique($other_addresses['j'][$dongri]); } // 기타 주소 목록을 정리하여 업데이트한다. $other_addresses_temp = array(); foreach ($other_addresses['b'] as $building_name) { $other_addresses_temp[] = $building_name; } foreach ($other_addresses['j'] as $dongri => $nums) { natsort($nums); $other_addresses_temp[] = $dongri . ' ' . implode(', ', $nums); } $ps_addr_update_other->execute(array(implode('; ', $other_addresses_temp), $address_info->id)); // 업데이트된 검색 키워드와 번호들을 추가한다. $keywords = array(); foreach ($jibeons as $last_num) { $keywords = array_merge($keywords, Postcodify_Utility::get_variations_of_dongri($last_num[0])); } $keywords = array_unique($keywords); foreach ($keywords as $keyword) { $keyword_crc32 = Postcodify_Utility::crc32_x64($keyword); if (isset($existing_keywords[$keyword_crc32])) { continue; } $ps_kwd_insert->execute(array($address_info->id, $keyword_crc32)); } // 번호들을 정리하여 저장한다. foreach ($jibeons as $last_num) { $number_key = $last_num[1] . '-' . $last_num[2]; if (isset($existing_numbers[$number_key])) { continue; } $ps_num_insert->execute(array($address_info->id, $last_num[1], $last_num[2])); } // 카운터를 표시한다. if (++$count % 32 === 0) { Postcodify_Utility::print_progress($count); } // 불필요한 변수들을 unset한다. unset($key, $jibeons, $road_id, $num_major, $num_minor, $is_basement); unset($address_info, $other_addresses, $other_addresses_raw, $other_addresses_temp, $nums, $last_num); unset($existing_keywords, $existing_numbers); unset($keywords, $numbers); } // 불필요한 변수들을 unset한다. unset($entries); } // 관련지번 파일을 닫는다. $zip->close(); unset($zip); Postcodify_utility::print_ok($count); } // 뒷정리. $db->commit(); unset($db); // 마지막으로 처리한 파일의 기준일을 반환한다. return $last_date; }
public function create_indexes($sqlite) { foreach ($this->_indexes as $table_name => $columns) { Postcodify_Utility::print_message($table_name . ' 인덱스 생성 중...'); $count = 0; foreach ($columns as $column) { Postcodify_Utility::print_progress($count++, count($columns)); $sqlite->exec('CREATE INDEX ' . $table_name . '_' . $column . ' ON ' . $table_name . ' (' . $column . ')'); } Postcodify_Utility::print_ok(count($columns)); } }
public function check() { // 기본적인 환경을 확인한다. if (version_compare(PHP_VERSION, '5.2', '<')) { echo '[ERROR] PHP 버전은 5.2 이상이어야 합니다.' . PHP_EOL; exit(2); } if (strtolower(PHP_SAPI) !== 'cli') { echo '[ERROR] 이 프로그램은 명령줄(CLI)에서 실행되어야 합니다.' . PHP_EOL; exit(2); } if (strtolower(substr(PHP_OS, 0, 3)) === 'win') { echo '[ERROR] 윈도우 환경은 지원하지 않습니다.' . PHP_EOL; exit(2); } // 필요한 모듈과 함수들을 확인한다. if (!class_exists('PDO') || !in_array('mysql', PDO::getAvailableDrivers())) { echo '[ERROR] PDO 모듈이 설치되지 않았거나 MySQL 드라이버를 사용할 수 없습니다.' . PHP_EOL; exit(2); } if (!class_exists('ZipArchive')) { echo '[ERROR] Zip 모듈이 설치되어 있지 않습니다.' . PHP_EOL; exit(2); } if (!function_exists('mb_check_encoding')) { echo '[ERROR] mbstring 모듈이 설치되어 있지 않습니다.' . PHP_EOL; exit(2); } if (!function_exists('pcntl_fork') || !function_exists('pcntl_wait')) { echo '[ERROR] pcntl_* 함수가 없거나 php.ini에서 막아 놓았습니다.' . PHP_EOL; exit(2); } if (!function_exists('shmop_open')) { echo '[ERROR] shmop_* 함수가 없거나 php.ini에서 막아 놓았습니다.' . PHP_EOL; exit(2); } // 필요한 데이터 파일이 모두 있는지 확인한다. $data_address_file = null; $data_roadlist_file = null; $data_files = scandir(dirname(POSTCODIFY_LIB_DIR) . '/data'); foreach ($data_files as $filename) { if (preg_match('/^20[0-9]{4}RDNM(ADR|CODE)\\.zip$/', $filename, $matches)) { if ($matches[1] === 'ADR') { $data_address_file = $filename; } else { $data_roadlist_file = $filename; } } } if (!$data_address_file) { echo '[ERROR] ******RDNMADR.zip 파일을 찾을 수 없습니다.' . PHP_EOL; exit(2); } if (!$data_roadlist_file) { echo '[ERROR] ******RDNMCODE.zip 파일을 찾을 수 없습니다.' . PHP_EOL; exit(2); } if (substr($data_address_file, 0, 10) !== substr($data_roadlist_file, 0, 10)) { echo '[ERROR] ******RDNMADR.zip 파일과 ******RDNMCODE.zip 파일의 날짜가 서로 다릅니다.' . PHP_EOL; exit(2); } if (!file_exists(dirname(POSTCODIFY_LIB_DIR) . '/data/areacd_pobox_DB.zip')) { echo '[ERROR] 우체국 사서함 (areacd_pobox_DB.zip) 파일을 찾을 수 없습니다.' . PHP_EOL; exit(2); } if (!file_exists(dirname(POSTCODIFY_LIB_DIR) . '/data/areacd_rangeaddr_DB.zip')) { echo '[ERROR] 우편번호 범위 (areacd_rangeaddr_DB.zip) 파일을 찾을 수 없습니다.' . PHP_EOL; exit(2); } if (!file_exists(dirname(POSTCODIFY_LIB_DIR) . '/data/english_aliases_DB.zip')) { echo '[ERROR] 영문 번역 (english_aliases_DB.zip) 파일을 찾을 수 없습니다.' . PHP_EOL; exit(2); } if (!file_exists(dirname(POSTCODIFY_LIB_DIR) . '/data/oldaddr_zipcode_DB.zip')) { echo '[ERROR] 구 우편번호 범위 (oldaddr_zipcode_DB.zip) 파일을 찾을 수 없습니다.' . PHP_EOL; exit(2); } // DB의 사양을 점검한다. if (!($db = Postcodify_Utility::get_db())) { echo '[ERROR] MySQL DB에 접속할 수 없습니다.' . PHP_EOL; exit(2); } $version_query = $db->query('SELECT VERSION()'); $version = $version_query->fetchColumn(); if (!version_compare($version, '5.0', '>=')) { echo '[ERROR] MySQL DB의 버전은 5.0 이상이어야 합니다. 현재 사용중인 DB의 버전은 ' . $version . '입니다.' . PHP_EOL; exit(2); } $innodb_found = false; $innodb_query = $db->query('SHOW ENGINES'); while ($row = $innodb_query->fetch(PDO::FETCH_NUM)) { if (strtolower($row[0]) === 'innodb') { $innodb_found = true; break; } unset($row); } if (!$innodb_found) { echo '[ERROR] MySQL DB가 InnoDB 테이블 저장 엔진을 지원하지 않습니다.' . PHP_EOL; exit(2); } $buffersize_query = $db->query('SHOW VARIABLES LIKE \'innodb_buffer_pool_size\''); $buffersize = $buffersize_query->fetchColumn(1); if ($buffersize < 128 * 1024 * 1024) { $buffersize = round($buffersize / 1024 / 1024) . 'M'; echo '[ERROR] MySQL DB의 InnoDB 버퍼 크기를 128M 이상으로 설정해 주십시오. 현재 설정은 ' . $buffersize . '입니다.' . PHP_EOL; exit(2); } }