[PHP] PHPスクリプトから include_path を追加する方法

PHPプログラムからrequire/includeの検索パス(include_path)を上書き、追加するには、set_include_path()を使います。

例えば、ディレクトリ “/path/to/include/” をrequireの検索対象に追加する場合は以下のようにします。

<?php
set_include_path(get_include_path().PATH_SEPARATOR."/path/to/include/");

または ini_set()を使う方法もあります。

<?php
ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . '/usr/lib/pear');

get_include_path() や ini_get(‘include_path’) は、今のinclude_path 設定を取得している関数呼び出しです。
set_include_path() や ini_set() は既存設定を(追記ではなく)上書きするため、追記にするために既存設定を取得して、新しい設定をつなげて足して、追加としています。

PATH_SEPARATOR はPHPの定義済の定数で、include_pathでパスを繋げるための文字が入ります。
(Windowsならセミコロン、Mac OSやLinuxならコロンが入ります)

table から div へ

  .tr{ clear:left; }
  .td{ float:left; }

と指定すれば、

 <div class=”tr”>
     <div class=”td”>foo</td>
     <div class=”td”>boo</td>
 </div>

のように簡易的に列揃えの疑似テーブル構造ができます(行揃えはなし)。
過渡期の置き換え作業の際はこのように td, tr クラスを作るのもいいかもしれません。
 
参考:
CSS による段組(マルチカラム)レイアウト講座
CSSリファレンス(実験室:P)
テーブルを使わずにCSSでいこう

Emacs 編集基本

– カーソル上の文字を削除する。

Ctrl + d
または
delete

 Backspace も使用可能です。このあたりは、説明の必要はないでしょう。
 
– カーソルから行末まで削除する。

Ctrl + k

 カーソルが行末にある時は改行を削除します。
 
– 複数行を削除する。

削除開始位置で
Ctrl + Space
として(マーク)、削除終了位置で
Ctrl + w

 
– コピー(キルリングに保存)する。

コピー開始位置で
Ctrl + Space
として(マーク)、コピー終了位置で
Alt + w

vi でいうところの y キーと同じようなものですね。
 
– 最後に削除(あるいはキルリングに保存)したものを張り付ける(ヤンクする)。

Ctrl + y

 Emacs では vi 同様、削除 = カット(切り取り)となっています。
 
– 矩形削除する。

Ctrl + Space
Ctrl + x r k

ここで削除したものはコピー状態にはなりません。
 
– アンドゥ

Ctrl + –
または
Ctrl
または
Ctrl + x u

Undo, Redo の切替えには

Ctrl + g

を使います。
 
参考:
With Emacs Next – 5.4 基本的な編集操作
Emacs tips

ARP Poisoning してみる

どういうものかはあえて触れず、dsniff 付属の arpspool を使う手順だけメモしておきます。
下手をすると、自分がいる LAN 全体の通信を止めてしまう危険もあるものです。

(Linux + apt)
# apt-get dsniff
(FreeBSD)
# portinstall -r security/dsniff

ソースからビルドする場合は公式サイトからダウンロードします。
 
OS や FW の設定で、パケットの転送を許可しておく必要があります。

[Linux]
# sysctl -w net.ipv4.ip_forward = 1

転送を完全に許可するようにした上で、次のコマンドを打てば完了です。

arpspool < ゲートウェイのIPアドレス>

もし転送が禁止になっている状態でこれを実行すると、当然ですが指定のゲートウェイを通る新規の通信が全てつながらなくなります。
 
なお、何か通信に問題が発生した時に気づいてプログラムを停止しても, ゲートウェイの ARP テーブルが更新されるまでの数秒はつながらないままになります。
参考:
dsniffFAQ
Dumnetと遊ぼう
eringe – Statically Compiled ARP Poisoning Tool

PHP で MVC モデリングの Hello world

