PHP5.3.0がついにリリース

こんにちは。小川です。
PHP5.3.0がついにリリースされました。新機能の紹介もかねて色々と使ってみたいと思います。

PHP5.3.0では様々な機能追加などが行われています。詳しくはphp.netを参照してください。
いくつかピックアップしてみます。

●名前空間のサポート
●遅延静的束縛
●無名関数
●パフォーマンスの向上

特に名前空間や無名関数などの機能は期待していた方も多いのではないでしょうか。説明するよりもまずは動かしてみたいと思います。
まずはインストールです。php.netからソースをダウンロードしてインストールします。


$ wget http://jp2.php.net/get/php-5.3.0.tar.gz/from/jp.php.net/mirror
$ tar zxvf php-5.3.0.tar.gz
$ sudo mv php-5.3.0 /usr/local/lib
$ cd /usr/local/lib/php-5.3.0
$ ./configure 
$ make
$ sudo make install

Ubuntuにインストールしたのですがconfigureで「configure: error: xml2-config not found. Please check your libxml2 installation.」というエラーが出たので一応対処法も。


$ cd /usr/lib
$ sudo ln -s libxml2.so.2 libxml2.so
$ sudo aptitude install libxml2-dev

これでインストールは完了です。configureのオプションは適当に。。。実行ファイルはphp-5.3.0という名前にしてあります。


$ php-5.3.0 -v
PHP 5.3.0 (cli) (built: Jul  1 2009 15:44:52)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies

それではいよいよ使ってみましょう。

◆名前空間

PHP: 名前空間 – Manual


<?php
// namespace1.php
//var_dump(__NAMESPACE__);
//Fatal error: Namespace declaration statement has to be the very first statement
namespace foo;
var_dump(__NAMESPACE__);    // => string(3) "foo"
class MyObject
{
  public function method()
  {
    return __METHOD__;
  }
}
namespace bar;
var_dump(__NAMESPACE__);    // => string(3) "bar"
class MyObject
{
  public function method()
  {
    return __METHOD__;
  }
}
$obj1 = new \foo\MyObject;
$obj2 = new \bar\MyObject;
$obj3 = new MyObject;
var_dump($obj1->method());    // => string(20) "foo\MyObject::method"
var_dump($obj2->method());    // => string(20) "bar\MyObject::method"
var_dump($obj3->method());    // => string(20) "bar\MyObject::method"
namespace baz;
//$obj = new MyObject;
//Fatal error: Class 'baz\MyObject' not found

名前空間の区切り文字は「\」になります。namespaceという文が名前空間の宣言になります。namespaceは1つのファイルに複数記述可能ですが、最初の宣言の前に処理を行うとエラーになるようです。マニュアルによるとdeclare文のみはnamespaceより先に宣言可能なようです。
また\foo\MyObject のように先頭に\をつけると名前空間の絶対指定になります。


<?php
// namespace2.php
namespace foo;
//$datetime = new DateTime;
//Fatal error: Class 'foo\DateTime' not found
$datetime = new \DateTime;  // \ではじめることで絶対指定に
var_dump(get_class($datetime));   // => string(8) "DateTime"

名前空間を指定している場合、グローバルな名前空間に存在するオブジェクトは先頭に\をつけて指定すると取得できます。


<?php
// namespace3.php
require_once 'namespace1.php';
use foo\MyObject;
class ExtendedObject extends MyObject
{
  public function method()
  {
    return __METHOD__;
  }
}
$obj = new ExtendedObject;
$obj->method();   // => string(22) "ExtendedObject::method"

別の名前空間に所属するオブジェクトをインポートする場合はuseを使います。


<?php
// namespace4.php
// foo名前空間
namespace foo {
class MyObject {};
}
// グローバルな名前空間
namespace {
$class = 'foo\\MyObject';
$obj = new $class;
var_dump(get_class($obj));    // => string(12) "foo\MyObject"
}

先ほどから1つのファイルに複数の名前空間の記述を行っていましたが、本来複数の名前空間をひとつにまとめることは推奨されていません。必要な場合は上記の用に{}で囲む方がまだ良いとされています。グローバルな名前空間を使用したい場合は上記の用にnamespace {}で囲む方法しかないようですので、グローバルな名前空間を扱いたい場合は上記の用に記述をします。
php.netのマニュアルに色々と書いてありますので、是非目を通していただければと思います。

◆遅延静的束縛

PHP: 遅延静的束縛 (Late Static Bindings) – Manual


<?php
// lsb.php
class User
{
  protected $password;
  public function __construct($password)
  {
    $this->password = self::convertPassword($password);
  }
  public function getPassword()
  {
    return $this->password;
  }
  static public function convertPassword($password)
  {
    return md5($password);
  }
}
class AdminUser extends User
{
  static public function convertPassword($password)
  {
    return sha1($password);
  }
}
$user = new User('password');
var_dump($user->getPassword());
$admin = new AdminUser('password');
var_dump($admin->getPassword());

