unoh.github.com

PHP5.3の新機能まとめ

Tue Aug 25 21:35:57 -0700 2009

先日から「GnuCash」使って家計簿を付け始めたyukiです。
だいぶ長いこと複式簿記に触れていなかったので、思い出すのに一苦労でした。家計簿で「損益計算書」や「貸借対照表」、「キャッシュフロー」を見てみると新たな発見があっていいですね。是非オススメです。

さて今回は、先日の社内勉強会で行った「いまさらPHP5.3をあらためて見てみる」をレポートします。機能をサンプルコード交えてざっとチェックしていっただけですが、参考になれば幸いです。

参考:PHP 5.2.x から PHP 5.3.x への移行

新機能

新機能からチェックしていきます。まずは話題になっていた名前空間などから。

名前空間

# global.php

namespace {

class MyProject
{
    public function getName()
    {
        return 'global';
    }
}

}

# unoh.php

namespace unoh {

class MyProject
{
    public function getName()
    {
        return 'unoh';
    }
}

}

# photo.php

namespace unoh\photo {

class MyProject
{
    public function getName()
    {
        return 'photozou';
    }
}

# machi.php

namespace unoh\machi {

class MyProject
{
    public function getName()
    {
        return 'machi';
    }
}

}

# sample.php

require 'global.php';
require 'unoh.php';
require 'photo.php';
require 'machi.php';

// requireした名前空間には影響を受けない
var_dump(__NAMESPACE__);

$a = new MyProject;
var_dump($a->getName());

// globalを指定してnewする
$a = new \MyProject;
var_dump($a->getName());

// unoh空間のクラスを指定してnewする
$a = new \unoh\MyProject;
var_dump($a->getName());

// 下位空間のクラスを指定してnewする
$a = new \unoh\photo\MyProject;
var_dump($a->getName());

$a = new \unoh\machi\MyProject;
var_dump($a->getName());

// エイリアス作成
use \unoh\photo\MyProject as photozou;
var_dump(new photozou);

// エイリアスでタイプヒンティングしたらどうなるか?
class TypeHint
{
    public function hint(photozou $a)
    {
        var_dump($a);
    }
}

$a = new TypeHint;

$photozou = new photozou;
$a->hint($photozou);

$photozou = new \unoh\photo\MyProject;
$a->hint($photozou);
以上のコードを実行した時、結果は次のようになります。
string(0) ""
string(6) "global"
string(6) "global"
string(4) "unoh"
string(8) "photozou"
string(5) "machi"
object(unoh\photo\MyProject)#2 (0) {
}
object(unoh\photo\MyProject)#1 (0) {
}
object(unoh\photo\MyProject)#3 (0) {
}

ほぼ予想通りでしたが、タイプヒンティングにエイリアスが利用できるのは便利な反面、混乱の元になるかもしれません。手元環境しだいですが、バックスラッシュもあまり違和感を感じませんでした。

遅延静的束縛

静的呼び出し時にstaticキーワードが使えるようになりました。実際の動作は以下のような感じです。
# self.php

class A {
    public static function who() {
        echo __CLASS__ . "\n";
    }
    public static function test() {
        self::who();
    }
}

class B extends A {
    public static function who() {
         echo __CLASS__ . "\n";
    }
}

B::test();

# static.php

class A {
    public static function who() {
        echo __CLASS__ . "\n";
    }
    public static function test() {
        static::who();
    }
}

class B extends A {
    public static function who() {
         echo __CLASS__ . "\n";
    }
}

B::test();

違いはClass Aのtestメソッド内での呼び出しです。それぞれselfがA、staticがBと表示されます。

ジャンプラベル (制限付きgoto)

素人にはお勧めできない諸刃の剣、一歩間違えるとスパゲッティコード生成機のgoto文です。

# goto.php

for ($i = 0; $i < 100; $i++) {
    for ($j = 0; $j < 100; $j++) {
        if ($i > 50 && $i === $j ) {
            goto wall;
        }
    }
}

echo '超えられない壁';
exit;

wall:
echo '超えてしまった!';

結果は予想通り「超えてしまった!」と出ます。では次のコードはどうなるでしょうか?

# goto2.php

$photo = 'フォト蔵';

loop:
if ($photo === 'A') {
    goto A;
} elseif ($photo === 'B') {
    goto B;
} else {
    goto C;
}
exit;

C:
echo 'C';
goto B;
exit;

A:
echo 'A';
goto loop;
exit;

B:
echo 'B';
goto A;
exit;

このくらいならまだ判別できますが、だんだんわかりにくくなってきました。ちなみにこれを実行するとCBACBACBA...と無限ループします。

# goto3.php

class myClass
{
    public function jump()
    {
        goto out;
    }
}

$v = new myClass;
$v->jump();
exit;

out:
echo 'OUT!';

これは次の出力になります。

Fatal error: 'goto' to undefined label 'out' in /home/edy/php5.3/goto/goto3.php on line 8

ご覧のとおりメソッドの内外に出入りはできないようです。

クロージャ (ラムダ/無名関数)

コールバックに無名関数が指定できるようになりました。

# callback.php

$system = array('leopard','vista');

$upgrade = array_map(function($value) {
    if ($value === 'leopard') {
        return 'snow '.$value;
    } elseif ($value === 'vista') {
        return 'windows7';
    }
}, $system);

var_dump($upgrade);

ぱっと見がなんだかJavascriptっぽくなってますね。結果は以下の通りです。

array(2) {
  [0]=>
  string(12) "snow leopard"
  [1]=>
  string(8) "windows7"
}

スコープ外の変数を利用する場合は次のようになります。

# use.php

$args = 'B';

$patternA = function($value) use ($args) {
    $args = 'C';
    return "Value: $value Args: $args";
};

$patternB = function() use (&$args) {
    $args = 'D';
};

var_dump($args);
var_dump($patternA('A'));
$patternB();
var_dump($args);

結果は以下の通りです。

string(1) "B"
string(16) "Value: A Args: C"
string(1) "D"

&の有無で挙動が代わるので注意が必要です。

他には以下のような変更がありました。便利なのが追加された印象ですね。

この中だと、とくに三項演算子に注意が必要です。式の結果がtrueであれば「式の結果」を、でなければ?:に続くものを返します。

非推奨関数

もともと非推奨だったものが継続されているイメージですが、ereg系はまだ現役でコードに存在すると思いますので、注意が必要ですね。動かないわけではないので、今のところ実害はないと思います。

パラメータ変更

目に付いたのはこのあたりです。

nl2br()
is_xhtml が追加されました。
round()
mode が追加されました。
json_encode()
options が追加されました。
json_decode()
depth が追加されました。
Exception::__construct
previous が追加されました。ネストした例外補足の時など、以前の例外を追加して統合するため。

新クラス

日付・時刻

DateTimeクラス自体も強化されたので、date()とかmktime()とかtime()とかで泥臭さが軽減されるかもしれません。個人的には嬉しい機能追加です。

Phar

SPL

Heap,Queue,Stack,FixedArray,ObjectStorageなどのデータ構造系が追加されています。

その他

その他細かくは以下のあたりが気になりました。

なぜモザイクが...