Archive for the 'PHP' Category

PHPで相対パスから絶対URL(URI)を作成する

金曜日, 1月 8th, 2010

HTMLページをパースしてURLを取り出す処理を書いていたのですが、ページ内のリンクなどが全部絶対URLで記述されていれば非常に楽なのですが、現実としてそうでもなく、ページによっては相対パスで書かれていたりして、正規表現で偏にリンクからURLを抜き出すだけではうまくできませんでした。

そこで少しググってみたら

PHPで相対パスから絶対URL(URI)を作成する|PHPプログラムメモ|プログラムメモ

という記事を発見!おぉ、これは便利!
と思って使わせてもらおうと思ったのですが、いくつかテストしてみて、相対パス処理で不備があるなーと思ったところがあったのでちょっと改良させてもらいました。

37~38行目は正直いらない気がしたのですが、 PHP 5.3 のCLIでWindows上でテストした際に、なぜか \/ (アルファベットのVではなく、\/ ) で出力されたのが気になったので、無駄かもしれないけどあえて記述。
あと $parse の初期化もここまでする必要ないけど、念のためNotice対策を…w

相対パスから絶対URLする関数


PHP:
  1. <?php
  2. /**
  3. * 相対パスから絶対URLを返します
  4. *
  5. * @param string $base ベースURL(絶対URL)
  6. * @param string $relational_path 相対パス
  7. * @return string 相対パスの絶対URL
  8. * @link http://blog.anoncom.net/2010/01/08/295.html
  9. * @link http://logic.stepserver.jp/data/archives/501.html
  10. */
  11. function createUri( $base = '', $relational_path = '' ) {
  12.    
  13.     $parse = array (
  14.         'scheme' => null,
  15.         'user' => null,
  16.         'pass' => null,
  17.         'host' => null,
  18.         'port' => null,
  19.         'path' => null,
  20.         'query' => null,
  21.         'fragment' => null,
  22.     );
  23.     $parse = parse_url ( $base );
  24.    
  25.     // パス末尾が / で終わるパターン
  26.     if ( strpos( $parse['path'], '/', ( strlen( $parse['path'] ) - 1 ) ) !== FALSE ) {
  27.         $parse['path'] .= '.'// ダミー挿入
  28.     }
  29.     if ( preg_match ( '#^https?\://#', $relational_path ) ) {
  30.         // 相対パスがURLで指定された場合
  31.         return $rel_path;
  32.     } elseif ( preg_match ( '#^/.*$#', $relational_path ) ) {
  33.         // ドキュメントルート指定
  34.         return $parse['scheme'] . '://' . $parse ['host'] . $relational_path;
  35.     } else {
  36.         // 相対パス処理
  37.         $basePath = explode ( '/', dirname ( $parse ['path'] ) );
  38.         $relPath = explode ( '/', $relational_path );
  39.         foreach ( $relPath as $relDirName ) {
  40.             if ($relDirName == '.') {
  41.                 array_shift ( $basePath );
  42.                 array_unshift ( $basePath, '' );
  43.             } elseif ($relDirName == '..') {
  44.                 array_pop ( $basePath );
  45.                 if ( count ( $basePath ) == 0 ) {
  46.                     $basePath = array( '' );
  47.                 }
  48.             } else {
  49.                 array_push ( $basePath, $relDirName );
  50.             }
  51.         }
  52.         $path = implode ( '/', $basePath );
  53.         return $parse ['scheme'] . '://' . $parse ['host'] . $path;
  54.     }
  55. }



テストケース

ベースURL
 http://example.com/path/to/url

相対パス
  • /
  • /index.html
  • /foo/bar/baz/
  • ./foo/bar/baz
  • ../../../foo/bar/baz/index.html
  • foo/bar/baz.html
  • foo/bar/baz/../index.html

テストコード


PHP:
  1. <?php
  2. $base = 'http://example.com/path/to/url';
  3.  
  4. $pathes[] = '/';
  5. $pathes[] = '/index.html';
  6. $pathes[] = '/foo/bar/baz/';
  7. $pathes[] = './foo/bar/baz';
  8. $pathes[] = '../../../foo/bar/baz/index.html';
  9. $pathes[] = 'foo/bar/baz.html';
  10. $pathes[] = 'foo/bar/baz/../index.html';
  11.  
  12. foreach ( $pathes as $path ) {
  13.     echo createUri($base, $path) . "\n";
  14. }



