그누보드 크롤링 및 마이그레이션용 board class
Board.php 클래스 사용 설명서
그누보드5 게시판 게시물 등록 클래스
개요
Board 클래스는 그누보드5 게시판에 프로그래밍 방식으로 게시물을 등록할 수 있는 유틸리티 클래스입니다.
의존성 주입(DI) 패턴을 사용하여 $g5와 $config를 외부에서 주입받습니다.
파일 위치
/lib/Board.php기본 사용법
<?php
// common.php 로드 (필수)
include_once('../common.php');
// Board 클래스 로드
include_once(G5_LIB_PATH.'/Board.php');
// 인스턴스 생성 (의존성 주입)
$board = new Board($g5, $config);
// 게시물 등록
$wr_id = $board->write(
'freeboard', // 게시판 테이블명
'게시물 제목', // 제목
'게시물 내용입니다.' // 내용
);
if ($wr_id) {
echo "게시물 등록 성공! ID: {$wr_id}";
} else {
echo "게시물 등록 실패";
}
write() 메서드
메서드 시그니처:
write($bo_table, $subject, $content, $files = array(), $options = array())
매개변수:
$bo_table(string, 필수) - 게시판 테이블명 (예: freeboard, notice)$subject(string, 필수) - 게시물 제목$content(string, 필수) - 게시물 내용 (HTML 가능)$files(array, 선택) - 첨부파일 경로 배열$options(array, 선택) - 추가 옵션 배열
반환값:
int|false - 성공 시 등록된 게시글 ID(wr_id), 실패 시 false
$options 배열 옵션
작성자 정보:
mb_id- 회원 아이디 (기본값: 'admin')wr_name- 작성자명 (기본값: '관리자')wr_email- 이메일 (기본값: '')wr_ip- 작성자 IP (기본값: REMOTE_ADDR)
게시물 속성:
ca_name- 분류 (기본값: '')wr_link1,wr_link2- 링크 URL (기본값: '')html- HTML 사용: 'html1' (태그필터), 'html2' (모든태그), '' (사용안함) (기본값: 'html1')secret- 비밀글: 'secret' 또는 '' (기본값: '')mail- 답변 메일 수신: 'mail' 또는 '' (기본값: '')
날짜/시간:
wr_datetime- 등록일시 (기본값: 현재시간, Y-m-d H:i:s 형식)wr_last- 최종 수정일시 (기본값: wr_datetime과 동일)
기타:
wr_hit- 조회수 (기본값: 0)wr_num- 글 번호 (기본값: 자동생성)wr_parent- 부모 게시글 ID (기본값: 자동설정)wr_1~wr_10- 여분 필드 (기본값: '')
파일 첨부 예제
방식 1: 단순 경로 배열
$files = [
'/home/www/data/uploads/image1.jpg',
'/home/www/data/uploads/document.pdf'
];
$wr_id = $board->write('freeboard', '제목', '내용', $files);
방식 2: 연관 배열 (원본 파일명 지정)
$files = [
['path' => '/tmp/upload_abc123', 'name' => '원본파일명.jpg'],
['path' => '/tmp/upload_def456', 'name' => '문서.pdf']
];
$wr_id = $board->write('freeboard', '제목', '내용', $files);
전체 옵션 사용 예제
<?php
include_once('../common.php');
include_once(G5_LIB_PATH.'/Board.php');
$board = new Board($g5, $config);
// 첨부 파일
$files = [
'/home/www/data/temp/report.pdf'
];
// 옵션 설정
$options = [
'mb_id' => 'user123',
'wr_name' => '홍길동',
'wr_email' => 'user@example.com',
'ca_name' => '공지사항',
'wr_link1' => 'https://example.com',
'html' => 'html2', // 모든 HTML 태그 허용
'secret' => '', // 공개글
'wr_datetime' => '2024-01-15 10:30:00',
'wr_1' => '커스텀값1', // 여분 필드
'wr_2' => '커스텀값2'
];
$content = '
<h2>안내사항</h2>
<p>본 게시물은 <strong>자동 등록</strong>된 게시물입니다.</p>
<ul>
<li>첫 번째 항목</li>
<li>두 번째 항목</li>
</ul>
';
$wr_id = $board->write(
'notice', // 공지사항 게시판
'[공지] 시스템 점검 안내',
$content,
$files,
$options
);
if ($wr_id) {
echo "등록 완료: wr_id = {$wr_id}";
}
내부 동작 흐름
- 1 게시판 테이블 존재 여부 확인
- 2 옵션 기본값 설정 및 데이터 이스케이프 처리
- 3 g5_write_{bo_table} 테이블에 게시글 INSERT
- 4 g5_board_new 테이블에 새글 정보 INSERT
- 5 g5_board 테이블의 게시글 수 증가 (bo_count_write)
- 6 첨부파일 처리 (data/file/{bo_table}/ 디렉토리에 저장)
- 7 g5_board_file 테이블에 파일 정보 INSERT
주의사항
- common.php 필수: 반드시 common.php를 먼저 include해야 합니다.
- 게시판 존재 확인: bo_table에 해당하는 게시판이 실제로 존재해야 합니다.
- 파일 경로: $files에 전달하는 경로는 서버의 물리적 절대 경로여야 합니다.
- 권한: 파일 업로드 디렉토리에 쓰기 권한이 필요합니다.
- 실행파일 차단: php, cgi, exe 등 실행 파일은 자동으로 확장자에 -x가 추가됩니다.
- 이미지 검증: 이미지 확장자 파일은 실제 이미지인지 검증됩니다.
활용 예시
1. 외부 데이터 마이그레이션:
기존 시스템의 게시물을 그누보드5로 이전할 때 사용
2. API 연동:
외부 서비스에서 받은 데이터를 게시판에 자동 등록
3. 크롤링 데이터 저장:
웹 크롤링으로 수집한 데이터를 게시판에 저장
4. 배치 작업:
정기적인 알림이나 공지사항 자동 등록
Board.php 클래스 사용 설명서 | 파일 위치: /lib/Board.php
[code]
[/code]
<?php
/**
* 그누보드5 게시판 관리 클래스
*
* 의존성 주입 패턴을 사용하여 $g5를 외부에서 주입받습니다.
*/
if (!defined('_GNUBOARD_')) exit;
class Board {
private $g5;
private $config;
private $db;
/**
* 생성자
*
* @param array $g5 그누보드5 전역 설정 배열
* @param array $config 그누보드5 config 배열
*/
public function __construct($g5, $config) {
$this->g5 = $g5;
$this->config = $config;
$this->db = $g5['connect_db'];
}
/**
* 게시물 등록
*
* @param string $bo_table 게시판명 ex) freeboard
* @param string $subject 게시물 제목
* @param string $content 게시물 내용
* @param array $files 업로드할 파일 경로 배열, 물리적 경로입력
* ex) ['/home/account/www/data/uploads/image1.jpg', ...]
* @param array $options 추가 옵션
* - mb_id: 회원 아이디 (기본값: 'admin')
* - wr_name: 작성자명 (기본값: '관리자')
* - wr_email: 이메일 (기본값: '')
* - ca_name: 분류 (기본값: '')
* - wr_link1, wr_link2: 링크 (기본값: '')
* - html: HTML 사용 여부 (기본값: 'html1')
* - secret: 비밀글 여부 (기본값: '')
* - mail: 답변 메일 수신 (기본값: '')
* - wr_num: 글 번호 (기본값: 자동 생성)
* - wr_datetime: 등록일시 (기본값: 현재 시간)
* - wr_last: 최종 수정일시 (기본값: wr_datetime과 동일)
* - wr_hit: 조회수 (기본값: 0)
* - wr_ip: 작성자 IP (기본값: REMOTE_ADDR)
* - wr_parent: 부모 게시글 ID (기본값: 등록 후 자동 설정)
* - wr_1 ~ wr_10: 여분 필드
* @return int|false 등록된 게시글 ID(wr_id) 또는 실패 시 false
*/
public function write($bo_table, $subject, $content, $files = array(), $options = array()) {
// 게시판명 검증
if (empty($bo_table)) {
return false;
}
// 제목, 내용 검증
if (empty($subject) || empty($content)) {
return false;
}
$write_table = "g5_write_{$bo_table}";
// 테이블 존재 확인
$table_check = sql_query("SHOW TABLES LIKE '{$write_table}'");
if (sql_num_rows($table_check) == 0) {
return false;
}
// 옵션 기본값 설정
$mb_id = isset($options['mb_id']) ? $options['mb_id'] : 'admin';
$wr_name = isset($options['wr_name']) ? $options['wr_name'] : '관리자';
$wr_email = isset($options['wr_email']) ? $options['wr_email'] : '';
$ca_name = isset($options['ca_name']) ? $options['ca_name'] : '';
$wr_link1 = isset($options['wr_link1']) ? $options['wr_link1'] : '';
$wr_link2 = isset($options['wr_link2']) ? $options['wr_link2'] : '';
$html = isset($options['html']) ? $options['html'] : 'html1';
$secret = isset($options['secret']) ? $options['secret'] : '';
$mail = isset($options['mail']) ? $options['mail'] : '';
// wr_option 조합
$wr_option_arr = array();
if ($html) $wr_option_arr[] = $html;
if ($secret) $wr_option_arr[] = $secret;
if ($mail) $wr_option_arr[] = $mail;
$wr_option = implode(',', $wr_option_arr);
// wr_num 생성 (지정되지 않은 경우)
if (isset($options['wr_num'])) {
$wr_num = $options['wr_num'];
} else {
// common.lib.php의 get_next_num() 함수 사용
$wr_num = get_next_num($write_table);
}
$wr_reply = '';
// 날짜/시간 옵션 처리
$wr_datetime = isset($options['wr_datetime']) ? $options['wr_datetime'] : G5_TIME_YMDHIS;
$wr_last = isset($options['wr_last']) ? $options['wr_last'] : $wr_datetime;
$wr_hit = isset($options['wr_hit']) ? intval($options['wr_hit']) : 0;
$wr_ip = isset($options['wr_ip']) ? $options['wr_ip'] : $_SERVER['REMOTE_ADDR'];
$wr_parent = isset($options['wr_parent']) ? intval($options['wr_parent']) : 0;
// 데이터 이스케이프 처리
$wr_subject = sql_real_escape_string($subject);
$wr_content = sql_real_escape_string($content);
$ca_name = sql_real_escape_string($ca_name);
$wr_link1 = sql_real_escape_string($wr_link1);
$wr_link2 = sql_real_escape_string($wr_link2);
$mb_id = sql_real_escape_string($mb_id);
$wr_name = sql_real_escape_string($wr_name);
$wr_email = sql_real_escape_string($wr_email);
$wr_option = sql_real_escape_string($wr_option);
$wr_datetime = sql_real_escape_string($wr_datetime);
$wr_last = sql_real_escape_string($wr_last);
$wr_ip = sql_real_escape_string($wr_ip);
// 여분 필드 처리
$wr_1 = isset($options['wr_1']) ? sql_real_escape_string($options['wr_1']) : '';
$wr_2 = isset($options['wr_2']) ? sql_real_escape_string($options['wr_2']) : '';
$wr_3 = isset($options['wr_3']) ? sql_real_escape_string($options['wr_3']) : '';
$wr_4 = isset($options['wr_4']) ? sql_real_escape_string($options['wr_4']) : '';
$wr_5 = isset($options['wr_5']) ? sql_real_escape_string($options['wr_5']) : '';
$wr_6 = isset($options['wr_6']) ? sql_real_escape_string($options['wr_6']) : '';
$wr_7 = isset($options['wr_7']) ? sql_real_escape_string($options['wr_7']) : '';
$wr_8 = isset($options['wr_8']) ? sql_real_escape_string($options['wr_8']) : '';
$wr_9 = isset($options['wr_9']) ? sql_real_escape_string($options['wr_9']) : '';
$wr_10 = isset($options['wr_10']) ? sql_real_escape_string($options['wr_10']) : '';
// 게시글 INSERT
$sql = " INSERT INTO {$write_table}
SET wr_num = '{$wr_num}',
wr_reply = '{$wr_reply}',
wr_comment = 0,
ca_name = '{$ca_name}',
wr_option = '{$wr_option}',
wr_subject = '{$wr_subject}',
wr_content = '{$wr_content}',
wr_link1 = '{$wr_link1}',
wr_link2 = '{$wr_link2}',
wr_link1_hit = 0,
wr_link2_hit = 0,
wr_hit = {$wr_hit},
wr_good = 0,
wr_nogood = 0,
mb_id = '{$mb_id}',
wr_password = '',
wr_name = '{$wr_name}',
wr_email = '{$wr_email}',
wr_homepage = '',
wr_datetime = '{$wr_datetime}',
wr_last = '{$wr_last}',
wr_ip = '{$wr_ip}',
wr_1 = '{$wr_1}',
wr_2 = '{$wr_2}',
wr_3 = '{$wr_3}',
wr_4 = '{$wr_4}',
wr_5 = '{$wr_5}',
wr_6 = '{$wr_6}',
wr_7 = '{$wr_7}',
wr_8 = '{$wr_8}',
wr_9 = '{$wr_9}',
wr_10 = '{$wr_10}' ";
if (!sql_query($sql)) {
return false;
}
$wr_id = sql_insert_id();
// 부모 아이디에 UPDATE (옵션으로 지정되지 않은 경우 자신의 ID로 설정)
if ($wr_parent == 0) {
$wr_parent = $wr_id;
}
sql_query("UPDATE {$write_table} SET wr_parent = '{$wr_parent}' WHERE wr_id = '{$wr_id}'");
// 새글 INSERT
sql_query("INSERT INTO {$this->g5['board_new_table']}
(bo_table, wr_id, wr_parent, bn_datetime, mb_id)
VALUES ('{$bo_table}', '{$wr_id}', '{$wr_parent}', '{$wr_datetime}', '{$mb_id}')");
// 게시글 수 증가
sql_query("UPDATE {$this->g5['board_table']} SET bo_count_write = bo_count_write + 1 WHERE bo_table = '{$bo_table}'");
// 파일 처리
if (!empty($files) && is_array($files)) {
$file_count = $this->writeFiles($bo_table, $wr_id, $files);
// 파일 개수 업데이트
if ($file_count > 0) {
sql_query("UPDATE {$write_table} SET wr_file = '{$file_count}' WHERE wr_id = '{$wr_id}'");
}
}
return $wr_id;
}
/**
* 게시물 파일 업로드 처리
*
* @param string $bo_table 게시판명
* @param int $wr_id 게시글 ID
* @param array $files 파일 경로 배열 또는 파일 정보 배열
* - 문자열 배열: ['경로1', '경로2', ...]
* - 연관 배열: [['path' => '경로', 'name' => '원본명'], ...]
* @return int 업로드된 파일 개수
*/
private function writeFiles($bo_table, $wr_id, $files) {
if (empty($files) || !is_array($files)) {
return 0;
}
// 파일 개수 체크
$file_count = 0;
$upload_count = count($files);
for ($i = 0; $i < $upload_count; $i++) {
$file_path = is_array($files[$i]) ? $files[$i]['path'] : $files[$i];
if (!empty($file_path) && file_exists($file_path)) {
$file_count++;
}
}
if ($file_count == 0) {
return 0;
}
// 디렉토리가 없다면 생성
$upload_dir = G5_DATA_PATH.'/file/'.$bo_table;
if (!is_dir($upload_dir)) {
@mkdir($upload_dir, G5_DIR_PERMISSION);
@chmod($upload_dir, G5_DIR_PERMISSION);
}
$chars_array = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z'));
// 가변 파일 업로드
$upload = array();
$success_count = 0;
for ($i = 0; $i < count($files); $i++) {
$upload[$i] = array(
'file' => '',
'source' => '',
'filesize' => 0,
'image' => array('', '', '')
);
// 연관 배열 또는 문자열 처리
if (is_array($files[$i])) {
$tmp_file = $files[$i]['path'];
$original_name = isset($files[$i]['name']) ? $files[$i]['name'] : basename($tmp_file);
} else {
$tmp_file = $files[$i];
$original_name = basename($tmp_file);
}
if (!file_exists($tmp_file)) {
continue;
}
$filesize = filesize($tmp_file);
$filename = $original_name;
$filename = get_safe_filename($filename);
// 이미지나 플래시 파일 검증
$timg = @getimagesize($tmp_file);
if (preg_match("/\.({$this->config['cf_image_extension']})$/i", $filename) ||
preg_match("/\.({$this->config['cf_flash_extension']})$/i", $filename)) {
if (!$timg || $timg[2] < 1 || $timg[2] > 16) {
continue;
}
}
$upload[$i]['image'] = $timg ? $timg : array('', '', '');
$upload[$i]['source'] = $filename;
$upload[$i]['filesize'] = $filesize;
// 실행 파일 확장자 차단
$filename = preg_replace("/\.(php|phtm|htm|cgi|pl|exe|jsp|asp|inc)/i", "$0-x", $filename);
shuffle($chars_array);
$shuffle = implode('', $chars_array);
// 파일명 생성
$upload[$i]['file'] = abs(ip2long($_SERVER['REMOTE_ADDR'])).'_'.substr($shuffle, 0, 8).'_'.replace_filename($filename);
$dest_file = $upload_dir.'/'.$upload[$i]['file'];
// 파일 복사
if (copy($tmp_file, $dest_file)) {
chmod($dest_file, G5_FILE_PERMISSION);
$success_count++;
} else {
// 복사 실패 시 초기화
$upload[$i]['file'] = '';
$upload[$i]['source'] = '';
$upload[$i]['filesize'] = 0;
}
}
// 파일 정보를 DB에 저장
$saved_count = 0;
for ($i = 0; $i < count($upload); $i++) {
if (empty($upload[$i]['file'])) {
continue;
}
$bf_source = sql_real_escape_string($upload[$i]['source']);
$bf_file = sql_real_escape_string($upload[$i]['file']);
$bf_filesize = (int)$upload[$i]['filesize'];
$bf_width = isset($upload[$i]['image'][0]) ? (int)$upload[$i]['image'][0] : 0;
$bf_height = isset($upload[$i]['image'][1]) ? (int)$upload[$i]['image'][1] : 0;
$bf_type = isset($upload[$i]['image'][2]) ? (int)$upload[$i]['image'][2] : 0;
$sql = "INSERT INTO {$this->g5['board_file_table']}
SET bo_table = '{$bo_table}',
wr_id = '{$wr_id}',
bf_no = '{$saved_count}',
bf_source = '{$bf_source}',
bf_file = '{$bf_file}',
bf_content = '',
bf_download = 0,
bf_filesize = '{$bf_filesize}',
bf_width = '{$bf_width}',
bf_height = '{$bf_height}',
bf_type = '{$bf_type}',
bf_datetime = '".G5_TIME_YMDHIS."'";
if (sql_query($sql)) {
$saved_count++;
}
}
// 빈 파일 정보 삭제
$row = sql_fetch("SELECT MAX(bf_no) as max_bf_no FROM {$this->g5['board_file_table']}
WHERE bo_table = '{$bo_table}' AND wr_id = '{$wr_id}'");
if ($row && isset($row['max_bf_no'])) {
for ($i = (int)$row['max_bf_no']; $i >= 0; $i--) {
$row2 = sql_fetch("SELECT bf_file FROM {$this->g5['board_file_table']}
WHERE bo_table = '{$bo_table}' AND wr_id = '{$wr_id}' AND bf_no = '{$i}'");
if ($row2 && !empty($row2['bf_file'])) {
break;
}
sql_query("DELETE FROM {$this->g5['board_file_table']}
WHERE bo_table = '{$bo_table}' AND wr_id = '{$wr_id}' AND bf_no = '{$i}'");
}
}
// 최종 파일 개수 조회
$row = sql_fetch("SELECT COUNT(*) as cnt FROM {$this->g5['board_file_table']}
WHERE bo_table = '{$bo_table}' AND wr_id = '{$wr_id}'");
return $row ? (int)$row['cnt'] : 0;
}
}
[/code]
첨부파일
|
댓글을 작성하시려면 로그인이 필요합니다.
댓글 2개