Do You PHP?    
Search Engine Optimization  php5 powerd  Valid XHTML 1.0!  Valid CSS!  このサイトのはてなブックマーク数 



last updated
2003/01/13

counter hits
since 1999/11/06


Zend Engine2のα版を試してみる

2002/06/09、ついにPHP5で採用される新エンジン「Zend Engine2」(以下ZE2)のα版がPHP4.3.0dev+ZE2という形で公開されました。

memo[2003/01/13] cvs.php.netからもZE2版PHPをcheckoutできます。2003/01/13現在で、ついに「PHP5.0.0dev」になりました(以下イメージ参照)。インストール手順は、「anonymous cvsからPHPのソースを取得する」にもまとめてありますので、気になる方は是非どうぞ。

PHP5.0.0dev

前々から気になっていたのですが、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パターンが以下のように書けます。

memo[2002/07/01] こちらから動作を確認できます

●ChainOfResponsibility.php

<?php
class CheckHandler
{
    private $next_;

    /**
     * 次に実行されるクラス
     * 参照受け・渡しを意識しない(「&setNext(&$next)」としなくて良い)
     */
    function setNext($next)
    {
        $this->next_ = $next;
        return $this->next_;
    }
          :
}

          :

    /**
     * 処理チェーンの作成。Javaのように、
     * ch.setNext(cn).setNext(ca).setNext(cm)と書けるようになった
     */
    $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_; }

    /**
     * デバッグ情報のトレース
     * 参考文献:WEB+DB PRESS Vol.12(技術評論社,ISBN4-7741-1623-8),p.205
     */
    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>";
    }
}

/**
 * Oracle例外クラス
 */
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); }
}

/**
 * Oracleクラス
 * とりあえず、接続・切断のみ
 */
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()
{
    /**
     * catchされない例外がthrowされると、
     *「Fatal error: Uncaught exception!」となる
     */
    try {
        $ora = new Oracle();

        /**
         * 存在しない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();
    }

    // Javaならスコープ外
    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。。。(^-^;



About This Site |  Privacy Policy |  Contact
Copyright © 1999 - 2003 by Hideyuki SHIMOOKA all rights reserved.