PageRenderTime 57ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/wiki/replaceimageurlctl.class.php

http://github.com/unpush/p2-php
PHP | 389 lines | 281 code | 38 blank | 70 comment | 74 complexity | e1b5316469fb814a50faad7fc576571f MD5 | raw file
  1. <?php
  2. /*
  3. ReplaceImageURL(url) メイン関数
  4. save(array) データを保存
  5. load() データを読み込んで返す(自動的に実行される)
  6. clear() データを削除
  7. autoLoad() loadされていなければ実行
  8. */
  9. require_once P2_LIB_DIR . '/FileCtl.php';
  10. class ReplaceImageURLCtl
  11. {
  12. var $filename = "p2_replace_imageurl.txt";
  13. var $data = array();
  14. var $isLoaded = false;
  15. // replaceの結果を外部ファイルにキャッシュする
  16. // とりあえず外部リクエストの発生する$EXTRACT入りの場合のみ対象
  17. // 500系のエラーだった場合は、キャッシュしない
  18. var $cacheFilename = "p2_replace_imageurl_cache.txt";
  19. var $cacheData = array();
  20. var $cacheIsLoaded = false;
  21. // 全エラーをキャッシュして無視する(永続化はしないので今回リクエストのみ)
  22. var $extractErrors = array();
  23. // 全replaceImageURLをキャッシュする(永続化はしないので今回リクエストのみ)
  24. var $onlineCache = array();
  25. function clear() {
  26. global $_conf;
  27. $path = $_conf['pref_dir'] . '/' . $this->filename;
  28. return @unlink($path);
  29. }
  30. function autoLoad() {
  31. if (!$this->isLoaded) $this->load();
  32. if (!$this->cacheIsLoaded) $this->load_cache();
  33. }
  34. function load() {
  35. global $_conf;
  36. $lines = array();
  37. $path = $_conf['pref_dir'].'/'.$this->filename;
  38. if ($lines = @file($path)) {
  39. foreach ($lines as $l) {
  40. if (substr($l, 0, 1) === ';' || substr($l, 0, 1) === "'" ||
  41. substr($l, 0, 1) === '#' || substr($l, 0, 2) === '//') {
  42. //"#" ";" "'" "//"から始まる行はコメント
  43. continue;
  44. }
  45. $lar = explode("\t", trim($l));
  46. if (strlen($lar[0]) == 0 || count($lar) < 2) {
  47. continue;
  48. }
  49. $ar = array(
  50. 'match' => $lar[0], // 対象文字列
  51. 'replace' => $lar[1], // 置換文字列
  52. 'referer' => $lar[2], // リファラ
  53. 'extract' => $lar[3], // EXTRACT
  54. 'source' => $lar[4], // EXTRACT正規表現
  55. 'recheck' => $lar[5], // EXTRACTしたページを次回もチェックしたいか
  56. 'ident' => $lar[6], // 置換結果の画像URLに対する正規
  57. // 表現。指定されている場合はこれ
  58. // でマッチした文字列で前回キャッ
  59. // シュと比較し、同一であれば同じ
  60. // 画像と見做す
  61. );
  62. $this->data[] = $ar;
  63. }
  64. }
  65. $this->isLoaded = true;
  66. return $this->data;
  67. }
  68. /**
  69. * saveReplaceImageURL
  70. * $data[$i]['match'] Match
  71. * $data[$i]['replace'] Replace
  72. * $data[$i]['del'] 削除
  73. */
  74. function save($data)
  75. {
  76. global $_conf;
  77. $path = $_conf['pref_dir'] . '/' . $this->filename;
  78. $newdata = '';
  79. foreach ($data as $na_info) {
  80. $a[0] = strtr(trim($na_info['match'] , "\t\r\n"), "\t\r\n", " ");
  81. $a[1] = strtr(trim($na_info['replace'], "\t\r\n"), "\t\r\n", " ");
  82. $a[2] = strtr(trim($na_info['referer'], "\t\r\n"), "\t\r\n", " ");
  83. $a[3] = strtr(trim($na_info['extract'], "\t\r\n"), "\t\r\n", " ");
  84. $a[4] = strtr(trim($na_info['source'] , "\t\r\n"), "\t\r\n", " ");
  85. $a[5] = strtr(trim($na_info['recheck'] ,"\t\r\n"), "\t\r\n", " ");
  86. $a[6] = strtr(trim($na_info['ident'] ,"\t\r\n"), "\t\r\n", " ");
  87. if ($na_info['del'] || ($a[0] === '' || $a[1] === '')) {
  88. continue;
  89. }
  90. $newdata .= implode("\t", $a) . "\n";
  91. }
  92. return FileCtl::file_write_contents($path, $newdata);
  93. }
  94. function load_cache() {
  95. global $_conf;
  96. $lines = array();
  97. $path = $_conf['pref_dir'].'/'.$this->cacheFilename;
  98. FileCtl::make_datafile($path);
  99. if ($lines = @file($path)) {
  100. foreach ($lines as $l) {
  101. list($key, $data) = explode("\t", trim($l));
  102. if (strlen($key) == 0 || strlen($data) == 0) continue;
  103. $this->cacheData[$key] = unserialize($data);
  104. }
  105. }
  106. $this->cacheIsLoaded = true;
  107. return $this->cacheData;
  108. }
  109. function storeCache($key, $data) {
  110. global $_conf;
  111. if ($this->cacheData[$key]) {
  112. // overwrite the cache file
  113. $this->cacheData[$key] = $data;
  114. $body = '';
  115. foreach ($this->cacheData as $_k => $_v) {
  116. $body .= implode("\t", array($_k,
  117. serialize(ReplaceImageURLCtl::sanitizeForCache($_v)))
  118. ) . "\n";
  119. }
  120. return FileCtl::file_write_contents($_conf['pref_dir'] . '/'
  121. . $this->cacheFilename, $body);
  122. } else {
  123. // append to the cache file
  124. $this->cacheData[$key] = $data;
  125. return FileCtl::file_write_contents(
  126. $_conf['pref_dir'] . '/' . $this->cacheFilename,
  127. implode("\t", array($key,
  128. serialize(ReplaceImageURLCtl::sanitizeForCache($data)))
  129. ) . "\n",
  130. FILE_APPEND
  131. );
  132. }
  133. }
  134. static function sanitizeForCache($data) {
  135. if (is_array($data)) {
  136. foreach(array_keys($data) as $k) {
  137. $data[$k] = ReplaceImageURLCtl::sanitizeForCache($data[$k]);
  138. }
  139. return $data;
  140. } else {
  141. return str_replace(array("\t", "\r", "\n"), '', $data);
  142. }
  143. }
  144. /**
  145. * replaceImageURL
  146. * リンクプラグインを実行
  147. * return array
  148. * $ret[$i]['url'] $i番目のURL
  149. * $ret[$i]['referer'] $i番目のリファラ
  150. */
  151. function replaceImageURL($url) {
  152. global $_conf;
  153. // http://janestyle.s11.xrea.com/help/first/ImageViewURLReplace.html
  154. $this->autoLoad();
  155. $src = FALSE;
  156. if (array_key_exists($url, $this->onlineCache)) {
  157. return $this->onlineCache[$url];
  158. }
  159. if ($this->cacheData[$url]) {
  160. if ($_conf['wiki.replaceimageurl.extract_cache'] == 1) {
  161. // キャッシュがあればそれを返す
  162. return $this->_reply($url, $this->cacheData[$url]['data']);
  163. }
  164. if ($this->cacheData[$url]['lost']) {
  165. // ページが消えている場合キャッシュを返す
  166. return $this->_reply($url, $this->cacheData[$url]['data']);
  167. }
  168. if ($_conf['wiki.replaceimageurl.extract_cache'] == 3) {
  169. // 前回キャッシュで結果が得られてなければやめ
  170. if (array_key_exists('data', $this->cacheData[$url]) && is_array($this->cacheData[$url]['data'])
  171. && count($this->cacheData[$url]['data']) == 0) {
  172. return $this->_reply($url, $this->cacheData[$url]['data']);
  173. }
  174. }
  175. }
  176. foreach ($this->data as $v) {
  177. if (preg_match('{^'.$v['match'].'$}', $url)) {
  178. $match = $v['match'];
  179. $replace = str_replace('$&', '$0', $v['replace']);
  180. $referer = str_replace('$&', '$0', $v['referer']);
  181. // 第一置換(Match→Replace, Match→Referer)
  182. $replace = @preg_replace ('{'.$match.'}', $replace, $url);
  183. $referer = @preg_replace ('{'.$match.'}', $referer, $url);
  184. // $EXTRACTがある場合
  185. // 注:$COOKIE, $COOKIE={URL}, $EXTRACT={URL}には未対応
  186. // $EXTRACT={URL}の実装は容易
  187. if (strstr($v['extract'], '$EXTRACT')){
  188. if ($_conf['wiki.replaceimageurl.extract_cache'] == 2) {
  189. if ($this->cacheData[$url] && !$v['recheck']) {
  190. return $this->_reply($url, $this->cacheData[$url]['data']);
  191. }
  192. }
  193. $source = $v['source'];
  194. $return = $this->extractPage($url, $match, $replace, $referer, $source, $v['ident']);
  195. } else {
  196. $return[0]['url'] = $replace;
  197. $return[0]['referer'] = $referer;
  198. }
  199. break;
  200. }
  201. }
  202. /* plugin_imageCache2で処理させるためコメントアウト
  203. // ヒットしなかった場合
  204. if (!$return[0]) {
  205. // 画像っぽいURLの場合
  206. if (preg_match('{^https?://.+?\\.(jpe?g|gif|png)$}i', $url)) {
  207. $return[0]['url'] = $url;
  208. $return[0]['referer'] = '';
  209. }
  210. }
  211. */
  212. return $this->_reply($url, $return);
  213. }
  214. function _reply($url, $data) {
  215. $this->onlineCache[$url] = $data;
  216. return $data;
  217. }
  218. function extractPage($url, $match, $replace, $referer, $source, $ident=null) {
  219. global $_conf;
  220. $ret = array();
  221. $source = @preg_replace ('{'.$match.'}', $source, $url);
  222. $get_url = $referer;
  223. if ($this->extractErrors[$get_url]) {
  224. // 今回リクエストでエラーだった場合
  225. return ($this->cacheData[$url] && $this->cacheData[$url]['data'])
  226. ? $this->cacheData[$url]['data'] : $ret;
  227. }
  228. if (!class_exists('HTTP_Request', false)) {
  229. require 'HTTP/Request.php';
  230. }
  231. $params = array();
  232. $params['timeout'] = $_conf['http_conn_timeout'];
  233. $params['readTimeout'] = array($_conf['http_read_timeout'], 0);
  234. if ($_conf['proxy_use']) {
  235. $params['proxy_host'] = $_conf['proxy_host'];
  236. $params['proxy_port'] = $_conf['proxy_port'];
  237. }
  238. $req = new HTTP_Request($get_url, $params);
  239. if ($this->cacheData[$url] && $this->cacheData[$url]['responseHeaders']
  240. && $this->cacheData[$url]['responseHeaders']['last-modified']
  241. && strlen($this->cacheData[$url]['responseHeaders']['last-modified'])) {
  242. $req->addHeader("If-Modified-Since",
  243. $this->cacheData[$url]['responseHeaders']['last-modified']);
  244. }
  245. $req->addHeader('User-Agent',
  246. (!empty($_conf['expack.user_agent'])) ? $_conf['expack.user_agent']
  247. : $_SERVER['HTTP_USER_AGENT']);
  248. $response = $req->sendRequest();
  249. $code = $req->getResponseCode();
  250. if (PEAR::isError($response) || ($code != 200 && $code != 206 && $code != 304)) {
  251. $errmsg = PEAR::isError($response) ? $response->getMessage() : $code;
  252. // 今回リクエストでのエラーをオンラインキャッシュ
  253. $this->extractErrors[$get_url] = $errmsg;
  254. // サーバエラー以外なら永続キャッシュに保存
  255. if ($code && $code < 500) {
  256. // ページが消えている場合
  257. if ($this->_checkLost($url, $ret)) {
  258. return $this->cacheData[$url]['data'];
  259. }
  260. $this->storeCache($url, array('code' => $code,
  261. 'errmsg' => $errmsg,
  262. 'responseHeaders' => $req->getResponseHeader(),
  263. 'data' => $ret));
  264. }
  265. return ($this->cacheData[$url] && $this->cacheData[$url]['data'])
  266. ? $this->cacheData[$url]['data'] : $ret;
  267. }
  268. if ($code == 304 && $this->cacheData[$url]) {
  269. return $this->cacheData[$url]['data'];
  270. }
  271. $body = $req->getResponseBody();
  272. preg_match_all('{' . $source . '}i', $body, $extracted, PREG_SET_ORDER);
  273. foreach ($extracted as $i => $extract) {
  274. $_url = $replace; $_referer = $referer;
  275. foreach ($extract as $j => $part) {
  276. if ($j < 1) continue;
  277. $_url = str_replace('$EXTRACT'.$j, $part, $_url);
  278. $_referer = str_replace('$EXTRACT'.$j, $part, $_referer);
  279. }
  280. if ($extract[1]) {
  281. $_url = str_replace('$EXTRACT', $part, $_url);
  282. $_referer = str_replace('$EXTRACT', $part, $_referer);
  283. }
  284. $ret[$i]['url'] = $_url;
  285. $ret[$i]['referer'] = $_referer;
  286. }
  287. // ページが消えている場合
  288. if ($this->_checkLost($url, $ret)) {
  289. return $this->cacheData[$url]['data'];
  290. }
  291. if ($ident && $this->cacheData[$url] && $this->cacheData[$url]['data']) {
  292. $ret = self::_identByCacheData($ret, $this->cacheData[$url]['data'], $ident);
  293. }
  294. // 結果を永続キャッシュに保存
  295. $this->storeCache($url, array('code' => $code,
  296. 'responseHeaders' => $req->getResponseHeader(),
  297. 'data' => $ret));
  298. return $ret;
  299. }
  300. function _checkLost($url, $data) {
  301. if (count($data) == 0 && $this->cacheData[$url] &&
  302. $this->cacheData[$url]['data'] &&
  303. count($this->cacheData[$url]['data']) > 0) {
  304. $rec = $this->cacheData[$url];
  305. $rec['lost'] = true;
  306. $this->storeCache($url, $rec);
  307. return true;
  308. }
  309. return false;
  310. }
  311. /**
  312. * 前回キャッシュの内容に今回取得の画像URLがあるかを
  313. * 指定の正規表現で探し、あった場合はキャッシュのものを
  314. * 使用するように置き換える.
  315. *
  316. * 画像URLに認証用クエリなどが付いている、
  317. * ファイル名に規則的にセッション文字列が付く、
  318. * などの場合でも同じ画像を取りにいかないようにしたいため.
  319. */
  320. static function _identByCacheData($data, $cache, $identRegex) {
  321. $ret = $data;
  322. foreach ($ret as &$d) {
  323. $ident_match = array();
  324. if (!preg_match('{^'.$identRegex.'}', $d['url'], $ident_match))
  325. {
  326. continue;
  327. }
  328. // マッチした後方参照があるならそれだけ比較したいので
  329. // マッチ全体[0]を塗りつぶし
  330. if (count($ident_match) > 1) $ident_match[0] = '';
  331. foreach ($cache as $c) {
  332. $ident_cache_match = array();
  333. if (!preg_match('{^'.$identRegex.'}', $c['url'],
  334. $ident_cache_match))
  335. {
  336. continue;
  337. }
  338. // マッチした後方参照があるならそれだけ比較したいので
  339. // マッチ全体[0]を塗りつぶし
  340. if (count($ident_cache_match) > 1) $ident_cache_match[0] = '';
  341. if ($ident_match === $ident_cache_match) {
  342. // キャッシュデータを使用する
  343. $d = $c;
  344. break;
  345. }
  346. }
  347. }
  348. unset($d);
  349. return $ret;
  350. }
  351. }