2014年10月31日金曜日

PHPにおける文字のエスケープ処理

magic_quotes_gpcというオプションがphp.iniにある。このオプションが有効になっていると、データを渡していくことを繰り返すうちに文字列のエスケープの回数が増えるという問題が発生するそうだ(詳しくはこちらを参照)。オプションが有効化されていることで、エスケープされてしまう文字としては5c問題に該当する文字になる。そこで、一般的な処理としてどのようにエスケープ処理のコードを書くのかということについて示しておく。今回はDB(PostgreSQL)に入れるための文字列がフォームから入ってきた場合を想定しておく。

<?php
function convert_str_for_query($string){
  if( get_magic_quotes_gpc() ){ // magic_quotes_gpcオプションの状態を取得
    $string = stripslashes($string); // 全体に対しバックスラッシュを取り除いた文字列を返す
  }
  $string = htmlspecialchars($string); // <や&のような文字列をエスケープする
  // 上のコードは一般的には表示の際に対策を立てておくためと考えられる
  $string = pg_escape_string($string); // PostgreSQLのクエリとなる文字列をエスケープする
  // ちなみにMySQLであれば、$string = mysql_real_escape_string($string); とする
  return $string;
}
?>

2014年10月28日火曜日

PHPmemo_day15

PHPの基本に関するメモ。参考にしているのはPostgreSQL徹底入門の第5章です。

  • header関数を用いることで、charsetを指定することができる。htmlのheadタグ内に書かなくてもよい。またリダイレクト先を指定することもできる。書き方は以下のコード参照。
  • <?php
    header('Content-Type: text/html; charset=UTF-8'); // 文字コードの指定
    header('Location: リダイレクト先のURL'); // リダイレクト先の指定
    ?>
  • pg_query_params関数は文字列を挿入してqueryを実行する関数であるが、自動的にエスケープ処理を行うことができる。これによりSQLインジェクションを回避することができる。
  • PHPにもCやJavaと同じく三項演算子がある。
  • $_REQUESTというスーパーグローバル変数がある。これはphp.iniのvariables_orderの項目でリクエストの内容をマージしたものが含まれている。普通は$_GET、$_POST、$_COOKIEの優先順位となる。リクエストメソッドに関係なく、$_REQUESTから参照すればパラメータの値を受け取ることができる。
  • htmlのコードとしてPHPを入れる場合はinclude関数でファイルを読み込む。同じファイルで何度も読み込む場合があるのでinclude_once関数自体はあるが一般的ではない(何度も読み込むミスを防ぐ場合に使う)。関数を読み込みたい場合はrequire_once関数を使うという棲み分けがある。
  • 最近のwebシステムにおけるユーザ登録ではメールを仮登録し、仮登録した先のメールアドレスに本登録用のURLを送ることでメールアドレスが正しいものであるか検証することが多い。メールアドレスの形式が妥当なものであるか判断するのは厳しくしすぎないようにしておく(厳密にRFCに従っているか調べるということまではしないということ)。
  • パスワードを単純にsha1関数で暗号化したものを保存しておくのは、レインボーテーブル(暗号化前の文字列と暗号化後の文字列がペアになったテーブル)により、容易にパスワードが破られる可能性があるためよくない。そこで、ソルトと呼ばれる、ランダムな文字列を適当につけてから暗号化することにより、レインボーテーブルによるパスワード対策を行うことができる。
  • require関数はリダイレクトすることになるが、このときリダイレクト先はURLではなく相対パスにしなければならない。ファイルの位置関係で指定の仕方が変わってくることに注意。
  • HTTPの仕様として、データは文字列でやりとりされるので、0という値がブラウザから渡されるとそれは数値ではなく文字列となっている。ただしPHPでは配列の添字の数字は数値として扱われる。以下の例では10は数値として、testという文字列はtestという文字列で扱われることになる。
  • <?php
    <a href="?a[]=1&a[10]=5&a[test]=10">link</a>
    ?>
  • pg_affected_rows関数はDELETEをクエリで実行した際に影響を受けた行数を返す関数である。これを利用して削除処理の成功/不成功を判定することができる。
  • ini_get関数でクッキーを使っているか確認できる。session_get_cookie_params関数でクッキーのパラメータを取得し、session_name()関数の返り値となるセッション名に空文字を設定することでセッションIDをクリアできる。サーバ上のセッションファイルはsession_destroy関数を呼び出せばOK。
  • トランザクション機能を使うと、一連の処理におけるSQLの実行が途中で失敗しても、その処理の直前までもとに戻すことができる。
  • <?php
    pg_query(コネクション名, 'BEGIN');
    (ここに書いたSQL文がトランザクションになる)
    pg_query(コネクション名, 'COMMIT');
    ?>