近年は MVC というと PHP の Mojavi とか Java の Struts 等の MVC フレームワークが取りざたされます。しかし MVC フレームワークと聞くと何か難しい、MVC も概念は分かるけど使うのは難しい、という人もいるんじゃないでしょうか。
 
というわけで、フレームワークを一切使わず、MVC モデルの Hello, world 的スクリプトを PHP で書いてみました。
 
名前を入力してその人に挨拶する、次のようなコードがあります。これを MVC の構造に変えてみましょう。

nameform.php:
<html>
<head><title>名前入力</title></head>
<body>
   <form action=”” method=”POST”>
       名前:<input type=”text” name=”name”><br>
       <input type=”submit” value=”挨拶”><br>
   </form>
   <?php
      if(!empty($_POST[‘name’])){
          $name = htmlspecialchars($_POST[‘name’]);
          echo “こんにちは、$name!!”;
      }
   ?>
</body>
</html>

これに簡単な MVC モデルを適用すると、次のようになります。

nameform.php:
<?php
    // 挨拶プログラム(Controller)
    
    // 機能部分を読み込み
    require_once ‘include/greeting.inc’;
    // 挨拶を実行し、出力用に特殊文字をエスケープする。
    $result = htmlspecialchars( greet() );
    // 結果を表示。
    require_once ‘template/nameform.html’; // View を呼び出し。
?>

include/greeting.inc:
<?php
  //
  // 挨拶プログラムの Model (ドメイン固有)
  //
  
  require_once dirname(__file__).’/request.inc’; // パラメータ取得用
  /**
   * 名前を取得し、挨拶する。
   * @return string 挨拶の言葉。処理できなかった場合空文字
   * @access public
   */
  function greet(){
      $name = get_parameter(‘name’);
      if($name){
          return greet_to($name);
      } else {
          return;
      }
  }
  
  /**
   * 指定した相手に挨拶する。
   * @param string $name 相手
   * @param string 挨拶の言葉
   * @access private
   */
  function greet_to($name){
      echo “こんにちは、$name!!”;
  }
?>

include/request.inc
<?php
//
// パラメータ操作用ユーティリティファイル (非ドメイン固有 Model)
//
/**
 * 指定した名前を持つリクエストパラメータを取得する。
 * 存在しない場合は空文字を返す。
 * @access public
 * @param string $key 取得したいパラメータの識別名
 * @return string パラメータの中身。存在しない場合空文字
 */
function get_parameter($key){
    return empty($_POST[$key])? : $_POST[‘key’];
}
?>

template/nameform.html:
<!– 名前入力フォーム(view) –>
<html>
<head><title>名前入力</title></head>
<body>
    <form action=”” method=”post”>
        名前:<input type=”text” name=”name”><br>
        <input type=”submit” value=”挨拶”><br>
    </form>
    <!– 処理結果 –>
    $result
    ?>
</body>
</html>

どうでしょう?
PHP のコード(モデル、コントローラ)と HTML デザイン(ビュー)がしっかり分離できているのが確認できると思います。また、メインルーチン(コントローラ)と関数(モデル)が別ファイルに分けられていて、関数はさらに汎用的なもの(共通モデル)と挨拶プログラム固有と思われるもの(ドメイン固有モデル)とに分けています。
 
この程度であればメリットは感じず、ただ書く量が増えるだけに感じると思いますが、これが数百行を超えると大きく変わってきます。
 
– ビューとコントローラを分けることでデザイナとプログラマの作業分担をしやすくなる。
– コントローラとモデルを分けることで同じ処理を行う時にコピーペーストを行う必要がなくなる。
– さらにモデルをドメイン固有モデルと共通モデルとに分ける事で、他のシステムで使い回しても影響がない部品(=共通モデル)が明確になり、再利用性が高まる。
 
感覚が掴めそうな方は、サンプルに挙げた挨拶プログラムを基本として、次のように改善していってみましょう。
 
