]> 4ch.mooo.com Git - test.git/blob - lib/pio/pio.log.php
new file: ChangeLog
[test.git] / lib / pio / pio.log.php
1 <?php\r
2 /**\r
3  * PIO Log API\r
4  *\r
5  * 提供存取以 Log 檔案構成的資料結構後端的物件\r
6  *
7  * @package PMCLibrary\r
8  * @version $Id: pio.log.php 671 2008-09-20 05:54:15Z scribe $\r
9  * @date $Date: 2008-09-20 13:54:15 +0800 (星期六, 20 九月 2008) $
10  *
11  * @package PMCLibrary\r
12  * @version $Id$\r
13  * @date $Date$\r
14  */\r
15 \r
16 class PIOlog{\r
17         var $ENV, $logfile, $treefile, $porderfile; // Local Constant\r
18         var $logs, $trees, $LUT, $porder, $torder, $prepared; // Local Global\r
19         //var $memcached, $mid;\r
20 \r
21         /* private 設定 memcached 資料\r
22         function _memcacheSet($isAnalysis=true){\r
23                 if(!$this->_memcachedEstablish()) return false;\r
24                 $this->memcached->set('pmc'.$this->mid.'_isset', true);\r
25                 // 是否需要將每行資料分析為陣列\r
26                 $this->memcached->set('pmc'.$this->mid.'_logs', ($isAnalysis ? array_map(array($this, '_AnalysisLogs'), $this->logs) : $this->logs));\r
27                 $this->memcached->set('pmc'.$this->mid.'_trees', $this->trees);\r
28                 $this->memcached->set('pmc'.$this->mid.'_LUT', $this->LUT);\r
29                 $this->memcached->set('pmc'.$this->mid.'_porder', $this->porder);\r
30                 $this->memcached->set('pmc'.$this->mid.'_torder', $this->torder);\r
31         }\r
32 \r
33         /* private 取得 memcached 資料\r
34         function _memcacheGet(){\r
35                 if(!$this->_memcachedEstablish()) return false;\r
36                 if($this->memcached->get('pmc'.$this->mid.'_isset')){ // 有資料\r
37                         $this->logs = $this->memcached->get('pmc'.$this->mid.'_logs');\r
38                         $this->trees = $this->memcached->get('pmc'.$this->mid.'_trees');\r
39                         $this->LUT = $this->memcached->get('pmc'.$this->mid.'_LUT');\r
40                         $this->porder = $this->memcached->get('pmc'.$this->mid.'_porder');\r
41                         $this->torder = $this->memcached->get('pmc'.$this->mid.'_torder');\r
42                         return true;\r
43                 }else return false;\r
44         }\r
45 \r
46         /* private 建立 memcached 實體\r
47         function _memcachedEstablish(){\r
48                 if(!extension_loaded('memcache')) return ($this->memcached = false);\r
49                 if(is_null($this->memcached)){\r
50                         $this->memcached = new Memcache;\r
51                         if(!$this->memcached->pconnect('localhost')) return ($this->memcached = false);\r
52                         return true;\r
53                 }\r
54                 return ($this->memcached===false) ? false : true;\r
55         }*/\r
56 \r
57         function PIOlog($connstr='', $ENV){\r
58                 $this->ENV = $ENV;\r
59                 $this->logs = $this->trees = $this->LUT = $this->porder = $this->torder = array();\r
60                 $this->prepared = 0;\r
61                 //$this->mid = md5($_SERVER['SCRIPT_FILENAME']); // Unique ID\r
62                 //$this->memcached = false; // memcached object (null: use, false: don't use)\r
63 \r
64                 if($connstr) $this->dbConnect($connstr);\r
65         }\r
66 \r
67         /* private 把每一行 Log 解析轉換成陣列資料 */\r
68         function _AnalysisLogs($line){\r
69                 $tline = array();\r
70                 list($tline['no'], $tline['resto'], $tline['md5chksum'], $tline['category'], $tline['tim'], $tline['ext'], $tline['imgw'], $tline['imgh'], $tline['imgsize'], $tline['filename'], $tline['tw'], $tline['th'], $tline['pwd'], $tline['now'], $tline['name'], $tline['email'], $tline['sub'], $tline['com'], $tline['host'], $tline['status']) = explode(',', $line);\r
71                 return array_reverse($tline); // list()是由右至左代入的\r
72         }\r
73 \r
74         /* private 將回文放進陣列 */\r
75         function _includeReplies($posts){\r
76                 $torder_flip = array_flip($this->torder);\r
77                 foreach($posts as $post){\r
78                         if(array_key_exists($post, $torder_flip)){ // 討論串首篇\r
79                                 $posts = array_merge($posts, $this->trees[$post]);\r
80                         }\r
81                 }\r
82                 return array_merge(array(), array_unique($posts)); // 去除重複值\r
83         }\r
84 \r
85         /* private 取代 , 成為 &#44; 避免衝突 */\r
86         function _replaceComma($txt){\r
87                 return str_replace(',', '&#44;', $txt);\r
88         }\r
89 \r
90         /* private 由編號取出資料分析成陣列 */\r
91         function _ArrangeArrayStructure($line){\r
92                 $line = (array)$line; // 全部視為Arrays\r
93                 $posts = array();\r
94                 foreach($line as $i){\r
95                         if(!isset($this->LUT[$i])) continue;\r
96                         if(!is_array($this->logs[$this->LUT[$i]])){ // 進行分析轉換\r
97                                 $line = $this->logs[$this->LUT[$i]]; if($line=='') continue;\r
98                                 $this->logs[$this->LUT[$i]] = $this->_AnalysisLogs($line);\r
99                         }\r
100                         $posts[] = $this->logs[$this->LUT[$i]];\r
101                 }\r
102                 return $posts;\r
103         }\r
104 \r
105         /* PIO模組版本 */\r
106         function pioVersion(){\r
107                 return '0.6 (v20100404)';\r
108         }\r
109 \r
110         /* 處理連線字串/連接 */\r
111         function dbConnect($connStr){\r
112                 if(preg_match('/^log:\/\/(.*)\:(.*)\/$/i', $connStr, $linkinfos)){\r
113                         $this->logfile = $this->ENV['BOARD'].'/'.$linkinfos[1]; // 投稿文字記錄檔檔名\r
114                         $this->treefile = $this->ENV['BOARD'].'/'.$linkinfos[2]; // 樹狀結構記錄檔檔名\r
115                         $this->porderfile = $this->ENV['LUTCACHE']; // LUT索引查找表暫存檔案\r
116                 }\r
117         }\r
118 \r
119         /* 初始化 */\r
120         function dbInit(){\r
121                 $chkfile = array($this->logfile, $this->treefile, $this->porderfile);\r
122                 // 自動建置\r
123                 foreach($chkfile as $value){\r
124                         if(!is_file($value)){ // 檔案不存在\r
125                                 $fp = fopen($value, 'w');\r
126                                 stream_set_write_buffer($fp, 0);\r
127                                 if($value==$this->logfile) fwrite($fp, '0,0,,,0,,0,0,,,0,0,,08/11/08(土) 10:24:04,【スパーキー(④ ^ヮ^)】,,'.$this->ENV['NOTITLE'].','.$this->ENV['NOCOMMENT'].',,,'); // PIO Structure V4\r
128                                 if($value==$this->treefile) fwrite($fp, '0');\r
129                                 if($value==$this->porderfile) fwrite($fp, '0');\r
130                                 fclose($fp);\r
131                                 unset($fp);\r
132                                 @chmod($value, 0666);\r
133                         }\r
134                 }\r
135                 return true;\r
136         }\r
137 \r
138         /* 準備/讀入 */\r
139         function dbPrepare($reload=false, $transaction=true){\r
140                 if($this->prepared && !$reload) return true;\r
141                 if($reload && $this->prepared) $this->porder = $this->torder = $this->LUT = $this->logs = $this->trees = array();\r
142                 //if($this->_memcacheGet()){ $this->prepared = 1; return true; } // 如果 memcache 有快取則直接使用\r\r
143                 $this->logs = file($this->logfile); // Log每行原始資料\r
144                 if(!file_exists($this->porderfile)){ // LUT不在,重生成\r
145                         $lut = '';\r
146                         foreach($this->logs as $line){\r
147                                 if(!isset($line)) continue;\r
148                                 $tmp = explode(',', $line); $lut .= $tmp[0]."\r\n";\r
149                         }\r
150                         $fp = fopen($this->porderfile, 'w'); // LUT\r
151                         stream_set_write_buffer($fp, 0);\r
152                         flock($fp, LOCK_EX); // 鎖定檔案\r
153                         fwrite($fp, $lut);\r
154                         flock($fp, LOCK_UN); // 解鎖\r
155                         fclose($fp);\r
156                 }\r
157                 $this->porder = array_map('rtrim', file($this->porderfile)); // 文章編號陣列\r
158                 $this->LUT = array_flip($this->porder); // LUT索引查找表\r
159 \r
160                 $tree = array_map('rtrim', file($this->treefile));\r
161                 foreach($tree as $treeline){ // 解析樹狀結構製成索引\r
162                         if($treeline=='') continue;
163                         if($treeline==0) break;\r
164                         $tline = explode(',', $treeline);\r
165                         $this->torder[] = $tline[0]; // 討論串首篇編號陣列\r
166                         $this->trees[$tline[0]] = $tline; // 特定編號討論串完整結構陣列\r
167                 }\r
168                 //$this->_memcacheSet(); // 把目前資料設定到 memcached 內\r
169                 $this->prepared = 1;\r
170         }\r
171 \r
172         /* 提交/儲存 */\r
173         function dbCommit(){\r
174                 if(!$this->prepared) return false;\r
175 \r
176                 $log = $tree = $lut = '';\r
177                 $this->logs = array_merge(array(), $this->logs); // 更新logs鍵值\r
178                 $this->torder = array_merge(array(), $this->torder); // 更新torder鍵值\r
179                 $this->porder = $this->LUT = array(); // 重新生成索引\r
180 \r
181                 foreach($this->logs as $line){\r
182                         if(!isset($line)) continue;\r
183                         if(is_array($line)){ // 已被分析過\r
184                                 $log .= implode(',', $line).",\r\n";\r
185                                 $lut .= ($this->porder[] = $line['no'])."\r\n";\r
186                         }else{ // 尚未分析過\r
187                                 $log .= $line;\r
188                                 $tmp = explode(',', $line); $lut .= ($this->porder[] = $tmp[0])."\r\n";\r
189                         }\r
190                 }\r
191                 $this->LUT = array_flip($this->porder);\r
192                 $tcount = count($this->trees);\r
193                 for($tline = 0; $tline < $tcount; $tline++){\r
194                         $tree .= $this->isThread($this->torder[$tline]) ? implode(',', $this->trees[$this->torder[$tline]])."\r\n" : '';\r
195                 }\r
196                 //$this->_memcacheSet(false); // 更新快取 (不需要再分析)\r
197 \r
198                 $fp = fopen($this->logfile, 'w'); // Log\r
199                 stream_set_write_buffer($fp, 0);\r
200                 flock($fp, LOCK_EX); // 鎖定檔案\r
201                 fwrite($fp, $log);\r
202                 flock($fp, LOCK_UN); // 解鎖\r
203                 fclose($fp);\r
204 \r
205                 $fp = fopen($this->treefile, 'w'); // tree\r
206                 stream_set_write_buffer($fp, 0);\r
207                 flock($fp, LOCK_EX); // 鎖定檔案\r
208                 fwrite($fp, $tree);\r
209                 flock($fp, LOCK_UN); // 解鎖\r
210                 fclose($fp);\r
211 \r
212                 $fp = fopen($this->porderfile, 'w'); // LUT\r
213                 stream_set_write_buffer($fp, 0);\r
214                 flock($fp, LOCK_EX); // 鎖定檔案\r
215                 fwrite($fp, $lut);\r
216                 flock($fp, LOCK_UN); // 解鎖\r
217                 fclose($fp);\r
218         }\r
219 \r
220         /* 資料表維護 */\r
221         function dbMaintanence($action,$doit=false){\r
222                 switch($action) {\r
223                         case 'export':\r
224                                 if($doit){\r
225                                         $this->dbPrepare(false);\r
226                                         $gp = gzopen('piodata.log.gz', 'w9');\r
227                                         gzwrite($gp, $this->dbExport());\r
228                                         gzclose($gp);\r
229                                         return '<a href="piodata.log.gz">下載 piodata.log.gz 中介檔案</a>';\r
230                                 }else return true; // 支援匯出資料\r
231                                 break;\r
232                         case 'optimize':\r
233                         case 'check':\r
234                         case 'repair':\r
235                         default: return false; // 不支援\r
236                 }\r
237         }\r
238 \r
239         /* 匯入資料來源 */\r
240         function dbImport($data){\r
241                 $arrData = explode("\r\n", $data);\r
242                 $arrData_cnt = count($arrData) - 1; // 最後一個是空的\r
243                 $arrTree = array();\r
244                 $tree = $logs = $lut = '';\r
245                 for($i = 0; $i < $arrData_cnt; $i++){\r
246                         $line = explode(',', $arrData[$i], 4); // 切成四段\r
247                         $logs .= $line[0].','.$line[1].','.$line[3]."\r\n"; // 重建討論結構\r
248                         $lut .= $line[0]."\r\n"; // 重建 LUT 查找表結構\r
249                         if($line[1]==0){ // 首篇\r
250                                 if(!isset($arrTree[$line[0]])) $arrTree[$line[0]] = array($line[0]); // 僅自身一篇\r
251                                 else array_unshift($arrTree[$line[0]], $line[0]);\r
252                                 continue;\r
253                         }\r
254                         if(!isset($arrTree[$line[1]])) $arrTree[$line[1]] = array();\r
255                         array_unshift($arrTree[$line[1]], $line[0]);\r
256                 }\r
257                 foreach($arrTree as $t) $tree .= implode(',', $t)."\r\n"; // 重建樹狀結構\r
258                 $chkfile = array($this->logfile, $this->treefile, $this->porderfile);\r
259                 foreach($chkfile as $value){\r
260                         $fp = fopen($value, 'w');\r
261                         stream_set_write_buffer($fp, 0);\r
262                         if($value==$this->logfile) fwrite($fp, $logs);\r
263                         if($value==$this->treefile) fwrite($fp, $tree);\r
264                         if($value==$this->porderfile) fwrite($fp, $lut);\r
265                         fclose($fp);\r
266                         unset($fp);\r
267                         @chmod($value, 0666);\r
268                 }\r
269                 return true;\r
270         }\r
271 \r
272         /* 匯出資料來源 */\r
273         function dbExport(){\r
274                 if(!$this->prepared) $this->dbPrepare();\r
275                 $f = file($this->logfile);\r
276                 $data = '';\r
277                 foreach($f as $line){\r
278                         $line = explode(',', $line, 3); // 分成三段 (最後一段特別長)\r
279                         if($line[1]==0 && isset($this->trees[$line[0]])){\r
280                                 $lastno = array_pop($this->trees[$line[0]]);\r
281                                 $line2 = $this->fetchPosts($lastno);\r
282                                 $root = gmdate('Y-m-d H:i:s', substr($line2[0]['tim'], 0, 10)); // UTC 時間\r
283                                 unset($this->trees[$line[0]]); // 刪除表示已取過\r
284                         }else{\r
285                                 $root = '0';\r
286                         }\r
287                         $data .= $line[0].','.$line[1].','.$root.','.$line[2];\r
288                 }\r
289                 return $data;\r
290         }\r
291 \r
292         /* 文章數目 */\r
293         function postCount($resno=0){\r
294                 if(!$this->prepared) $this->dbPrepare();\r
295 \r
296                 return $resno ? ($this->isThread($resno) ? count(@$this->trees[$resno]) : 0) : count($this->porder);\r
297         }\r
298 \r
299         /* 討論串數目 */\r
300         function threadCount(){\r
301                 if(!$this->prepared) $this->dbPrepare();\r
302 \r
303                 return count($this->torder);\r
304         }\r
305 \r
306         /* 取得最後的文章編號 */\r
307         function getLastPostNo($state){\r
308                 if(!$this->prepared) $this->dbPrepare();\r
309 \r
310                 switch($state){\r
311                         case 'beforeCommit':\r
312                         case 'afterCommit':\r
313                                 return reset($this->porder);\r
314                 }\r
315         }\r
316 \r
317         /* 輸出文章清單 */\r
318         function fetchPostList($resno=0, $start=0, $amount=0){\r
319                 if(!$this->prepared) $this->dbPrepare();\r
320 \r
321                 $plist = array();\r
322                 if($resno){\r
323                         if($this->isThread($resno)){\r
324                                 if($start && $amount){\r
325                                         $plist = array_slice($this->trees[$resno], $start, $amount);\r
326                                         array_unshift($plist, $resno);\r
327                                 }\r
328                                 if(!$start && $amount) $plist = array_slice($this->trees[$resno], 0, $amount);\r
329                                 if(!$start && !$amount) $plist = $this->trees[$resno];\r
330                         }\r
331                 }else{\r
332                         $plist = $amount ? array_slice($this->porder, $start, $amount) : $this->porder;\r
333                 }\r
334                 return $plist;\r
335         }\r
336 \r
337         /* 輸出討論串清單 */\r
338         function fetchThreadList($start=0, $amount=0, $isDESC=false){\r
339                 if(!$this->prepared) $this->dbPrepare();\r
340                 $tmp_array = $this->torder;\r
341                 if($isDESC) rsort($tmp_array); // 按編號遞減排序 (預設為按最後更新時間排序)\r
342                 return $amount ? array_slice($tmp_array, $start, $amount) : $tmp_array;\r
343         }\r
344 \r
345         /* 輸出文章 */\r
346         function fetchPosts($postlist){\r
347                 if(!$this->prepared) $this->dbPrepare();\r
348 \r
349                 return $this->_ArrangeArrayStructure($postlist); // 輸出陣列結構\r
350         }\r
351 \r
352         /* 刪除舊附件 (輸出附件清單) */\r
353         function delOldAttachments($total_size, $storage_max, $warnOnly=true){\r
354                 global $FileIO;\r
355                 if(!$this->prepared) $this->dbPrepare();\r
356 \r
357                 $rpord = $this->porder; sort($rpord); // 由舊排到新 (小->大)\r
358                 $arr_warn = $arr_kill = array();\r
359                 foreach($rpord as $post){\r
360                         $logsarray = $this->_ArrangeArrayStructure($post); // 分析資料為陣列\r
361                         if($FileIO->imageExists($logsarray[0]['tim'].$logsarray[0]['ext'])){ $total_size -= $FileIO->getImageFilesize($logsarray[0]['tim'].$logsarray[0]['ext']) / 1024; $arr_kill[] = $post; $arr_warn[$post] = 1; } // 標記刪除\r
362                         if($FileIO->imageExists($logsarray[0]['tim'].'s.jpg')) $total_size -= $FileIO->getImageFilesize($logsarray[0]['tim'].'s.jpg') / 1024;\r
363                         if($total_size < $storage_max) break;\r
364                 }\r
365                 return $warnOnly ? $arr_warn : $this->removeAttachments($arr_kill);\r
366         }\r
367 \r
368         /* 刪除文章 */\r
369         function removePosts($posts){\r
370                 if(!$this->prepared) $this->dbPrepare();\r
371                 if(count($posts)==0) return array();\r
372 \r
373                 $posts = $this->_includeReplies($posts); // 包含所有回文\r
374                 $filelist = $this->removeAttachments($posts); // 欲刪除附件\r
375                 $torder_flip = array_flip($this->torder);\r
376                 $pcount = count($posts);\r
377                 $logsarray = $this->_ArrangeArrayStructure($posts); // 分析資料為陣列\r
378                 for($p = 0; $p < $pcount; $p++){\r
379                         if(!isset($logsarray[$p])) continue;\r
380                         if($logsarray[$p]['resto']==0){ // 討論串頭\r
381                                 unset($this->trees[$logsarray[$p]['no']]); // 刪除樹狀記錄\r
382                                 if(array_key_exists($logsarray[$p]['no'], $torder_flip)) unset($this->torder[$torder_flip[$logsarray[$p]['no']]]); // 從討論串首篇陣列中移除\r
383                         }else{\r
384                                 // 從樹狀檔刪除\r
385                                 if(array_key_exists($logsarray[$p]['resto'], $this->trees)){\r
386                                         $tr_flip = array_flip($this->trees[$logsarray[$p]['resto']]);\r
387                                         unset($this->trees[$logsarray[$p]['resto']][$tr_flip[$posts[$p]]]);\r
388                                 }\r
389                         }\r
390                         unset($this->logs[$this->LUT[$logsarray[$p]['no']]]);\r
391                         if(array_key_exists($logsarray[$p]['no'], $this->LUT)) unset($this->porder[$this->LUT[$logsarray[$p]['no']]]); // 從討論串編號陣列中移除\r
392                 }\r
393                 $this->LUT = array_flip($this->porder);\r
394                 return $filelist;\r
395         }\r
396 \r
397         /* 刪除附件 (輸出附件清單) */\r
398         function removeAttachments($posts){\r
399                 global $FileIO;\r
400                 if(!$this->prepared) $this->dbPrepare();\r
401                 if(count($posts)==0) return array();\r
402 \r
403                 $files = array();\r
404                 $logsarray = $this->_ArrangeArrayStructure($posts); // 分析資料為陣列\r
405                 $lcount = count($logsarray);\r
406                 for($i = 0; $i < $lcount; $i++){\r
407                         if($logsarray[$i]['ext']){\r
408                                 if($FileIO->imageExists($logsarray[$i]['tim'].$logsarray[$i]['ext'])) $files[] = $logsarray[$i]['tim'].$logsarray[$i]['ext'];\r
409                                 if($FileIO->imageExists($logsarray[$i]['tim'].'s.jpg')) $files[] = $logsarray[$i]['tim'].'s.jpg';\r
410                         }\r
411                 }\r
412                 return $files;\r
413         }\r
414 \r
415         /* 新增文章/討論串 */\r
416         function addPost($no, $resto, $md5chksum, $category, $tim, $ext, $imgw, $imgh, $imgsize, $filename, $tw, $th, $pwd, $now, $name, $email, $sub, $com, $host, $age=false, $status='') {\r
417                 if(!$this->prepared) $this->dbPrepare();
418 \r
419                 $tline = array($no, $resto, $md5chksum, $category, $tim, $ext, $imgw, $imgh, $imgsize, $filename, $tw, $th, $pwd, $now, $name, $email, $sub, $com, $host, $status);\r
420                 $tline = array_map(array($this, '_replaceComma'), $tline); // 將資料內的 , 轉換 (Only Log needed)\r
421                 array_unshift($this->logs, implode(',', $tline).",\r\n"); // 更新logs\r
422                 array_unshift($this->porder, $no); // 更新porder\r
423                 $this->LUT = array_flip($this->porder); // 更新LUT\r
424 \r
425                 // 更新torder及trees\r
426                 if($resto){\r
427                         $this->trees[$resto][] = $no;\r
428                         if($age){\r
429                                 $torder_flip = array_flip($this->torder);\r
430                                 unset($this->torder[$torder_flip[$resto]]); // 先刪除舊有位置\r
431                                 array_unshift($this->torder, $resto); // 再移到頂端\r
432                         }\r
433                 }else{\r
434                         $this->trees[$no][0] = $no;\r
435                         array_unshift($this->torder, $no);\r
436                 }\r
437         }\r
438 \r
439         /* 檢查是否連續投稿 */\r
440         function isSuccessivePost($lcount, $com, $timestamp, $pass, $passcookie, $host, $isupload){\r
441                 if(!$this->prepared) $this->dbPrepare();\r
442 \r
443                 $pcount = $this->postCount();\r
444                 $lcount = ($pcount > $lcount) ? $lcount : $pcount;\r
445                 for($i = 0; $i < $lcount; $i++){\r
446                         $logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列\r
447                         list($lcom, $lhost, $lpwd, $ltime) = array($logsarray[0]['com'], $logsarray[0]['host'], $logsarray[0]['pwd'], substr($logsarray[0]['tim'],0,-3));\r
448                         if($host==$lhost || $pass==$lpwd || $passcookie==$lpwd) $pchk = 1;\r
449                         else $pchk = 0;\r
450                         if($this->ENV['PERIOD.POST'] && $pchk){ // 密碼比對符合且開啟連續投稿時間限制\r
451                                 if($timestamp - $ltime < $this->ENV['PERIOD.POST']) return true; // 投稿時間相距太短\r
452                                 if($timestamp - $ltime < $this->ENV['PERIOD.IMAGEPOST'] && $isupload) return true; // 附加圖檔的投稿時間相距太短\r
453                                 if($com == $lcom && !$isupload) return true; // 內文一樣\r
454                         }\r
455                 }\r
456                 return false;\r
457         }\r
458 \r
459         /* 檢查是否重複貼圖 */\r
460         function isDuplicateAttachment($lcount, $md5hash){\r
461                 global $FileIO;\r
462 \r
463                 $pcount = $this->postCount();\r
464                 $lcount = ($pcount > $lcount) ? $lcount : $pcount;\r
465                 for($i = 0; $i < $lcount; $i++){\r
466                         $logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列\r
467                         if(!$logsarray[0]['md5chksum']) continue; // 無附加圖檔\r
468                         if($logsarray[0]['md5chksum']==$md5hash){\r
469                                 if($FileIO->imageExists($logsarray[0]['tim'].$logsarray[0]['ext'])) return true; // 存在MD5雜湊相同的檔案\r
470                         }\r
471                 }\r
472                 return false;\r
473         }\r
474 \r
475         /* 有此討論串? */\r
476         function isThread($no){\r
477                 if(!$this->prepared) $this->dbPrepare();\r
478 \r
479                 return isset($this->trees[$no]);\r
480         }\r
481 \r
482         /* 搜尋文章 */\r
483         function searchPost($keyword,$field,$method){\r
484                 if(!$this->prepared) $this->dbPrepare();\r
485 \r
486                 $foundPosts = array();\r
487                 $keyword_cnt = count($keyword);\r
488                 $pcount = $this->postCount();\r
489                 for($i = 0; $i < $pcount; $i++){\r
490                         $logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列\r
491                         $found = 0;\r
492                         foreach($keyword as $k){\r
493                                 if(strpos($logsarray[0][$field], $k)!==FALSE) $found++;\r
494                                 if($method=="OR" && $found) break;\r
495                         }\r
496                         if($method=="AND" && $found==$keyword_cnt) array_push($foundPosts, $logsarray[0]); // 全部都有找到 (AND交集搜尋)\r
497                         elseif($method=="OR" && $found) array_push($foundPosts, $logsarray[0]); // 有找到 (OR聯集搜尋)\r
498                 }\r
499                 return $foundPosts;\r
500         }\r
501 \r
502         /* 搜尋類別標籤 */\r
503         function searchCategory($category){\r
504                 if(!$this->prepared) $this->dbPrepare();\r
505 \r
506                 $category = strtolower($category);\r
507                 $foundPosts = array();\r
508                 $pcount = $this->postCount();\r
509                 for($i = 0; $i < $pcount; $i++){\r
510                         $logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列\r
511                         if(!($ary_category = $logsarray[0]['category'])) continue;\r
512                         if(strpos(strtolower($ary_category), '&#44;'.$category.'&#44;')!==false) array_push($foundPosts, $logsarray[0]['no']); // 找到標籤,加入名單\r
513                 }\r
514                 return $foundPosts;\r
515         }\r
516 \r
517         /* 取得文章屬性 */\r
518         function getPostStatus($status){\r
519                 return new FlagHelper($status); // 回傳 FlagHelper 物件\r
520         }\r
521 \r
522         /* 更新文章 */\r
523         function updatePost($no, $newValues){\r
524                 if(!$this->prepared) $this->dbPrepare();\r
525 \r
526                 $chk = array('resto', 'md5chksum', 'category', 'tim', 'ext', 'imgw', 'imgh', 'imgsize', 'filename', 'tw', 'th', 'pwd', 'now', 'name', 'email', 'sub', 'com', 'host', 'status');\r
527 \r
528                 $this->_ArrangeArrayStructure($no); // 將資料變成陣列\r
529                 foreach($chk as $c)\r
530                         if(isset($newValues[$c]))\r
531                                 $this->logs[$this->LUT[$no]][$c] = $this->_replaceComma($newValues[$c]); // 修改數值\r
532         }\r
533 \r
534         /* 設定文章屬性 */\r
535         function setPostStatus($no, $newStatus){\r
536                 $this->updatePost($no, array('status' => $newStatus));\r
537         }\r
538 }\r
539 ?>