2014年10月25日土曜日

PHPmemo_day14

PHPの基本に関するメモ。参考にしているのはPHPによるWebアプリケーションスーパーサンプル第2版の第13章(処理制御)です。Smartyなど素のPHPとは関係のないところは省略。

  • スクリプトを途中で終了する場合にはexit関数、die関数を使う。前者は終了時にエラーメッセージを出さないのに対し、後者は終了時にエラーメッセージが出てくるようになっている。関数の引数に文字列を与えることでエラー時のメッセージを制御することができる。また、関数を引数に与えることで異常時でも正常終了させるなどすることができる。
  • 処理を一時停止するにはsleep関数を使う。これは秒単位で停止する。マイクロ秒単位で制御する場合はusleep関数を用いる。
  • エラーメッセージの抑制は変数、関数の前に@マークをつけることでできる。例えば@num = 10/0;のようにすればnumに関するエラーメッセージは出なくなる。
  • error_reporting関数はエラーを表示させるレベルを設定できる。関数を呼出した後からレベルが変化する。
  • error_log関数はエラーメッセージを任意のファイルに送信することができる。Webサーバのログ、メールなどにも書き込める。
  • trigger_error関数を使うと、ユーザエラーを発生させることができる。このとき、引数にE_USER_ERRORを指定することになる。
  • header関数でHTTPヘッダを送信することが可能になる。例えばLocationヘッダを指定することで移動先のURLを指定できる。
  • exec関数を利用すると外部プログラムを実行する。exec関数は返り値を要求しない場合に使う。system関数は標準出力を返してくる。
  • 外部プログラムでシェルを使うときはescapeshellargでシェル引数として与える文字列をエスケープしなければならない。これはexec、system関数の両方に対して言える。

2014年10月23日木曜日

PHPmemo_day13

PHPの基本に関するメモ。参考にしているのはPHPによるWebアプリケーションスーパーサンプル第2版の第12章(セッション/クッキー)です。

セッションはデータの受け渡しに使われる、ユーザの情報を継続して管理するための仕組みである。

  • セッション情報が置かれるディレクトリはphp.iniのsession.save_pathに書かれている。
  • セッションの自動開始はphp.iniのsession.auto_startを参照する。1であれば自動で始まる。
  • セッションの開始をスクリプトで制御するにはスクリプトの最初の方で、session_start関数を呼び出せばよい。
  • セッション変数はスーパーグローバル変数で、$_SESSIONである。$_SESSION["name"]="abc";のように設定すればよい。これはPHP5以上であれば問題なく利用できる。
  • 設定されたセッション変数はunsetで削除できる。設定されているかどうかはisset関数で調べられる。
  • セッション変数の保存先はsession_save_path関数でスクリプト側から調べることができる。ディレクトリがないと保存できずエラーになるので、そのような問題が発生した場合の調査に用いる関数である。
  • session_id関数で現在のセッションIDを取得することができる。クッキーが使用できる場合はクッキーにセッションIDが保存される。クッキーが使えない環境では、ページの遷移の度にセッション変数を遷移先に渡さないとセッション情報が引き継げない。
  • セッションハイジャックを防ぐ方法としてsession_regenerare_id関数を使う方法がある。この関数はセッションIDを新しいものに置き換えることができる。最近のPHPではsession_regenerate_id(TRUE)とすることで、古いセッションデータが削除され、よりセキュアになる仕組みが提供されている。
  • クッキーはユーザ側でデータを保存するための仕組みである。ただし、悪意あるサイトなどにクッキーの情報を盗まれる危険性があるので、カード番号などの重要情報はクッキーに設定しないようにしておく必要がある。
  • クッキーにデータを設定するにはsetcookie関数を用いる。参照する場合は$_COOKIE["変数名"]とする。setcookie関数の引数には変数名、値、有効期限、パス、ドメイン、HTTPSが必要か否かを与えることができる。
  • クッキーのデータを削除するには、setcookie関数の引数に古い有効期限を与えればよい。それにより特定の変数名のデータを削除することができる。削除されたことを確認するにはisset関数の返り値がFALSEになっていることを調べればよい。