– ビューに PHP コードでなく Smarty を使うようにする。その場合、htmlspecialchars はやめ、代わりに Smarty の ${result|escape} など escape 修飾子を利用するといいでしょう。
– モデルを関数ではなくクラスにする。
– コントローラに処理を書いていく時は、処理を関数として抽出できないか目を光らせておきましょう。抽出できそうな時はドメイン固有モデルとして抽出、移動しましょう。
– ドメイン固有モデルが増えてきた時は、汎用的な共通モデルにできないか考えましょう。つまり、モデルのコードを一切変更することなく、他のシステムで利用可能かどうか考えましょう。他のシステムでも有用な機能があるのに、他のシステムで使うには変更の必要がありそうなときは、変更せずに済む方法を実装し、共通モデルにすることも検討してみましょう。(内部パラメータの setter を用意する、設定ファイルを読み込めるようにするなど)
 
なお、PHP での MVC モデリングについては Zend コーディング指針の MVC の項も参考になります。
 
参考:
Smarty(日本語マニュアル)

CSS バグ辞典

http://members.at.infoseek.co.jp/cssbug/index.html
特に IE について、けっこうな量の不具合があるのだなぁと改めてびっくりです。
例えば vertical-align 周りの動作の怪しさはずっと気になっていたので、検証コードで確認できてすっきりしました。
参考:
CSS テクニックコミュニティ(mixi)

bind8/bind9 設定の基本

named.conf 全体設定、zone の項目でゾーン(ドメイン、逆引き時の IP アドレス)を定義し、実際の DNS レコードについては、ゾーンごとにゾーンファイルに記述していく。

named.conf:
    options {
        directory “/etc/named”;
        // 任意のディレクトリ名。ここで設定したものが、
        // ゾーンファイルなど設定ファイルのルートディレクトリになる。
    };
    // 正引き(ドメイン -> IP アドレス)用。
    zone “example.org” { // example.org ドメインについての定義
        type master; // 他の DNS サーバを参照するのではなく、自分で
                      // DNS レコードを定義する場合は master。
                      // 他のサーバを参照する場合は slave
        file “master/example.org”; // 利用するゾーンファイル。ファイル名は任意のものでよい。master 用。
        // masters { 192.168.1.1; 10.0.0.10; }; // DNS レコード参照先マスタサーバ。slave 用。
    };
    
    // 逆引き(IP アドレス -> ドメイン)用。
    zone “1.168.192.in-addr.arpa” { // 192.168.1.* 用。ドット区切りで逆転して、in-addr.arpa を付加する。
        type master;
        file “master/1.168.192.in-addr.arpa”;
    }

次にゾーン設定ファイルです。ドメイン指定の部分では、最後にドットをつける形式(FQDN)にすることに注意します。これは FQDN 形式でないと後にドメイン名が続く(example.org のゾーン設定の場合、ns.example.org は ns.example.org.example.org.)と解釈するためです。

master/example.org:
$TTL 3600 ; Default TTL. 全てのレコードについて有効期間を何秒にするか。
@ IN SOA ns.example.org. postmaster.examle.org. (
       ; 第一パラメータはどのホストについてのレコードか、で @ は *.example.org の意味です。
       ; 第二パラメータは IN は Internet の意味です。常に IN にするものだと思っていていいんじゃないでしょうか。
       ; 第三パラメータはレコードの内容です。SOA は Start of Authority の意味で、ゾーン設定で最初に設定します。
       ; SOA レコードには後ろに二つパラメータと ( 〜 ) が続きます。
       ; ns.example.org が DNS サーバ, postmaster.example.org が
       ; 管理者宛メールアドレス(@ を . に置き換えたもの)です。
       2006021714; Serial. 任意の数値でよいが、更新の必要の有無は
                 ; この数値以下かで判別されるため、
                 ; 更新の際はこの数値以上にする。
       3600 ; Refresh (sec). 更新確認の頻度を何秒ごとにするか。
       1000 ; Retry (times). 失敗時の試行回数。
       86400 ; Expire (sec). 失敗したときに何秒後に破棄するか。
       86400 ; Minimum TTL(sec).
    )
  IN NS ns.example.org. ; example.org, *.example.org の DNS サーバのドメインを ns.example.org にします。
  IN A 192.168.1.11 ; example.org, *.example.org の名前解決結果を 192.168.1.11 にします。
