目次


PHP のニュー・フェイス

一新された PHP

PHP の主要な新しい言語機能を探る

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: PHP のニュー・フェイス

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:PHP のニュー・フェイス

このシリーズの続きに乞うご期待。

PHP は、何百人 (もしかすると何千人) ものコントリビューターによってオープンソース・プロジェクトとして保守、開発が行われています。これらのコントリビューターは、この言語を進化させて最近の Web 開発のニーズを満たせるよう積極的に取り組んでいます。PHP は次々と新しいプログラミングのアイデアを取り入れ、他のプログラミング言語からもアイデアを拝借していますが、上位レベルでの後方互換性は維持しています。PHP が持つこのような性質が、PHP を現在の傑出した存在へと至らせたのです。現在、PHP は Web の 82 パーセントを実行するために使われており、最大規模の Web サイト (Facebook など) でも採用されています。さらに、WordPress、Drupal、Magento、Joomla! などのコンテンツ管理システム (CMS) フレームワークをサポートしているコア・テクノロジーも、PHP です (Web の約 30 パーセントで、これらの CMS フレームワークが使われています)。

長い間 (あるいは、ここ 2 ~ 3 年だけでも) PHP に目を向けていなかったとしたら、現在の PHP の形を理解できないかもしれません。この全 4 回からなる連載の第 1 回では、PHP 5.3、5.4、5.5 などの最近のリリースで追加された最新の機能を紹介します。

もちろん、PHP の言語のみが単独で変わってきたわけではありません。新しい言語機能は PHP 全体の進化の一部に過ぎず、PHP プログラマーが開発サーバーをアセンブルする方法、サード・パーティー・ライブラリーを管理する方法、Web セキュリティーに対処する方法も、同じく変わってきています。この連載の以降の記事では、進化する PHP エコシステムでのこれらの側面を探ります。

誰かが何かを盗んでいるとは思いません。私たちは皆、借りているのです。

B.B. King

名前空間

名前空間は、互いに異なるライブラリーに含まれるクラス (および関数) 同士が同じ名前を使用できるようにするために設計されたプログラミング機能です。PHP が言語として成長し、コード・ライブラリーの再利用がより広がっていくと、名前の衝突が大きな問題になり始めました。自分が使用しているクラスと同じ名前のクラスがたまたまサード・パーティーのライブラリーに含まれていても、それぞれのライブラリーを専用の名前空間に区分けすれば、そのサード・パーティーのライブラリーをインストールして使用することができます (悪い結果をもたらすことはありません)。

名前空間のサポートが組み込まれる以前は、この問題を解決するために、ライブラリーではクラスのすべてに一貫したプレフィックスを付けるという手法を採っていました。例えば、Zend Framework の場合、そのプレフィックスは Zend_ です。この手法では、Zend_Db_Table といった難解なクラス名になり、コーディングの最中に繰り返し、このプレフィックスを入力しなければなりません。この問題が頂点に達したのは、バージョン 5.2 で (切望されていた) DateTime クラスが PHP のコアに追加された時点です。それまで多くのオープンソース・ライブラリーがこのクラスの欠如に対処するために、DateTime という名前の独自のクラスを作成していたことから、DateTime クラスが導入されたことで、これらのオープンソース・ライブラリーが突然機能しなくなったのです。

名前空間を作成するには、namespace キーワードを使用します。名前空間の階層構造は、バックスラッシュ (\) で区切ります。リスト 1 に、単純な例を示します。

リスト 1. 単純な名前空間の使用例
<?php
namespace zebra;

class DateTime
{
   public function __construct() {
      echo "Today!";
   }
}

function stripes() {
   echo "-=-=-=-=-=-=-=-";
}

リスト 1 で、私は zebra という名前の独自の名前空間を定義し、その名前空間の中にクラスと関数の両方を定義しています。この例の場合、DateTime クラスを再定義しても、問題やエラーは発生しません。DateTime の独自のバージョンは、定義した名前空間の中に含まれているためです。この名前空間を使用するには、完全な名前を参照し、区切り文字として \ を使用します (リスト 2 を参照)。

