検索
連載

さっくり理解するPHP 5.5の言語仕様と「いい感じ」の使い方PHP 5.5の新機能(2/2 ページ)

PHP 5.5.0が公開されました。オペコードキャッシュやジェネレータなど、言語仕様としても実行エンジンとしても挑戦的な内容が含まれています。

PC用表示 関連情報
Share
Tweet
LINE
Hatena
前のページへ |       

メモリ使用量を削減可能

 例えば、1から1万までの、1万個の値に対して何らかの処理を実行することを考えましょう。1から1万までの値は、配列を使えばrange()関数で作ることができ、以下のように書けます(1行目)。2行目以下のforeachによるループで、1万個の値に対して何らかの処理(下の例ではdo_something関数)を実行します。

$array = range(1, 10000);
foreach ($array as $val) {
    do_something($val);
}

 ただしこの場合は、当然ながら1万要素分のメモリが確保されます。これを、ジェネレータを使って書き換えると次のようになります。

function counting($limit) {
    for ($i = 1; $i <= $limit; ++$i) {
        yield $i;
    }
}
foreach (counting(10000) as $val) {
   do_something($val);
}

 1から1万までの1万個の値は、foreachによる繰り返しのたびに呼ばれるジェネレータから取得しているので、1万要素分のメモリが確保されることはありません。もし何らかのロジックによって配列の要素を作り出せるならば、要素数が多くなる場合はジェネレータを用いた方がメモリの利用効率では有利になります。

 次に、ジェネレータの例題としてよく挙げられる「無限に素数を生成するジェネレータ」を作ってみましょう。PHPでは以下のように書けます。

function prime() {
    $n = 2;
    $p = [];
    while (1) {
        $prime = true;
        foreach ($p as $f) {
            if ($n % $f == 0) {
                $prime = false;
                break;
            }
        }
        if ($prime) {
            yield $n;
            $p[] = $n;
        }
        ++$n;
    }
}

 これを、下のように普通にforeachを用いて繰り返し実行してしまうと、それこそメモリの許す限り無限に素数を生成します。

foreach (prime() as $prime) {
    echo $prime . ' ';
}

 そこで、Iteratorのcurrentメソッドやnextメソッドを用いて、以下のように必要な個数だけ取り出すように変更するとよいでしょう。

$p = prime();
for ($i = 0; $i < 100; ++$i) {
    echo $p->current() .' ';
    $p->next();
}

 PHPのジェネレータはIteratorインターフェイスに合わせて、連想配列(インデックスに、整数の代わりに文字列を指定する配列)の要素を返すこともできます。下のコードは、date()関数を利用して各月の英語名を返すジェネレータmonthsを作った例です。

function months() {
    foreach (range(1, 12) as $num) {
        $month = date('F', mktime(0, 0, 0, $num));
        yield $num => $month;
    }
}
foreach (months() as $num => $name) {
    echo "$num: $name\n";
}

 このプログラムの出力は以下のようになります。

1: January
2: February
3: March
4: April
5: May
6: June
7: July
8: August
9: September
10: October
11: November
12: December

例外処理でついにfinallyが利用可能に

 PHPの例外処理はバージョン5.0から実装されています。ただし、Javaのfinallyブロックに相当する機能がなかったため、この機能を実装するようずっと要望が出されていました。PHP 5.5では、例外処理のfinallyブロックが実装されました。

 例外処理を実装するには、tryブロックと、それと対になるcatchブロックを作成します。catchブロックの後にさらにfinallyブロックが存在すると、例外が発生したかどうかにかかわらず、finallyブロックが必ず実行されます。

try {
    throw new Exception('some error');
} cactch (Exception $e) {
    echo $e->getMessage() ."\n";
} finally {
    // 例外が発生してもしなくても実行される
    echo "finally!\n";
}

パスワード専用のハッシュ関数を用意

 ユーザーアカウントのパスワードをデータベースなどに保存する場合は、ハッシュ化して保存することが一般的です。これはもしデータベースに格納されている生のデータが漏えいしても、データの内容は知られないようにするためです。ただし、PHPでのハッシュ化の方法は複数あり、どの方法が適切なのかは技術の進歩に応じて変わっていくものなので、一概にこれだと言い切ることはできません。かつては適切だったmd5やsha1も、昨今のセキュリティ事情では決して安全とは言えません。

 そこでPHP 5.5ではパスワードをハッシュ化する専用の関数を用意しました。これはcrypt()のラッパーになっており、アルゴリズムの選定やソルト(ハッシュ処理の際に追加するデータ)の指定などを自動で行うものです。

// パスワード
$password = 'UUDDLRLRBA';
// ハッシュ化パスワードを生成
$hashed = password_hash($password, PASSWORD_DEFAULT);
echo "$hashed\n";

 上のコードの出力は、以下のようになります。異なるソルトが毎回自動生成されるので、出力内容は毎回変わります。

$2y$10$cz.haHF6FJxNyaKtEdbBs.u5W5UxD31uH0eT/HsSLkBE4mwSePUwu

 パスワードを検証するには以下のようなコードを書きます。このコードを見ると分かるように、使用したアルゴリズムやそのオプション、ソルト情報などはハッシュ文字列に含まれているので、関数にオプションを渡す必要がなく簡潔に記述できます。

if (password_verify($password, $hashed)) {
  echo "Vefified!\n";
}

 PHP 5.5で改善された主な機能を解説しました。他にも、例えば文法では、これまでシンタックスエラーになっていた、あると便利な構文が数多く追加されています。

 PHP 5.5は、PHP 5.4から順当に進化をしながらも、細かい部分の使い勝手を改善してきたバージョンになっています。これまでPHPに新バージョンが出た後の普及速度を見ると、PHP 5.5が広く使われるようになるのは1年以上先になりそうですが、Opcacheの性能向上具合によっては一気に広まるかもしれません。

Copyright © ITmedia, Inc. All Rights Reserved.

前のページへ |       
ページトップに戻る