月別アーカイブ: 2013年5月

PHP5.1.2以降のspl_autoload_registerを使う

外部PHPファイルをインポートして呼び出そうとするとき、いちいちファイルの頭で読み込む数だけincludeを行うのは面倒くさいですね。それならば、使いそうなクラス・関数をfunctions.phpのような一ファイルにまとめてしまえば呼び出しが一回で済む、ということも考えつきますが、これだと一クラス一ファイルの原則に反する上、外部ライブラリを使用する場合などは結局インクルードが必要になり、上手く行きません。

そこで、PHP5以降のヴァージョンに搭載された便利なautoload機能を使いましょう。

PHP5以降の__autoload関数を使ったautoload

PHPには、マジックメソッドと呼ばれる”__(アンダーバー2つ)”から始まる特殊なメソッドがあります。たとえばPHPのクラスについてのエントリで出てきた、__construct。これは予約されたメソッド名なので、この名前を使ってオリジナルの関数を作ってはいけません。既にこの名前のメソッドには使用方法が決められているのです。__constructであれば、クラスのインスタンスが作られたときに呼び出される処理ということでしたね。

さて、autoload機能を実現する__autoloadは、クラスに紐づけられたメソッドというわけではなく、グローバル関数です。ですが、基本的にはマジックメソッドの一類であると考えて下さい。この関数は、未定義のクラスが呼ばれた際に自動的に呼び出されます。

<?php
//未定義のクラス
$instance = new undefinedClass();
?>

もしこの呼び出しの時点で、undefinedClass.phpをincludeしていなかったら、通常はメソッドが見つからないというエラーになります。一方、__autoloadを定義しておくことで、こうしたクラスの見つからないケースでの挙動を指定できます。

<?php
__autoload($classname){
include $classname . ".php";
}
?>

__autoloadは呼び出されたクラス名を引数としてとるので、それを適当な変数名で受けて、includeするファイルネームを生成する処理を書くのです。こうすることによって、クラスを作ったらクラス名.phpという名前に保存するようにすれば、呼び出し時に自動的に探し出してincludeしてくれます。

PHP5.1.2以降のspl_autoload_register関数を使ったautoload

ただ、__autoloadにも不便な点があります。それは、一度定義してしまうと、それ以外の__autoload処理を追加していくことが出来ないということです。__constructの例と同じように、こうした特別なメソッドは一度きりのタイミングに処理を登録することが許されているのです。そこで、複数のautoload処理(たとえば、優先順をつけて探索処理を追加したい場合)を登録するにはどうすれば良いか。そのような場合には、spl_autoload_register関数で処理を追加します。

//autoloadスタックに登録したい関数その1
function searchClassPhp($classname){
include $classname . ".class.php";
}
//autoloadスタックに登録したい関数その2
function searchPhp($classname){
include $classname . ".php";
}
 
//関数を両方ともautoloadスタックに追加
spl_autoload_register("searchClassPhp");
spl_autoload_register("searchPhp");

この例では、定義されていないundefinedClassが呼ばれたとき、最初にundefinedClass.class.phpというファイルが存在するかチェックされます。もし見つからない場合は、undefinedClass.phpが探されます。

ちなみに、spl_autoload_register関数を使ってしまうと、__autoloadで定義した処理は魔法が解けて無視されるようになってしまいます。そこで、__autoloadを定義しており、spl_autoload_register関数も使いたい場合には、__autoload自体もautoloadスタックに登録して下さい。

//魔法の解けた__autoloadを普通の関数として登録
spl_autoload_register("__autoload");

spl_autoload_register関数には、無名関数も登録することが出来ます。

//無名関数の登録
spl_autoload_register(function($classname){
include $classname . ".class.php";
});

ただし無名関数については5.3.0以降のヴァージョンに限定されます。だんだん便利になっているのか、母屋を継ぎ足し継ぎ足し増築しているのか、なんともPHPらしいですね。


WebKitブラウザをXMLビューワとして使う際の落とし穴

Safari、ChromeなどのWebKitブラウザは、標準のデバッギングツールも含めほぼオールマイティな機能を備えています。その機能の一つにXMLのネスト表示というものがあり、ブラウザでXMLにアクセスした際にデバッギングツール内で折りたたみ表示してくれるという、ごく一部の人間にだけうれしい機能をもっています。

これはたとえばXMLを返却するURLを指定し、帰ってくる値を確認しつつ作業をすすめるという場面で重宝する機能です。WEBサービスの中には、XMLで値を取得できるというものがかなりありますので、値だけ取得してローカルで表示するクライアントを作成する際に便利です。

先日まさにそういった使い方をしていたのですが、ブラウザで確認したノード名をクライアント側で取得する際に、どうやっても取得できないという現象に見舞われました。ブラウザ上ではそのノード名で値が取れています。でもローカルではその値を指定しても取れない。この現象に何度も頭をひねりました。

そうして発見したのは、WebKit系のブラウザがノード名を勝手に小文字に変換して表示しているということです。

XMLでは、大文字小文字は勿論区別します。たとえば<node>という値と<Node>という値は異なりますので、<Node>という名前でセットされた値に、<node>でアクセスしようとしても、そんな名前は知らないと言われます。