2014年10月22日水曜日

PHPmemo_day12

PHPの基本に関するメモ。参考にしているのはPHPによるWebアプリケーションスーパーサンプル第2版の第2章(フォーム)後半です。

  • SQLコマンドに入る文字列をエスケープするにはmysql_real_escape_string(MySQL)、pg_escape_string(PostgreSQL)などの関数を用いる。ただし、php.iniでmagic_quotes_gpcをOnにしている場合、一旦stripslashes関数で文字列のエスケープ処理を解除してからDB専用の関数でエスケープ処理を行うのが良い。
  • フォームから受け取った値を用いてSQLクエリを構成する場合、数値は数値に変換してから挿入する。つまりintval関数で数値に変換するか、sprintfを使って数値を受け取る場合は"%d"として受け取ることになる。
  • フォームから受け取ったデータをsystem関数、exec関数を用いてシェルに渡すことがある。そのまま渡すと意図しないコマンドを実行される可能性があるので、シェルのメタ文字をエスケープするにはescapeshellcmd関数を用いる。
  • 特殊文字をエスケープするにはhtmlspecialchars関数を用いる。ただし「'」をエスケープするには関数の引数にENT_QUOTESを指定する必要がある。また、「"」、「'」をエスケープしない場合はENT_QUOTESを引数に与えることになる。
  • htmlタグをフォームから受け取った内容から取り除くにはstrip_tags関数を用いる。悪意のあるコード対策で用いられる。
  • 一部のタグだけ置換したいという場合はstr_replaceで置き換える。置換前と置換後の文字列を配列で指定することで複数のタグを一括で置換することもできる。
  • リンクに漢字を含むものについてはurlencode関数でエンコードを行う。これにより%5Cのように%と16進数から構成される文字列に置き換えられる。これをもとに戻すにはurldecode関数を用いる。
  • textarea内で改行をしても、表示では改行が含まれないようになっている。改行して表示するためにはnl2br関数を用いる必要がある。
  • アクセスの種類を判別するにはif( $_SERVER["REQUEST_METHOD"] == "POST" ){ ... }などとする。
  • explode関数で区切り文字を指定して、1つの文字列を配列に分割・収納することができるようになる。
  • mktime関数は日付をタイムスタンプ形式に変換できる。この関数では3月32日を4月1日のように自動的に解釈するようになっている。
  • checkdate関数は指定した年月日がグレゴリオ暦と矛盾がないかを調べる関数である。
  • mb_detect_encoding関数は文字コードの検出に使われる。引数で文字コードの検出の優先順位をつけておくことになる。
  • 文字コードの変換はmb_convert_encoding関数を使う。
  • 「$$変数名」という表記法がある。
  • $a = "abc";
    $$a = "xyz";
    echo $abc; // xyzと表示される。
    

2014年10月21日火曜日

CentOS 6でホスト名を書き換える