リスト 2. カスタム名前空間を使用する
<?php
include 'listing1.php';

// Use the stripes function I declared in my namespace:
zebra\stripes();

// Use my own DateTime class:
$dt = new zebra\DateTime();

// Now use the 'root' level Datetime class:
$real = new \DateTime('tomorrow noon');
echo $real->format(\DateTime::ATOM);

リスト 2 の 2 行目で include を使用して、namespace ディレクティブで始まるファイル (listing1.php) をインクルードします。これにより、zebra\ 名前空間を先頭に追加することで、この名前空間内のクラスと関数を参照することができます。グローバル・レベルのクラス (元の DateTime など) も引き続き使用することができます。それには、グローバル名前空間であることを示すために、先頭にバックスラッシュを追加します。

リスト 2 の手法は便利ですが、コードの見た目をさらにシンプルにする方法があります。それは、新しく追加された use キーワードです。このキーワードを使用すると、その名前空間の特定のクラスに直接アクセスすることができます (リスト 3 を参照)。

リスト 3. use によって名前空間を含める
<?php
include 'listing1.php';
use zebra\DateTime;

// Use our own DateTime class:
$dt = new DateTime();

use キーワードにエイリアスを作成させて、現在のスコープ内で任意のクラスを名前変更することもできます。リスト 4 に、エイリアスを作成する方法を示します。

リスト 4. エイリアスを作成する
<?php
include 'listing1.php';
use zebra\DateTime as ZDT;

// Use our own DateTime class:
$dt = new ZDT();

名前空間では、例えばサブ名前空間を作成するなど、ここで触れた以外にもさまざまな操作を行うことができます。公式マニュアルで詳しく探ってください。

トレイト

オブジェクト指向プログラミングは、従来、継承が行われるクラスとオブジェクトの概念に深く根ざしています。つまり、抽象概念に始まり、より具体的な詳細になると複数の子によるサブクラスが続きます。オブジェクト間で一貫した API が必要な場合は、インターフェースの概念が関わってきます。オブジェクトに実装する必要のあるメソッドは、インターフェースで定義することができるからです。その一方で、どのメソッドを存在させる必要があるかを宣言するだけでなく、これらのメソッドの実装を提供する必要があるとしたら、どうなるでしょう?そのような場合に活躍するのが、トレイトです。

PHP 5.4 で追加されたトレイトは、(継承が垂直方向でのコードの再利用であるのとは対照的に) コードを水平方向で再利用するための機能です。この機能は、他の言語では「ミックスイン」とも呼ばれています。いずれにしても、その概念は単純明快です。トレイトあるいはミックスインは、一度にいくつものメソッドを開発する手段となります。おそらく皆さんは、いくつかのオブジェクトが共有しなければならないデータやビジネス・ロジックをフィルタリングして操作するための共通メソッドを使用していることでしょう。それらの共通メソッドをトレイトに保存すれば、任意のクラスで再利用することが可能になります。

リスト 5 に、任意のクラスがイベントをログに記録する一貫した方法として使用できるロギング・メソッドを提供する単純な例を示します。

リスト 5. トレイトを宣言して使用する
<?php
trait logging {
   private static $LOG_ERROR = 100;
   private static $LOG_WARNING = 50;
   private static $LOG_NOTICE = 10;

   protected $log_location = 'output.log';

   protected function log($level, $msg) {

      $output = [];
      $output[] = "Class: ".__CLASS__.' | ';
      $output[] = "Level: {$level} | ";
      $output = array_merge($output, (array)$msg, ["\n"]);
      file_put_contents($this->log_location, $output, FILE_APPEND);
   }
}

class User {
   use logging;

   public function __construct() {
      $this->log(self::$LOG_NOTICE, 'New User Created');
   }
}

