|
2002/06/09、ついにPHP5で採用される新エンジン「Zend Engine2」(以下ZE2)のα版がPHP4.3.0dev+ZE2という形で公開されました。
[2003/01/13] cvs.php.netからもZE2版PHPをcheckoutできます。2003/01/13現在で、ついに「PHP5.0.0dev」になりました(以下イメージ参照)。インストール手順は、「anonymous cvsからPHPのソースを取得する」にもまとめてありますので、気になる方は是非どうぞ。
前々から気になっていたのですが、PHP4と比べてオブジェクトの扱いがかなり変わるようです(Javaっぽく。。。)。概要は以下のような感じです。
- 新しいオブジェクトモデルの採用(オブジェクトの参照扱い)
- 「private」メンバー変数
- オブジェクトのクローニング
- 強制的なオブジェクトの削除(デストラクタ)
- ネストしたクラス
- コンストラクタの名称の統一
- 例外処理(try~catch/Exceptionのthrow)
- その他いろいろ
とりあえずインストールし、PHP本家にあるスクリプト例を実行して、どんな感じか試してみました。以下、サンプルの一部だけ扱っていますが、こちらで全サンプルの動きを実際に確認できるようにしましたので、気になる方は見てみてください。なお、サーバが落ちていたり、PHPのバージョンを入れ替えてたりしますので、常に繋がるとは限りませんので、あしからず。。。m(_"_)m
新しいオブジェクトモデルの採用
実際にサンプルを実行しても「なんてことない」かも知れませんが、個人的には、これ、(良い方の)インパクトが結構大きいです。ちなみに、PHP4.2.0の場合、Parse error: parse error, unexpected T_OBJECT_OPERATOR in factory_method.php on line 27となります。
●factory_method.php
<?php
class Circle {
function draw()
{
print "Circle\n";
}
}
class Square {
function draw()
{
print "Square\n";
}
}
function ShapeFactoryMethod($shape)
{
switch ($shape) {
case "Circle":
return new Circle();
case "Square":
return new Square();
}
}
ShapeFactoryMethod("Circle")->draw();
ShapeFactoryMethod("Square")->draw();
?>
ZE2であれば、以前にご紹介したChain of Responsibilityパターンが以下のように書けます。
[2002/07/01] こちらから動作を確認できます
●ChainOfResponsibility.php
<?php
class CheckHandler
{
private $next_;
function setNext($next)
{
$this->next_ = $next;
return $this->next_;
}
:
}
:
$ch->setNext($cn)->setNext($ca)->setNext($cm);
:
?>
上のサンプルでは、クラスのメンバー変数を「private」宣言しているので、クラス外からはアクセスできなくなります。
ネストしたクラス
扱い的には、Javaと似てますね。new [クラス名]::[ネストクラス名]としてインスタンス化するようです。
●nested_class.php
<?php
class Database {
class MySQL {
var $host = "";
function db_connect($user) {
print "Connecting to MySQL database '$this->host' as $user\n";
}
}
class Oracle {
var $host = "localhost";
function db_connect($user) {
print "Connecting to Oracle database '$this->host' as $user\n";
}
}
}
$MySQL_obj = new Database::MySQL();
$MySQL_obj->db_connect("John");
$Oracle_obj = new Database::Oracle();
$Oracle_obj->db_connect("Mark");
unset($MySQL_obj);
unset($Oracle_obj);
?>
コンストラクタの名称の統一
PHPマニュアルにも「先頭が__(アンダースコア2つ)で始まる関数名は予約されている」とありますが、ZE2ではコンストラクタに「__construct」が使えます。で、どうしても「__construct」にしなきゃいけないかと言うとそうでもないようで、互換性のために「__construct」がない場合、従来の方法でコンストラクタを探すようです(ZEND_CHANGES.txtを参照)。
●oci_test2.php
<?php
class BaseClass {
function __construct()
{
print "In BaseClass constructor\n";
}
}
class SubClass extends BaseClass {
function __construct()
{
parent::__construct();
print "In SubClass constructor\n";
}
}
$obj = new BaseClass();
$obj = new SubClass();
?>
例外処理
スクリプト言語でここまでやるか?という感もありますが、やっぱり一番気になるのがこれでしょう(^-^; サンプルではちょっと分かりづらいですが、実際の使い方としては、「ある処理を行った結果エラーかどうかを判定し、エラーの場合は例外をthrowする」という感じではないかと思います。
●exception_handling.php
<?php
class MyException {
function MyException($_error) {
$this->error = $_error;
}
function getException()
{
return $this->error;
}
}
function ThrowException()
{
throw new MyException("'This is an exception!'");
}
try {
} catch (MyException $exception) {
print "There was an exception: " . $exception->getException();
print "\n";
}
try {
ThrowException();
} catch (MyException $exception) {
print "There was an exception: " . $exception->getException();
print "\n";
}
?>
[2003/01/08]久しぶりにExceptionのサンプルを作ってみました(WEB+DB PRESS Vol.12の小山さんの記事「PHPこども電話相談室 - PHP5はこうなる?」に触発されました(^-^;)。今度はより実践的(?)にしてみました。中身は、以下のような感じです。
- 例外クラス「Exception」「OracleException」「ConnectionFailedException」を用意する
- Oracleクラスのconnectメソッドは、接続に失敗した場合に「ConnectionFailedException」をthrowする
- Oracleアクセスクラスを使用する関数「main」を用意し、try~catchブロック内でOracleクラスのconnectメソッドを「エラーが発生するように」呼び出す。また、catchする例外は、「OracleException」「ConnectionFailedException」の2つ
- 参考程度に、Javaの「Throwable.printStackTrace()」を真似たメソッドも用意
なお、動作確認は2002/12/25にcvsから取得したソースをbuildしたものです。
●exception_handling3.php
<?php
class Exception
{
protected $msg_;
function __construct($msg) { $this->msg_ = $msg; }
function getMessage() { return $this->msg_; }
function getStackTrace()
{
$bt = debug_backtrace();
$ret = get_class($this) . ":" . $this->getMessage();
foreach ($bt as $stack) {
$pathinfo = pathinfo($stack["file"]);
$ret .= sprintf(
" at %s%s(%s:%s)\n",
isset($stack["class"]) ? $stack["class"] . "." : "",
$stack["function"],
$pathinfo["basename"],
$stack["line"]);
}
return $ret;
}
function printStackTrace()
{
echo "<pre>" . $this->getStackTrace() . "</pre>";
}
}
class OracleException extends Exception
{
protected $msg_;
function __construct($msg) { parent::__construct($msg); }
function getMessage() { return $this->msg_["message"]; }
}
class ConnectionFailedException extends OracleException
{
protected $msg_;
function __construct($msg) { parent::__construct($msg); }
}
class Oracle
{
private $conn_;
function connect($user, $pass, $netstr)
{
$this->conn_ = @OCILogon($user, $pass, $netstr);
if (!$this->conn_) {
throw new ConnectionFailedException(OCIError());
}
}
function close()
{
if (!$this->conn_) {
OCILogoff($this->conn_);
}
}
}
?>
<?php
function main()
{
try {
$ora = new Oracle();
$ora->connect("foo", "bar", "hoge");
echo "OK<br>";
$ora->close();
}
catch (ConnectionFailedException $e) {
echo "ConnectionFailedException: " . $e->getMessage() . "<br>";
$e->printStackTrace();
}
catch (OracleException $e) {
echo "OracleException: " . $e->getMessage() . "<br>";
$e->printStackTrace();
}
if (isset($e)) {
echo $e->getMessage() . "<br>";
}
echo "finished<br>";
}
main();
?>
●出力結果ConnectionFailedException: ORA-12154: TNS: サービス名を解決できませんでした。
connectionfailedexception:ORA-12154: TNS: サービス名を解決できませんでした。
at connectionfailedexception.getstacktrace(exception_handling3.php:32)
at connectionfailedexception.printstacktrace(exception_handling3.php:96)
at main(exception_handling3.php:110)
ORA-12154: TNS: サービス名を解決できませんでした。
finished
試して分かったことですが、
- try~catchブロックでcatchされない例外がthrowされると、「Fatal error: Uncaught exception!」となる
- catchする例外は、サブクラスから順に記述する必要がある
- 例外をcatchした場合、try~catchブロック以降で例外が使えてしまう(仕方がないのか。。。)
な感じです。2番目は、Javaと同じですね。気を付ける必要がありそうです。
しかし、スタックトレースが妙にJava。。。(^-^;
|