サーバのホスト名を書き換えるということは、仮想環境を利用するとしばしば起こりうることである。たとえば、VMwareに含まれる仮想マシンをコピーするテンプレートを作成するという機能を利用すると、この作業が必要になる。それはコピーして作られたホストは名前が重複しないようにしなければならないためである。さて、実際にホスト名を書き換える作業する際には次のような手順を踏むことになる。

  1. # vi /etc/hosts (ホスト名を書き換える)
  2. # vi /etc/sysconfig/network (名前解決の優先順位を書き換える)
  3. # hostname 書き換えたホスト名
  4. ログアウトして再度ログインしなおす
  5. ホスト名が変わっていることを確認する

名前解決において、/etc/hostsとDNSサーバのどちらが優先されるかということについては/etc/nsswitch.confを参照する。order: files,dnsなどと書かれている行があるはずである。filesが/etc/hosts、dnsがDNSサーバを意味しており、この順が名前解決の順序となる。普通はfiles,dnsの順序になっているはずである。また/etc/hostsには初期設定では自身以外の記述はないので、他のサーバにアクセスしようとすると、名前解決のためにDNSサーバを見に行くということになる。この仕組みを知っておくと、DNSサーバを構築しなくても、ごく小さな名前解決ができ便利である。

2014年10月20日月曜日

PHPmemo_day11

PHPの基本に関するメモ。参考にしているのはPHPによるWebアプリケーションスーパーサンプル第2版の第11章(クラス)です。

  • クラスの中の変数にはpublic、private、protectedが用いられる。varはPHP5では使わない方が良い。
  • コンストラクタメソッドは__construct()になる。デストラクタメソッドは__destruct()になる。
  • オブジェクトの参照のコピーは$obj2 = $obj1;でできる。オブジェクトをコピーするには$obj2 = clone $obj1;とする。
  • クラス中の静的変数・静的メソッドにはstaticを使う。この辺はJavaと変わらない。
  • 抽象クラスにはabstractをつける。継承されないクラスにはfinalをつける。
  • 定義されていないクラス、メソッド、フィールドが呼び出された場合、それぞれ__autoload、__call、__getメソッドをクラス内に用意しておくと、それらが自動で呼び出されることになる。エラー処理で使える。
  • get_decleared_classes関数で定義済みのクラス名を取得できる。
  • stdClassという内部で定義されているクラスを使うのは控えること。
  • get_class_methodsでクラス内に定義されている関数一覧を取得できる。
  • class_exists関数でクラスが存在するか調べることができる。存在していればTRUE、存在していなければFALSE。
  • method_exists関数でクラスのオブジェクト内にあるメソッド名が存在するか調べることができる。存在していればTRUE、存在していなければFALSE。
  • クラスのプロパティ(変数)を取得するにはget_class_vars関数を用いる。ただし、PHP5のprivateフィールドについては取得することはできない。また、オブジェクトのプロパティを取得するにはget_object_vars関数を用いることになる。

2014年10月17日金曜日

PHPからPostgreSQLに接続してデータを取得する

今回はPHPからPostgreSQLに接続してデータを取得するコードを書いてみます。DBについては既にインストールやアカウントの設定が終わっているものと仮定します。問題を簡単にするため文字コードについては考慮しませんが、DBからページに表示する際に文字コードを変換する関数も書いてみることにします。

$server = "localhost"; // 接続先のホスト
$dbname = "db_sample"; // データベース名
$user = "i_am_an_user"; // ユーザ名
$password = "passwd"; // パスワード
$conndef = "host=".$server." dbname=".$dbname." user=".$user." password=".$password; // 接続定義
$conn = pg_connect($conndef) or die("connection error"); // 接続

// SQLの実行(適当な例です)
$query = "SELECT * FROM mytable WHERE id <= 10";
$result = pg_query($conn, $query) or die("SQL error");
// 結果から各種情報を取得する
$nrow = pg_num_rows($result); // 行数(件数)の取得
$nfield = pg_num_fields($result); // 列数の取得