class DB {
   use logging;

   private $db = 'localhost:8080';

   public function connect() {
      // ... attempt to connect and fail:
      $this->log(self::$LOG_ERROR, ['Connection Failed-', $this->db]);
   }
}

リスト 5 では、2 行目で trait logging の宣言が始まっています。このトレイトには、メソッドと多数のプロパティー (静的プロパティーを含む) が含まれていることに注意してください。この宣言は、表面的にはクラスの宣言と同じように見えますが、trait キーワードを使用するという点が異なります。

リスト 5 の下のほうで、トレイトを User クラスと DB クラスに統合するために、再び use キーワードを使用しています。それぞれのクラス定義の先頭にある use logging; ディレクティブは、基本的に、logging トレイトのすべてのプロパティーとメソッドをこれらのクラスにネイティブに取り込みます。これにより、各クラスはすべてのロギング・ツールに直接アクセスできるようになるため、これらのツールを個別に実装する必要がなくなります。トレイトの内部でマジック定数 __CLASS__ を使用すると、その時点でこの定数がトレイトを使用したクラスの名前になります。これにより、ログ・メッセージを即座にクラスへとカスタマイズすることができます。

クロージャー (別名、匿名関数)

古いバージョンの PHP では、create_function を使用してプログラムによって関数を作成し、その関数を、関数を渡すための次善策として使用することができました。つまり、関数の名前をストリングとして送信した後、その関数を call_user_funccall_user_func_array で呼び出すという方法です。しかしこの方法には、メソッドやクラスの間で受け渡したり、適切なスコープで変数に保存したりすることが可能な、真の匿名関数の簡潔さがありませんでした。

匿名関数がとてもよく使われているのは JavaScript ですが、ほとんどの PHP プログラマーは、PHP だけでなく JavaScript のことも理解しています。そのため、PHP が進化して匿名関数が採り入れられたのは当然の成り行きでした。PHP 5.3 の時点では、変数を (保管する目的、または渡す目的で) 使えるところであれば、どこででも通常の関数宣言構文を使用できるようになっています。

一例としてリスト 6 に、組み込みソート関数を使用して独自のカスタム・ソート関数を指定する従来の方法を示します。

リスト 6. 関数を渡す従来の方法
<?php
$insurees = [
   'u4937' => ['name' => 'Thomas Smythe', 'age' => 33],
   'u1282' => ['name' => 'Gayle Runecor', 'age' => 25],
   'u9275' => ['name' => 'Sara Pinnicle', 'age' => 57],
   'u2078' => ['name' => 'Delilah Shock', 'age' => 41],
];

function insuree_age_sort($a, $b) {
   if ($a['age'] == $b['age']) { return 0; }
   return ($a['age'] > $b['age']) ? -1 : 1;
}

uasort($insurees, 'insuree_age_sort');

リスト 6 は、多少簡潔さに欠けています。それは、二度と使うことのない関数であっても、同じスコープ内で関数を定義した後に使用する必要があるからです。クロージャーにより、今では 1 つのステップで直接関数を作成して使用できるようになっています。リスト 7 に、この遥かに簡潔なソリューションの例を示します。

リスト 7. 匿名関数を使用してソートする
<?php
uasort($insurees, function ($a, $b) {
   if ($a['age'] == $b['age']) { return 0; }
   return ($a['age'] > $b['age']) ? -1 : 1;
});

上記コードを見たからと言って、このマイナーな使用例だけでクロージャーの機能が有効であるとするのは難しいと思いますが、ここで何が起こっているのか理解してください。uasort() に渡すためにオンザフライで作成するこの関数は、第一級市民の変数です。変数に関数を格納すれば、その関数を別の関数やクラスに渡すことができます。クロージャーと一緒に PHP に追加されたスコープ設定機能を見れば、クロージャーの真価は明らかです。