コンストラクタでselfキーワードを使っています。上記の場合、AdminUserではパスワードをmd5ではなくsha1でハッシュするようにしたいのですが、実行結果はいかになります。


string(32) "5f4dcc3b5aa765d61d8327deb882cf99"
string(32) "5f4dcc3b5aa765d61d8327deb882cf99"

selfというキーワードは実行される前の段階で展開されてしまいます。上記のコンストラクタは


<?php
class User
{
  public function __construct($password)
  {
    $this->password = User::convertPassword($password);
  }
  ...
}

このようにselfをUserに置き換えられて実行される仕組みになっています。この仕組みを解消するのが「遅延静的束縛 (Late Static Bindings)」という機能です。selfの代わりにstaticというキーワードを使用すると、呼び出しが行われたクラス名で解釈されるようになります。


<?php
class User
{
  public function __construct($password)
  {
    $this->password = static::convertPassword($password);
  }
  ...
}


string(32) "5f4dcc3b5aa765d61d8327deb882cf99"
string(40) "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8"

◆無名関数

PHP: 無名関数 – Manual

無名関数(クロージャ)は名前の通り関数名のついていない関数を作成するための機能です。今までcreate_functionというコールバックを作成するための関数は存在していましたが、それよりもスマートなやりかたで実行できます。


<?php
// closure.php
$function = function($a) {
  echo $a . "\n";
};
var_dump(get_class($function));   // => string(7) "Closure"
$function("I love you");          // => I love you

無名関数はfunction文を使って作成します。関数を作る要領で使えますのでスムーズに使えるかと思います。

ここまでに紹介した機能を使って簡単なArrayObjectの拡張クラスを作ってみました。RubyのEnumerableモジュールをまねしようと思ったのですが流石に色々と無理があったので、基本的な機能だけをArrayObjectに移植してみました。


<?php
// ArrayObject.php
namespace Asial;
class ArrayObject extends \ArrayObject
{
  public function each(\Closure $closure)
  {
    $this->map($closure);
    return $this;
  }
  public function map(\Closure $closure)
  {
    $values = array();
    foreach ($this as $key => $value) {
      $values[$key] = $closure($value);
    }
    return new static($values);
  }
  public function dup()
  {
    return new static($this->getArrayCopy());
  }
  public function delete(\Closure $closure)
  {
    $values = array();
    foreach ($this as $key => $value) {
      if (!$closure($value)) {
        $values[$key] = $value;
      }
    }
    $this->exchangeArray($values);
    return $this;
  }
  public function find(\Closure $closure)
  {
    foreach ($this as $key => $value) {
      if ($closure($value)) {
        return $value;
      }
    }
  }
}
$values = array('blue', 'red', 'green', 'yellow');
$array = new ArrayObject($values);
echo "要素を全て大文字に\n";
var_dump($array->map(function($v) { return strtoupper($v); }));
echo "複製したオブジェクトからrが含まれる要素を削除\n";
var_dump($array->dup()->delete(function($v) { return strpos($v, 'r') !== false; } ));
echo "末尾がdとなる要素を検索\n";
var_dump($array->find(function($v) { return substr($v, -1) === 'd'; }));
echo "遅延静的束縛のチェック\n";
class ArrayObject2 extends ArrayObject {};
$array2 = new ArrayObject2;
var_dump(get_class($array2->dup()));


要素を全て大文字に
object(Asial\ArrayObject)#4 (1) {
  ["storage":"ArrayObject":private]=>
  array(4) {
    [0]=>
    string(4) "BLUE"
    [1]=>
    string(3) "RED"
    [2]=>
    string(5) "GREEN"
    [3]=>
    string(6) "YELLOW"
  }
}
複製したオブジェクトからrが含まれる要素を削除
object(Asial\ArrayObject)#4 (1) {
  ["storage":"ArrayObject":private]=>
  array(2) {
    [0]=>
    string(4) "blue"
    [3]=>
    string(6) "yellow"
  }
}
末尾がdとなる要素を検索
string(3) "red"
遅延静的束縛のチェック
string(18) "Asial\ArrayObject2"

これ以外にも三項演算子のショートカット「?:」の実装、__DIR__定数、DateTimeオブジェクトの機能追加など様々な機能が追加されています。PHP5.3ではコードの幅が大きく広がったのではないかと思います。

駆け足での紹介となりましたがいかがだったでしょうか。ちなみにSymfonyやDoctrineは次期メジャーリリースでは5.3が必須になるようです。もっともまだまだ先の話になると思いますが。今後はPHP5.3以降で動作するソフトウェアが出てくると思いますので、是非PHP5.3で実装された機能をチェックしてみてください。