// 表示
for( $i = 0 ; $i < $nfield ; $i++ ){
  echo pg_field_name($result, $i)." "; // タイトルの表示
}
echo "\n";
while ($row = pg_fetch_array($result)) { // 1要素ずつ取得する
  for( $i = 0; $i < $nfield ; $i++ ){
    // $row["id"]などとしてもデータを取得することができる。
    // その場合はpg_fetch_array($result, $i, PGSQL_ASSOC)として1要素を取得することになる。
    echo $row[$i]." "; // 順番に各内容を表示
  }
  echo "\n";
}

pg_close($conn); // DB操作が終わったら切断

// DBから取得した文字列に対して文字コードを変換するための関数
// DBの文字コード
$db_encode = "UTF-8";
// 表示の際に使う文字コード(文字コードはhtmlに指定されているはずである)
// もしかするとApacheなどWebサーバの方で強制的に指定されているかもしれないので
// そちらの設定も確認しておくこと。全部統一しておけば、とりあえず安心できるだろう。
$disp_encode = "UTF-8";
function convert_encoding($string, $from, $to){
  $detected_encode = mb_detect_encoding($string, $from.", ".$to);
  if( $detected_encode and $detected_encode != $to ){
    return mb_convert_encoding($string, $to, $detected_encode);
  }else{
    return $string;
  }
}

MySQLとPostgreSQLでは引数に与えるクエリと接続定義の順序が逆になっている点に注意。

2014年10月16日木曜日

PHPmemo_day10

PHPの基本に関するメモ。参考にしているのはPHPによるWebアプリケーションスーパーサンプル第2版の第10章(ディレクトリ)です。

  • mkdir(ディレクトリ名, パーミッション)で指定したディレクトリを作成できる。返り値はディレクトリ作成の成功(非0)、失敗(0)である。
  • is_dir(ディレクトリ名)でディレクトリの存在を確認する。
  • dirname(ファイルパス)でファイルのディレクトリが分かる。絶対パスを取得するにはrealpath関数を用いる。
  • diskfreespace(ディレクトリ名)でディレクトリが使用可能な容量を取得できる。
  • rmdir(ディレクトリ名)で指定したディレクトリを削除する。返り値はディレクトリ削除の成功(非0)、失敗(0)である。
  • dir(ディレクトリ名)でディレクトリをオブジェクトとして取得する。ディレクトリの中身はreadメソッドで順に取得できる。lsコマンドと同様に、ディレクトリが含まれる点に注意。参照が終わったらcloseメソッドを呼ぶこと。
  • PHP5ではscandir関数を用いることで、指定したディレクトリに以下にあるファイル一覧を配列で取得できる。

2014年10月10日金曜日

PHPmemo_day9

PHPの基本に関するメモ。参考にしているのはPHPによるWebアプリケーションスーパーサンプル第2版の第9章(ファイル)の後半です。

  • readfile(URL)でhtmlのページを読み込みできる。ページの埋め込みに使える。
  • get_mata_tags関数でhtmlからメタタグを読み込みできる。返り値は配列であり、descriptionやkeywordsといったインデックスでメタタグの内容を参照できる。
  • fgetssはhtmlタグを取り除いた形でhtmlを1行ずつ読み込む。trim関数を組み合わせることでインデントの空白も除去して使うことが多い。
  • 外部ファイルに定義した関数を呼び出すにはrequireかinclude関数を用いる。前者は読み込みファイルがないと致命的なエラーになるのに対し、後者は警告で済むという違いがある。
  • 一度だけファイルを読み込むのであれば、require_onceかinclude_once関数を用いる。
  • include関数の戻り値でファイルがインクルード済か調べられる。読み込み済なら1が返される。@include(~)とすればエラーメッセージを抑制できる。
  • 配列からカンマ区切りの文字列(CSVの1行)を生成するにはimplode(",", $array)が一般的である。
  • fgetcsv関数では区切り文字を指定することでCSVファイルの1行を配列で取得できる。あるいはlist関数で変数としてfgetcsv関数の返り値を受け取ることもある。
  • getimagesize関数の返り値で指定したファイルが画像であるか否か判定することができる。