結果

こんな感じ

http://example.com/

http://example.com/index.html


http://example.com/foo/bar/baz/


http://example.com/path/to/foo/bar/baz


http://example.com/foo/bar/baz/index.html


http://example.com/path/to/foo/bar/baz.html


http://example.com/path/to/foo/bar/index.html


PreparedなINSERT文を簡単に作る方法

土曜日, 11月 7th, 2009

PHPでWebアプリケーションなどを開発していて、SQL文を発行する際に、セキュア面や利便性などから、ADODBPDOなどを用いて、Prepared Statementを使うSQLを書くこともあると思います。

その際、特にINSERT文などはカラムの数だけVALUESの中に ? が並ぶことになるかと思います。


SQL:
  1. -- 例:
  2. INSERT INTO
  3. `persons` (`id`, `name`, 'age`, `birthday`, `mailaddress`, `phone`, `zipcode`, `address`)
  4. VALUES (?, ?, ?, ?, ?, ?, ?, ?);



このとき、 ? がひたすら並んでいるだけとなると非常に見づらく、INSERTする情報が多くなってくると、指定したカラム数と VALUESの ? の数が一致せず、

Number of variables doesn't match number of parameters in prepared statement

といったエラーに遭遇したことが一度はあるかと思います。
特に、プログラムの改修をする際などは、カラム名だけ追加して、うっかり VALUESの ? だけ追加のし忘れなどをしてしまうことなんかも。。。

そこで、Prepared Statementを作る際のINSERT文のSQLを簡単で分かりやすくしたいと思います。


PHP:
  1. <?php
  2. $dsn = 'mysql:dbname=testdb;host=127.0.0.1';
  3. $user = 'dbuser';
  4. $password = 'dbpass';
  5.  
  6. try {
  7.     $db = new PDO($dsn, $user, $password);
  8.    
  9.     // INSET対象となるカラム名を指定
  10.     $columns = array(
  11.         'id',
  12.         'name',
  13.         'age',
  14.         'birthday',
  15.         'mailaddress',
  16.         'phone',
  17.         'zipcode',
  18.         'address',
  19.     );
  20.    
  21.     $binds = array(
  22.         1   => 'anon',
  23.         2   => 'anon',
  24.         3   => '25',
  25.         4   => '1984-05-02',
  26.         5   => 'root@example.com',
  27.         6   => '03-xxxx-xxxx',
  28.         7   => 'xxxxxx',
  29.         8   => 'Tokyo',
  30.     );
  31.    
  32.     $sql = 'INSERT INTO persons '
  33.          . implode(', ', $columns)
  34.          . ') VALUES ('
  35.          . implode(', ', array_fill(0, count($columns), '?'))
  36.          . ')'
  37.          ;
  38.    
  39.     /*
  40.     // またはテーブル名やカラム名を明示的にクオートする場合はこちら
  41.     $sql = 'INSERT INTO `persons` '
  42.          . '`' . implode('`, `', $columns) . '`'
  43.          . ') VALUES ('
  44.          . implode(', ', array_fill(0, count($columns), '?'))
  45.          ;
  46.     */
  47.    
  48.     $stmt = $db->prepare($sql);
  49.    
  50.     foreach($binds as $key => $value){
  51.         $stmt->bindValue($key, $value);
  52.     }
  53.    
  54.     return $stmt->execute();
  55.    
  56. } catch (Exception $e) {
  57.     error_log('[' . get_class($e) . '] ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
  58. }



要点は、 $columnsという配列にカラム名を配列で持たせ、Prepared INSERT文を発行する際に、implode()関数でそのカラム名をカンマ区切りで連結、VALUESの ? は array_fill()関数で、カラム名の配列の値の数だけ ? で埋めた配列を作成し、さらにそれをimplode()関数で連結していく。

というだけです。

バインドの個所は今回手抜きにしてしまいました。
本当はバインドも同じようにもう少し見直せばもっと分かりやすく、簡潔にできると思うのですが、
いい書き方が浮かびませんでした。。。

接続先のサーバが応答するか判別する

日曜日, 2月 22nd, 2009

PHPのプログラム中で、別のサーバに接続する際、タイムアウトを適切に設定していない場合や、データ取得時にタイムアウトを設定できない場合もあるかと思います。そんなときに、まず先に接続先が生存しているかを確認しておくと、エラー処理の判定もしやすくなると思います。

そこで、接続を開始する前に事前に接続先のサーバに接続を試行してみます。

※PHP5.0以前の環境ではこのまま実行すると、throwの部分で構文エラーになる可能性があります。その場合はバージョン判定部分とthrow new Exceptionの部分を取り除いてください。


PHP:
  1. /**
  2. * $hostname が接続先として接続応答出来る状態かを確認します。
  3. *
  4. * @access public static
  5. * @param string $hostname 接続先ホスト名またはIP
  6. * @param int $port 接続先ポート番号
  7. * @param int $timeout タイムアウトを指定する(秒)
  8. * @return boolean
  9. */
  10. function isAliveHost($hostname, $port = 80, $timeout = 5)
  11. {
  12.     if(empty($hostname)){
  13.         if(version_compare(PHP_VERSION, '5.0.0', '<')){
  14.             trigger_error('Hostname is empty.', E_USER_ERROR);
  15.         }else{
  16.             throw new Exception('Hostname is empty.');
  17.         }
  18.     }
  19.    
  20.     // port番号指定がint型以外、あるいはintの範囲外である場合はfalseを返します。
  21.     if(!is_int($port)){
  22.         if(version_compare(PHP_VERSION, '5.0.0', '<')){
  23.             trigger_error('Invalid port number: ' . $port, E_USER_ERROR);
  24.         }else{
  25.             throw new Exception('Invalid port number: ' . $port);
  26.         }
  27.     }
  28.    
  29.     // port番号が65535を超える場合は、不正な値とみなし、falseを返します。
  30.     if($port <0 || $port> 65535){
  31.         if(version_compare(PHP_VERSION, '5.0.0', '<')){
  32.             trigger_error('Invalid range of port number: ' . $port, E_USER_ERROR);    // for PHP4
  33.         }else{
  34.             throw new Exception('Invalid range of port number: ' . $port);
  35.         }
  36.     }
  37.    
  38.     $fp = fsockopen($hostname, $port, $errno, $errmsg, $timeout);
  39.     if(!$fp){
  40.         error_log('could not connect to ' . $hostname . ':' . $port . ', ' . $errmsg);
  41.         return false;
  42.     }
  43.     fclose($fp);
  44.    
  45.     return true;
  46.    
  47. }



使い方は以下の通り

(続きを読む...)

PHPでの簡易文字列の一致の判定

金曜日, 2月 20th, 2009

PHPで、ある文字列の中に、特定の文字列が含まれているかを判定する際、完全一致なら、比較演算子 === で比較すればすぐですが、それ以外となると通常の比較演算では処理できないので、ereg()関数やpreg()関数などで処理することも多いと思います。

ただ、前方一致や後方一致、部分一致を行いたい場合は、それだけに正規表現を使うのもなんだかコストが高い。もっとシンプルに実装できないものか・・・。

例えば、JavaやC#でいうところのStringクラスのstartsWith()やendsWith()のようなことが出来れば・・・

という訳で、PHPで前方一致、後方一致、部分一致の比較を簡単に行う関数を作ってみました。



PHP:
  1. /**
  2. * 前方一致
  3. * $haystackが$needleから始まるか判定します。
  4. * @param string $haystack
  5. * @param string $needle
  6. * @return boolean
  7. */
  8. function startsWith($haystack, $needle){
  9.     return strpos($haystack, $needle, 0) === 0;
  10. }
  11.  
  12. /**
  13. * 後方一致
  14. * $haystackが$needleで終わるか判定します。
  15. * @param string $haystack
  16. * @param string $needle
  17. * @return boolean
  18. */
  19. function endsWith($haystack, $needle){
  20.     $length = (strlen($haystack) - strlen($needle));
  21.     // 文字列長が足りていない場合はFALSEを返します。
  22.     if($length <0) return FALSE;
  23.     return strpos($haystack, $needle, $length) !== FALSE;
  24. }
  25.  
  26. /**
  27. * 部分一致
  28. * $haystackの中に$needleが含まれているか判定します。
  29. * @param string $haystack
  30. * @param string $needle
  31. * @return boolean
  32. */
  33. function matchesIn($haystack, $needle){
  34.     return strpos($haystack, $needle) !== FALSE;
  35. }



上記コードの検証結果は以下を参照

(続きを読む...)

Get Adobe Flash playerPlugin by wpburn.com wordpress themes