Пример #1
0
 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);
     }
 }
Пример #2
0
 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;
 }
Пример #3
0
    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);
Пример #4
0
 public static function progress($ch, $fd = null, $size = null)
 {
     if ($size <= 0) {
         return;
     }
     Postcodify_Utility::print_progress($size);
 }
Пример #5
0
 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);
 }
Пример #6
0
 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;
 }
Пример #7
0
 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));
     }
 }
Пример #8
0
 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);
     }
 }