ns IN A 192.168.1.2 ; ns.exmaple.org の名前解決結果を 192.168.1.2 にします。
www IN A 192.168.1.11 ; www.example.org の名前解決結果を 192.168.1.11 にします。

master/1.168.192.in-addr.arpa:
$ORIGIN 1.168.192.in-addr.arpa. ; 192.168.1.* についての設定であることを宣言
$TTL 3600
@ IN SOA ns.example.org. postmaster.example.org. (
        2006021714 ; Serial
        3600 ; Refresh
        1000 ; Retry
        86400 ; Expire
        86400 ; Minimum TTL
    )
  IN NS ns.example.org. ; Primary DNS
  IN NS ns2.exmaple.org. ; Secondary DNS (Optional)
; 以下 IP アドレスごとに固有で逆引き設定をする場合のみ記述する。
1 IN PTR root.example.org. ; 192.168.1.1 の逆引き結果
11 IN PTR www.example.org. ; 192.168.1.11 の逆引き結果

参考:
ネームサーバーの設定
bind9 設定
ゾーンファイルの書き方について教えてください(@IT)
ISC BIND(English)

パケットキャプチャツール

昔(2年ほど前)使っていて、機能的にも優秀で使いやすかったものなのですが、
名前を度忘れしていて今日やっと思い出しました orz.
 
Wireshark
(Etherealから改称)
ネットワークに実際に流れている中身をキャプチャして、GUI で見ることができるオープンソースなパケットキャプチャツールです。
Ethereal, Wireshark の Windows での動作には WinPcap が必要です。
 
Windows の他に、Linux 等さまざまな環境で動作します。
 
追記:
(2006-07-27) Ethereal は Wireshark として更新されるようになりました
参考:
Ethereal(イーサリアル)の日本語解説サイト
Wireshark

sh での変数とワイルドカードの落とし穴

sh, bash, csh, zshのようなシェルでは

echo *.sh

などというように、ワイルドカードでファイル一覧を取得することができます。
シェルで使えるワイルドカードはアスタリスク(*)、クエスチョンマーク(?)の2種類で、それぞれ、0文字以上の任意の文字列、1文字の任意の文字(英数記号など)を表します。

ワイルドカード文字を、ワイルドカードとしてではなくただの * や ? という1つの文字として扱いたい場合は、次のようにクオート記号で囲むか、エスケープします。

echo "*.sh"
echo "What's up?"

# 結果
# *.sh
# What's up? 
# が表示される

ここに変数が関係してくると、少し厄介なことになります。

# 間違った例
foo="SELECT * FROM table"
echo $foo

この結果は、

SELECT * FROM table

と表示されるかと思いきや、アスタリスクがワイルドカードとして展開されてしまうため、

SELECT FROM table

とアスタリスクが消える、または、現在の作業フォルダのファイル一覧が展開されて、

SELECT bin boot home root var FROM table

のように意図しない結果になってしまいます。

これはかなり怖いことで、この動きにより、最悪致命的なセキュリティホールや、データ消失につながる場合があります。

これを回避するためには, ワイルドカード文字を含む変数の参照時は常にダブルクオート/シングルクオートの中で参照するというテクニックを使います。

# 正しい例
foo="SELECT * FROM table"
echo "$foo"

この結果は、期待通り

SELECT * FROM table

となります。

多段になってもこの動きは変わりません。