2014年10月1日水曜日

PHPmemo_day8

PHPの基本に関するメモ。参考にしているのはPHPによるWebアプリケーションスーパーサンプル第2版の第8章(ファイル)の前半です。

  • die関数はexitと同じでスクリプトを終了するという意味がある。以下のコードはfopenに失敗した場合(ファイルが存在しない場合など)にdieが呼ばれる。
  • $file = fopen($target, "a") or die("ファイルオープンに失敗しました");
    flock($file, LOCK_EX); // ファイルの排他的ロック
    fputs($file, "abc</br>"); // 改行をつけてabcとファイルに書き込む
    flock($file, LOCK_UN); // unlock、ファイルの排他的ロックを解除する
    fclose($file);
  • 共有ロックはLOCK_SH、ロック中に他のflock()でロックさせたくなければLOCK_NBをflock関数の引数で指定する。
  • fopenはaで追記、wは上書きという意味になる。rは読み込み専用。xというオプションもある。+をつけることで読み書きの両方ができるようになる。
  • tmpfile()関数を用いると、一時ファイルを作成できる。ここにデータを書き込むなどすればよい。fclose()で一時ファイルを閉じると、ファイルは自動的に削除される。
  • file_put_contents関数を使うと、fopenでファイルを開かなくても指定したファイルに書き込みができる。fcloseなども不要になる。
  • ファイルの読み込み時はファイルが存在しないとエラーになるので、@をつけてエラーメッセージを非表示にする手法が用いられる。
  • // 読み込み(r)なので、@をつけないとエラーメッセージを抑制できない
    $file = @fopen($target, "r") or die("ファイルオープンに失敗しました");
    flock($file, LOCK_EX); // ファイルの排他的ロック
    while( feof($file) ){
     echo fgets($file, 1000)."</br>"; // 1行ずつ表示。1000と指定すると999バイトまで読み込める。
    }
    flock($file, LOCK_UN);
    flose($file);
  • fgetsに代わり、fgetcとすると1バイトずつ読み込むことになる。freadは指定したバイト数だけ読み込んでいく。
  • ファイル全体をまとめて読み込む場合には、fread関数とファイルサイズを調べるfilesize関数を組み合わせる方法がある。その他、file_get_contents関数、file関数がある。file_get_contents関数はファイルの中身を1つの文字列として取得する。file関数はファイルの内容を配列で入れる(改行なんかで1つの要素ごとに区切っているっぽい)。
  • ファイルの存在確認にはfile_exists関数(返り値はboolean)が使える。
  • rewindはファイルポインタを先頭にセットするための関数である。
  • fileatime関数はファイルの最終アクセス時刻を調べるための関数である。最終更新時刻はfilemtime関数で調べる。
  • ファイルの許可属性はfileperms関数で調べられる。ただし、出力結果はlinuxのパーミッションのように0~7の3桁の数字でもないので注意が要る。
  • 拡張子のチェックはpathinfo関数の返り値のインデックスextensionを使うとよい。pathinfoはパス名に関する情報を得るための関数である。インデックスbasenameにはファイル名(拡張子付)が含まれている。
  • ファイルの読み込みができるかについてはis_readable関数を、書き込み可能かどうかはis_writable関数を使う。実行可能かどうかについてはis_executable関数を使う。
  • copy(from, to)でファイルコピーができる。ファイルのコピー先は強制的に上書きされる。
  • rename(from, to)でファイル名の書き換えができる。変更先にファイルが既に存在しているとエラーになるので、そのような場合にエラーメッセージを抑制したい場合は@rename(from, to)とする。
  • unlink($file)で$fileを削除できる。削除対象がないとエラーになるので、file_existsで一度調べてから削除することもある。あるいは@unlink($file)のようにすることもある。

フォロワー

ページビューの合計