オーバーロードされた use キーワードを使用すると、関数がアクセスする必要のある現在のスコープ内で特定の変数を指定することができます。このようにすれば、かなり複雑な詳細であっても、毎回関数に渡すことなく、変数という形で関数にアクセスすることで対処することができます。リスト 8 とリスト 9 に、この威力を明らかにする (多少不自然な) 例を記載します。

リスト 8 では、コールバックの中で継承された変数スコープを使用します。

リスト 8. コールバックの中で継承された変数スコープを使用する
<?php
// Find only people over a certain age
$minage = 40;

$over = array_filter($insurees, function($a) use ($minage) {
   return ($a['age'] >= $minage);
});

リスト 9 では、複数の変数と直接呼び出しを含むクロージャーを使用します。

リスト 9. 複数の変数と直接呼び出しを含むクロージャー
<?php
$urls = [
   'training' => '/training',
   'magazine' => '/magazine',
   't-shirt' => '/swag/tshirts',
];

$current = $_SERVER['REQUEST_URI']; // May come from somewhere else

// Helper for links, ignoring links if we are on that page:
$link = function($name) use ($urls, $current) {
   if ($current == $urls[$name]) {
      return $name;
   } else {
      return "<a href=\"{$urls[$name]}\">{$name}</a>";
   }
};
?>
<p>Welcome to our website!  Make sure to check out
   our <?= $link('training') ?> offerings, see the
   latest issue of our <?= $link('magazine'); ?>,
   and don't forget to check out our latest
   <?= $link('t-shirt') ?> designs as well.</p>

JavaScript でクロージャーを使い慣れているとしたら、すでにクロージャーの威力、柔軟性、そしてときには危険な性質もご存知のことでしょう。

ジェネレーター

PHP 5.0 のリリースを機に、SPL (Standard PHP Library) が導入されました。SPL は、コンピューター・サイエンスの特定の問題を解決するための標準化された手段を集めたものです (また、クラス・ファイル自動ローダーなどの拡張可能な機能を提供するという目的もあります)。

SPL に含まれる機能の中に、イテレーターと呼ばれるものがあります。イテレーターは、foreach キーワードを使用して、任意のクラスを配列であるかのようにループ処理するために使用できるインターフェース (および一連の事前にビルドされたクラス) です。この見事な発明によって、リストにできるものであれば、どのリストでも一様にループ処理することが可能になりましたが、イテレーターはかなり複雑なシステムです。これを使用するには、クラスを作成して 4 つのメソッドを定義しなければなりません。標準的な foreach ループの機能は必要であっても、それを実現するためのクラス構造のオーバーヘッドは必要ない場合もあります。

新しいジェネレーター機能では、yield キーワードを使って、値のリストを生成してそれらの値を一度に提供する関数を作成することができます。基本的には、1 つの値を返すのではなく、必要とされる数だけ値を生成します (ジェネレーターを呼び出しているループに値を戻すように設定します)。こうすれば、関数に対して foreach ループを使用することで、その関数が生成する必要があるすべての値を取得することができます。

リスト 10 に、値の範囲を均等に分割して返す、単純な関数の例を記載します。

リスト 10. 範囲を分割するジェネレーター
<?php
function parts($start, $end, $parts) {
   // Find what our actual length is:
   $length = $end - $start;
   do {
      $start += $length / $parts;
      yield $start;
   } while ($start < $end);
}

// Break 5 feet into 3 parts:
foreach (parts(0, 5, 3) as $l) {
   echo $l, " ";
}
echo "\n";

// Break the range 10-90 into 2 parts:

foreach (parts(10, 90, 12) as $l) {
   echo $l, " ";
}
echo "\n";

7 行目では、yield キーワードを使用して魔法をかけています。基本的に、この時点で関数の実行は停止され、生成された値が返されます。関数に対する以降の呼び出しでは、次の yield まで、または関数が終了するまで、前回停止したところから実行が再開されます。