foo="SELECT * FROM table"
boo="$foo"
echo "$boo"
# foo, boo 両方の参照をダブルクォートでくくっていることに注意

この結果も、期待通り

SELECT * FROM table

となります。ここで

echo $boo

としていると、やはり変数展開されてしまうため、注意しましょう。
お約束として、変数を参照するときで、変数に記号が入る可能性がある場合は必ずダブルクオートで囲む癖をつけると、こういう間違いがなくなるので幸せになれます。

おまけ

一歩踏み込んだ例として、変数の遅延展開の例を紹介します。

export table=book # 1
# ↑これは全く利用されない
text='SELECT * FROM ${table}' # 2
# ↑シングルクオートなので、textに入る時点ではtableが展開されない。

export table=member # 3
# ↑これが利用される。子プロセスで参照できるよう export している
query=$(sh -c "echo \"$text\"") #4
# ↑ここでtableの中身が展開されます。* の展開を防ぐため $text をダブルクォートで囲んでいることに注意
echo "$query" # 5

この結果は、

SELECT * FROM member

と出力されます。
処理の流れは以下のとおりです。

1: table 変数に book をセット
2: text 変数に SELECT * FROM ${table} をセット(table変数の中身はここでは見ない)
3: table 変数の内容を member に書き換え
4:

sh -c "echo \"$text\""

を実行、shが立ち上がり、

echo "SELECT * FROM ${table}"

を実行する。(ここでtableが初めて展開される)
query 変数に、echo が出力した SELECT * FROM member をセットする。(ここでは変数代入だけで画面には出力されない)
5: query 変数の内容、SELECT * FROM member を表示する

あまりやらないですが、こういう複雑な処理でも期待通り動作するのは、適切にクオートしているおかげですね。

MySQL41 の SJIS 環境のクライアントからの文字化けを防ぐ

SJIS 以外は SET NAMES クエリでの設定で解決するが、SJIS ではクエリでは解決できない。
 
結論からいうと、

mysql –default-character-set=sjis [ -u username [ -p ]] […]

のように、クライアントでの接続時に –default-character-set=sjis と明示してやるだけで解決する(SET NAMES .. は不要)。my.cnf の [mysql] の項目に sjis と設定してやってもよい。
 
PHP でも my.cnf を見ているらしい。PHP ではこれ以外の妥当な設定の手段がないもよう。
現在は、一部のコードでのみ対応したい場合は、自分でラッパ関数を作ってやるのがよさそう。PEAR::DB の prepared statement 構文を使う場合は ujis (EUC_JP)として通信させてやらないとうまくいかなかった。
 
詳しく書くと、DB_Common::prepare で呼び出している内部関数で非 UTF8 のつもりでエスケープ処理を行うため、事前エンコードをすると文字が壊れてしまうのが原因。EUC-JP だと、ASCII コードに互換性があるためエスケープ処理で文字が壊れないのでエンコード後のエスケープでも問題ない。
 

//
// 単純なサンプル。実用コードではありませんよ。
//
$con->query(‘SET NAMES ujis’);
echo dec( $con->getOne(enc(‘SELECT description FROM product WHERE name = ?’), enc(array(‘苛性ソーダ’))) ) ;

/**
 *
 */
function dec($val){
    return mb_convert_encoding($val, mb_internal_encoding(), ‘EUC-JP’);
}
/**
 * エンコード。クエリを実行する前の前処理。
 * @param string $val 変換対象の文字列
 * @return string DB クエリの文字コードに変換した文字列
 */
function enc($val){
    return mb_convert_encoding($val, ‘EUC-JP’);
    // $val は mb_internal_encoding() の文字コード。それ以外も
    // 受け付ける場合, 第三引数を ‘auto,UTF-8’ 等にしておく。
}

参考:
OSS Web – 接続キャラクタセットの変更
ここにあるとおり、show variables like ‘character\_set\_%’ では同じなのに、実際は内部的には違うのがなんとも。