ひたすら<node>でのアクセスを試みて失敗したあげく、WEBサービスの管理者に問い合わせるところでした。

ゆるいHTML(<BR>とかですね)を処理・表示できるように小文字に統一しているのかもしれませんが、ひどい落とし穴もあったものです。


クラスベース言語PHPの基本作法

オブジェクト指向のタイプ別分類によれば、JavaScriptが属するプロトタイプベース言語とは異なる、クラスベース言語に含まれるPHP。けれども、そもそもPHPという言語は場当たり的な書き方ができる言語としてブイブイ鳴らしていたもので、ゆえにPHP4以前のヴァージョンでのオブジェクト指向サポートは少し心許ないものでした。

2004年にリリースされたPHP5では、その辺りの弱さを解決し、Javaに近いクラスベースに移行しています。したがって、PHPでのクラスを使った開発の作法というのは、基本的にはJavaのそれに近いものになります。基本的な部分の書き方を見てみましょう。

クラスの宣言とアクセス制限

PHPの基本的なクラス宣言です。

//クラス宣言 someHowClass.php
class someHowClass{
 
//クラスプロパティ
public $somehow = "とにかく";
 
//メソッド
function someHowMethod(){
return "動け!";
}
 
}
 
//別ファイルからの呼び出し・インスタンス化・メソッドへのアクセス
require_once("someHowClass.php");
$somehow = new someHowClass();
echo $somehow->somehow . $somehow->someHowMethod();
 
//結果表示
とにかく動け!

クラス宣言をする場合、classという予約語を使います。クラスプロパティ・クラスメソッドの宣言は、通常の変数宣言や関数定義をclassの括弧内で行うことでできます。この際にプロパティ・メソッドへのアクセス制限を行うことも可能で、その場合に使う修飾子はJavaやSmalltalkと同じく、public、private、protectedがあります。

アクセス修飾子 アクセス可能な範囲
public(初期値) メソッド・プロパティをどこからでも呼び出し可能
private クラス内でのアクセスが可能
protected クラス内および子クラスからのアクセスが可能

コンストラクタ

インスタンスが作成される際の初期値を定義するコンストラクタは、__construct()という関数の処理として書きます。

//クラス宣言 someHowClass.php
class someHowClass{
 
//コンストラクタ
function __construct($value){
$this->value = $value;
}
 
//メソッド
function someHowGetValue(){
return $this->value;
}
 
}
 
//別ファイルからの呼び出し・インスタンス化・メソッドへのアクセス
require_once("someHowClass.php");
$somehow = new someHowClass("動け!");
echo $somehow->someHowGetValue();
 
//結果表示
動け!

コンストラクタの呼び出し時に引数を与えることで、インスタンスの初期値を設定することができます。なお、PHP4の時点でのクラスは、コンストラクタがクラス名と同名のメソッドでした。互換性の為に現在でも、__constructの宣言が無い場合には同名メソッドをコンストラクタとする仕様になっています。

クラスの継承と親クラスのメソッド・プロパティの参照

クラスの継承についても見てみましょう。

//クラス宣言 anyWayClass.php
class anyWayClass extends someHowClass{
 
//メソッドをオーバーライド
function someHowGetValue(){
return $value . "!";
}
 
//メソッドの追加
function anyWayGetValue(){
//親クラスのメソッドの参照
return parent::someHowGetValue();
}
 
}
 
//別ファイルからの呼び出し・インスタンス化
require_once("someHowClass.php");
require_once("anyWayClass.php");
$anyway = new anyWayClass("動け!");
 
//子クラスでオーバーライドしたメソッドの呼び出し
echo $anyway->someHowGetValue();
 
//結果表示
動け!!
 
//子クラスで宣言したメソッドの呼び出し
//(処理内容は親クラスのsomeHowGetValue()メソッドの呼び出し)
echo $anyway->anyWayGetValue();
 
//結果表示
動け!

継承をする場合には、extendsという予約語を使い継承元のクラスを指定します。親クラスのプロパティ・メソッドは自動的に子クラスにも実装されるため、上の例では子クラス内で宣言の無いコンストラクタ(__construct())についても処理が出来ています。
同名プロパティ・メソッドに、子クラスでは違う処理を充てたいという場合には、子クラス内で同名プロパティ・メソッドを宣言すると、そちらが呼び出し時に優先されるようになります。上の例ではsomeHowGetValue()メソッドをオーバーライド(上書き)したため、処理結果が親クラスと変わっています。

一方、子クラスで宣言したものではなく、親クラスのsomeHowGetValue()メソッドを呼び出したくなった場合には、parent::という語をつけて呼び出すことで、子クラス内から親クラスのプロパティ・メソッドの参照が出来ます。

なお、PHPの継承は多重継承を認めない仕様になっています。

PHPのクラスの基本はこのような感じです。その他にinterfaceの定義などもあるのですが、とりあえずは説明をここまでとします。

また、いままでオブジェクト指向に全く触れたことがないという人間にとってはチンプンカンプンな内容だったと思うので、オブジェクト指向の説明記事についてはいずれ改めて書かせていただこうと思います。