pict-program-code.png URLが実際に存在するかどうかをPHPで調べる方法を紹介します。デッドリンクのチェックをおこなうプログラム等に利用できます。

fileの場合にはfile_exists()という関数があります。この関数でも良いのですが、PHP5で追加されたget_headers()関数を使う簡単な方法を紹介します。 注: get_headers()の前の@はエラー停止を抑制するためです。

  1. function url_exists($url) {
  2.   $header = @get_headers($url);
  3.   if(preg_match('#^HTTP/.*\s+[200|302]+\s#i', $header[0])) {
  4.     return true;
  5.   }
  6.   return false;
  7. }

$header[0]には以下のようなステータスコードが戻されます。
  • URLが存在する場合:'HTTP/1.1 200 OK'
  • URLが存在するがリダイレクトされている場合: 'HTTP/1.1 302 Found'
  • URLが存在しない場合:'HTTP/1.1 404 Not Found'
  • URLへアクセス許可されていない場合:'HTTP/1.1 403 Forbidden'
  • サーバーにアクセスできない場合:'HTTP/1.1 500 Internal Server Error'

他のケースもありますが、主としてこういったステータスが戻ります。上記のコードではステータスコードが200の場合と302の場合にのみOKとしています。302はファイルは存在するがそのファイル内部で別のURLにリダイレクトする場合に返されるコードです。以下のコードのような内容のファイルなどがこのステータスになります。ですのでこれも使用目的によってはURLが存在するとするべきかと思いOKとしました。
  1. <?php
  2.  header("Location: XXXXXX");
  3. ?>

ステータス 302 については以下のページに詳しい解説があります。
http://www.onflow.jp/cyano/archives/161

尚、get_headers()関数はPHP5で導入されましたので、PHP4では使用することができません。PHP4の環境の場合は同等の関数を自分で定義することで対応可能です。以下のPHPマニュアルのget_headers()のページのユーザからの投稿記事中にPHP4で使えるget_headers()と同等のコードサンプルがありますのでそちらを参照ください。
http://jp.php.net/manual/ja/function.get-headers.php

問題点

上記の関数でも、file_exists()でも、その他fopen()関数を内部的に使用するものは全て実行するPHP環境によってはうまく動作しない場合があります。PHPの初期設定ファイルであるphp.ini中で、allow_url_fopen=Offが設定されている場合は上記の関数は常にエラーとなりfalseを返してしまいます。

そこでテストした結果、以下の方法であれば、php.iniのallow_url_fopenがOffでも正しく動作することがわかりました。
  1. function url_exists($url) {
  2.   $a_url = parse_url($url);
  3.   if (!isset($a_url['port'])) $a_url['port'] = 80;
  4.   $errno = 0;
  5.   $errstr = '';
  6.   $timeout = 30;
  7.   if (isset($a_url['host']) && $a_url['host'] != gethostbyname($a_url['host'])) {
  8.     $fid = @fsockopen($a_url['host'], $a_url['port'], $errno, $errstr, $timeout);
  9.     if (!$fid) return false;
  10.     $page = isset($a_url['path']) ? $a_url['path'] : '';
  11.     $page .= isset($a_url['query']) ? '?' . $a_url['query'] : '';
  12.     fputs($fid, 'HEAD ' . $page . ' HTTP/1.0' . "\r\n" . 'HOST: '
  13.         . $a_url['host'] . "\r\n\r\n");
  14.     $head = fread($fid, 4096);
  15.     $head = substr($head, 0, strpos($head, 'Connection: close'));
  16.     fclose($fid);
  17.     if (preg_match('#^HTTP/.*\s+[200|302]+\s#i', $head)) {
  18.       return $pos !== false;
  19.     }
  20.   }
  21.   return false;
  22. }

ちなみに、この関数も以下のPHPマニュアルのget_headers()のページのユーザからの投稿記事中で見つけたものです。
http://jp.php.net/manual/ja/function.get-headers.php

また、参考までにですが、このPHPマニュアルのページに投稿されている以下のコードはホスト名のみでパスが付いていない場合はOKですが、それ以外は正しく動作しません
  1. function url_exists3($url) {
  2.   if(strstr($url, "http://")) $url = str_replace("http://", "", $url);
  3.   print "url [$url]\n";
  4.   $fp = @fsockopen($url, 80);
  5.   if($fp === false) return false;
  6.   return true;
  7. }

 

まだ投票はありません