上記の例は明らかに不自然ですが、例えば、データベース・クエリーの実行結果や、XML ファイルの構文解析によって返された結果に対して、この手法を適用することが考えられます。さらに、配列を直接模倣するために yield $key => $value という構文を使用して、値だけではなくキーも生成することができます。リスト 11 に記載する XML ベースの例を見てください。

リスト 11. ジェネレーターを使用して XML を処理する
<?php
$xml = <<<EOXML
<?xml version="1.0" encoding="UTF-8" ?>
<products>
  <books>
    <book isbn="978-1940111001">Mastering the SPL Library</book>
    <book isbn="978-1940111056">Functional Programming in PHP</book>
    <book isbn="978-0981034508">Guide to Date and Time Programming</book>
    <book isbn="0973589825">Guide to PHP Design Patterns</book>
  </books>
</products>
EOXML;

$books = function () use ($xml) {
   $products = simplexml_load_string($xml);
   foreach ($products->books->book as $book) {
      yield $book['isbn'] => (string)$book;
   }
};

foreach ($books() as $isbn => $title) {
   echo "{$isbn}: {$title}\n";
}

その他多数の機能

PHP の新しい機能のすべてを詳しく説明するには、数が多過ぎます。そこで表 1 に、この数年の間に追加されたその他の注目すべき機能を簡単にまとめます。

表 1. その他の新しい PHP 言語の機能
機能バージョン説明
遅延静的束縛5.3親クラスが、その子/継承クラスのいずれかで定義された静的なメソッドまたはプロパティーを呼び出すための機能 (通常の呼び出しとは逆の方向です)。例えば、親クラス内に存在する汎用機能に、その親から継承された子から、構成を取り込むことができます。
Nowdoc 構文5.3ストリングを、そこに含まれる変数が解釈されない、テキストのブロックとして指定する機能。
項の省略 (?:)5.3true 条件が元の比較値にデフォルト設定されるように、標準的な三項演算子の中央の項を省略する機能。例: $result = $value ?: 0;
ジャンプ・ラベル (goto)) 5.3goto 演算子については、一部の人々はこの言語の「進化」とは見なしませんでしたが、ステート・マシンの作成などといった特定のコーディングを PHP 内でより簡単に行えるように、goto 演算子が追加されました。
マジック・メソッド
__callStatic
__invoke
__debugInfo

5.3
5.3
5.6
クラスを設計する際に使用できる選択肢を強力なものへと拡充するために、この 3 つの新しいメソッドが、PHP 5.0 以降で使用可能になった他のマジック・メソッドに追加されました。オーバーロードされた未定義の静的メソッドを使用したり、オブジェクトを関数であるかのように呼び出したり、オブジェクトのデバッグ時に出力する内容を制御したりすることができるようになっています。
常に有効な短縮コードによるエコー出力 (<?=)5.4以前は、PHP で短縮コードを無効にすると、すべてのバリエーションがオフにされました。PHP 5.4 以降では、テンプレート作成で一般に使用される短縮コード <?= が常に使用可能になっています。
配列の短縮構文5.4配列を array(1,2,3) として宣言するのではなく、大括弧を使用して [1,2,3] として宣言することができます。
ビルトイン・ウェブ・サーバー5.4PHP ランタイムに、ビルトイン・ウェブ・サーバーが統合されるようになりました。これにより、Apache や IIS を構成することなく、コードの簡単なテストや開発を遥かに容易に行うことができます。

まとめ

最近の PHP 開発は、昔ながらの手続き型のコードとは様相が変わっています。開発で PHP を使用する割合は高い値を維持し続けています。PHP 7 の登場も間近で、2015年の後半にはリリースが予定されています。

この連載の次回の記事では、変化し続けるパスワード保護への要求について取り上げるとともに、この複雑な要求に対処できるように、PHP が Web 開発者をどのようにサポートしてきたかを説明します。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, Open source
ArticleID=1006833
ArticleTitle=PHP のニュー・フェイス: 一新された PHP
publish-date=05282015