PHP 7.0の概要・新機能・互換性

待望のPHP 7.0が遂にリリースされました。PHP 7.0は高性能だと聞いているが、バージョンアップが大変では?と疑問に思われている方も多いと思います。簡単に概要/新機能/互換性を紹介します。

まずはじめにPHP 7.0の特徴、PHPのバージョンとサポート期間についておさらいしましょう。

 

PHP 7.0の特徴

  1. 速い&軽量
  2. 直感的なプログラミングをサポート
  3. 高いPHPプログラム互換性
  4. 大幅に変更された内部API

PHP 7.0は開発時から「速い」と言われています。 実際、言語の基本機能は3倍以上速くなっています。更にメモリ使用量も削減されています。後述するPHP構文/機能の改善によりプログラマが意図した通りのプログラミングも行い易くなっています。メジャーバージョンアップですが従来のPHPプログラムとは高い互換性を維持しています。高速化に必須だったPHP内部構造変更により、モジュール作成などに利用するC言語APIは大幅に変更されました。このため、PHP 7.0用のモジュールでないと利用できません。

 

PHPバージョン

PHPのリリースバージョン番号は概ねセマンティック バージョンニングをサポートしています。

  1. APIの変更に互換性のない場合はメジャーバージョンを、
  2. 後方互換性があり機能性を追加した場合はマイナーバージョンを、
  3. 後方互換性を伴うバグ修正をした場合はパッチバージョンを上げます。

PHP 7.0はPHP 5.6からのバージョンアップなので「APIの変更に互換性のメジャーバージョンアップ」に該当します。セマンテックバージョニングをご存知の方なら「PHP 7.0にアップグレードするのはかなり大変だろう」と考えてしまうかも知れません。しかし、安心してください。PHP 7.0は内部構造が大幅に見直された為、C言語で作るモジュール作る場合に必要なAPIは大幅に変更され互換性がありませんが、PHPプログラムとの互換性は非常に高いレベルを維持しています。PHPプログラムはほとんど変更無しでも従来通り動作する場合が多いです。

 

各PHPバージョンのサポート期間

現在サポートされているPHPは7.0/5.6(バグフィックス含む)とPHP 5.5(セキュリティフィックスのみ)になります。PHP 5.4は9月にメンテナンスが終了しています。PHPのリリースとサポートされるバージョンの情報はPHPプロジェクトのWebページで確認できます。執筆時点でのサポート期間は以下のようになっています。

BranchInitial ReleaseActive Support UntilSecurity Support Until
5.5 20 Jun 2013 2 years, 5 months ago 10 Jul 2015 5 months ago 10 Jul 2016 in 6 months
5.6 28 Aug 2014 1 year, 3 months ago 28 Aug 2016 in 8 months 28 Aug 2017 in 1 year, 8 months
7.0 3 Dec 2015 14 days ago 3 Dec 2017 in 1 year, 11 months 3 Dec 2018 in 2 years, 11 months

PHPのリリースとメンテナンスの基本ポリシーは、リリース後2年間のバグフィックス(基本的に毎月)と+1年間のセキュリティフィックス(基本的に毎月)となっています。

毎月のバグ・セキュリティフィックスに加え、最低限でも3年に一回のマイナーまたはメジャーバージョンアップが必要になります。毎月のバグ/セキュリティフィックス、数年毎のマイナー/メジャーバージョンアップを行うのは大変です。弊社ではPHPのバージョンアップ検証の工数を大幅に削減するPROVE for PHPを販売しています。是非一度ご覧ください。

 

PHP 7.0の新機能

PHP 7.0の目玉はなんと言っても「高速」性にありますが、新しい機能も追加されています。PHPの新機能はソースコード中のUPGRADINGファイルに記載されています。ここでは主な新機能を紹介します。

統一変数文法(ユニフォーム バリアブル シンタックス)

あまり意識しなかったかも知れませんが、PHP 7.0以前ではオブジェクトや配列を表したPHP変数の解釈は統一されていませんでした。このため一般的に、変数参照が左から右へと解釈される、と思っていると異る動作をしました。PHP 7.0からは統一して左から右に解釈されるようになりました。これにより直感的なプログラミングが可能になりました。

$$foo['bar']['baz'] // interpreted as ($$foo)['bar']['baz']
$foo->$bar['baz']   // interpreted as ($foo->$bar)['baz']
$foo->$bar['baz']() // interpreted as ($foo->$bar)['baz']()
Foo::$bar['baz']()  // interpreted as (Foo::$bar)['baz']()

統一変数文法により、あの場合はこう、その場合はこう、という解りづらいルールを覚える必要がなくなりました。しかし、文法が変る、ということはプログラムの動作が変ることを意味します。

例えば、

global $$foo->bar;

は、((global $)$foo)->bar)のような形で左から右に評価されます。従来通りに解釈させる為には

global ${$foo->bar};

と明示的に{}を記述します。このようなケース/コード はあまり多くないのでPHP 7.0にアップグレードしても修正が必要な箇所は多くないはずです。

UPGRADINGファイルには記載されていませんが、統一変数文法は別のメリットがあります。今までのPHPはJavaScriptのようにクロージャーを即時呼び出し(クロージャーを定義して直ぐ呼び出す)が行なえませんでした。統一変数文法によりPHP 7.0からこれが可能になります。例えば、

 <?php
(function() {
echo 'hello';
})();


などと記述すると定義されたクロージャーがその場で呼び出されます。(PHP 5.6などでは文法エラー)JavaScriptなどでは良く利用されるコーディング方法ですが、PHPでは利用できなかったので歯痒かった方も多いと思います。PHP 7.0から可能になります!

参考リンク:

 

list()文法の変更

従来のlist()はあまり納得できない動作をしていました。

<?php
list($array[], $array[], $array[]) = [1, 2, 3];
var_dump($array);
?>

これを実行すると

array(3) {
[0]=>
int(3)
[1]=>
int(2)
[2]=>
int(1)
}

と順番が逆になっていました。PHP 7.0からは直感と同じく

array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}

となります。

そもそもlist()は配列用の機能なので文字列に使用するのは乱用と言えますが、従来は

$string = "xy";
list($x, $y) = $string;

として文字列を文字に分解することができました。PHP 7.0からは禁止されます。list()を文字列分解に利用するコードはあまりないと思いますが、メンテナンス性の高いコードを書くには、その機能が意図している機能として利用すると将来問題となる可能性が少なくなります。

参考リンク:

 

foreachの改良

配列などの要素に操作を行う場合に便利なforeachですが、少し変わった使い方をすると予想しないような動作をしていました。特に参照(例: &$var)を利用した場合、コードから直感する意図通りに動作しませんでした。PHP 7.0のforeachは参照を利用した場合の動作が直感と同じようになりました。例えば、以下のようになります。

$array = [0, 1, 2];
  foreach ($array as &$val) {
  var_dump(current($array));
}

PHP 5.6

int(1)
int(2)
bool(false)

PHP 7.0

int(0)
int(0)
int(0)

プログラマはforeachループ内の

  var_dump(current($array));

の$array内部イテレーターインデックスは影響されない、と期待しますが古いPHPでは影響される、などのケースが多数ありました。PHP 7.0からはほぼプログラマの直感と同じ動作をします。

参考リンク:

 

パラメータの取り扱い

PHP 7.0からパラメータの取り扱いが変わりました。

  • 同じパラメータ名の禁止
  • func_get_arg()で取得した値が現在の値となる(例外の場合も同じ)

この変更で困ることは普通はないでしょう。

参考リンク:

 

整数値の取り扱い

整数の不整合な記述や操作にたいしてエラーを発生するようになりました。

  • 無効な8進数リテラルはシンタックスエラー
  • 負のビットシフトはArithmetifcError

ビット幅を超えるシフトはオーバーフローしなくなりました。詳しくは実際の動作を確認してみてください。

参考リンク:

 

文字列の取り扱い

整数値の取り扱いとも関連しています。文字列中の16進数が整数として取り扱われ、思わぬ結果になった経験をお持ちの方も居ると思います。PHP 7.0からはそのような事はありません。文字列中の16進数表記も文字列として取り扱われます。

var_dump("0x123" == "291"); // bool(false) (previously true)
var_dump(is_numeric("0x123")); // bool(false) (previously true)
var_dump("0xe" + "0x1"); // int(0) (previously 16)

文字列のユニコードサポートも強化されました。"\u"で始まる場合、ユニコードとして解釈されます。指定方法には

"\u{ユニコード値}"と"\uユニコード値"

の2種類({}がある場合と無い場合)があります。新しい"\u{ユニコード値}"の形式として解釈され、エラーとなってしまう場合は、追加の"\"を追加してエスケープできます。

"\\u{ユニコード値}"

この場合、ユニコードの文字として解釈されません。

参考リンク:

 

エラー動作

エラー処理の動作/仕様が変わりました。

  • Throwableを実装するExceptionクラスとErrorクラスが実装された
  • 致命的なエラーとなる場合にErrorをthrowし、try/catchブロックでは無視される
  • パース(Parse)エラーがErrorクラスを拡張したParseError例外を発生させる。(evalでパースエラーをキャッチ可能になった)
  • 内部クラスのコンストラクターエラーで必ず例外を返す。(以前はNULLや利用できないオブジェクトを返していた)
  • 幾つかのE_STRICTエラーのレベルが変更された。

参考リンク:

 

その他の言語仕様変更

  • 非スタティックメソッドのスタティックコールの禁止
  • 準予約語(予約語と考えて構いません)の追加(bool, int, float, string, null, false, true, resource, object, mixed, numericはクラス名などに利用できません)
  • yeildの()はオプション(echoやprint、include/requireと同じです)
  • ASPタグとSCRIPTタグの廃止
  • newの結果のリファレンス代入を禁止
  • 互換性のない$thisの利用を禁止
  • INIファイルの#コメントを廃止
  • $HTTP_RAW_POST_DATAの廃止(php://inputストリームで代替)

参考リンク:

 

 

重要な標準ライブラリの変更

  • 切り詰めを発生したsubstr()が空文字列を返す。
  • call_user_method()とcall_user_method_array()の廃止。
  • 出力バッファ内で出力バッファを作った場合のエラーがE_ERRORからE_RECOVERBLE_ERRORに変更。
  • 内部ソートアルゴリズムを改善し、ほぼ安定ソート(順序を維持したソート)アルゴリズムに変更。
  • fpm-fcgi SAPIからdl()を削除。
  • 空のクッキー名の場合にE_WARNINGを発生させ、空のset-cookieヘッダー送信を抑制。

 

その他の重要なモジュール関数の変更

注意:これは完全なリストではありません。

  • CurlのCURLOPT_SAFE_UPLOADオプションの廃止。
  • mktime()とgmmktime()の$is_dstオプションの廃止。
  • datefmt_set_timezone_id()とIntlDateFormatter::setTimeZoneID()エイリアスの廃止。
  • mcrypt_generic_end()エイリアスの廃止。
  • mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb(),mcrypt_ofb()の廃止。
  • opcache.load_commentsオプションの廃止。
  • OpenSSLのrsa_key_size,CN_match, SNI_server_nameコンテクストオプションの廃止。
  • PCREの"/e"モディファイアの廃止。
  • PDO_pgsqlのPGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENTオプションの廃止。
  • setlocale()の文字列オプション指定の廃止。
  • set_magic_quotes_runtime()、magic_quotes_runtime()の削除。
  • JSONのRFC 7159違反のフォーマットの禁止。
  • json_decode()の結果が空文字、NULL、FALSEとなる場合に構文エラー。
  • set_socket_blocking()の廃止。
  • xsl.security_prefs INIオプションの廃止。

 

新機能

PHP 7.0には多くの新機能が実装されています。その一部を紹介します。

注意:これは完全なリストではありません。

基本機能

Opcache

  • ファイルキャッシュサポートの追加。
  • LinuxカーネルなどのHUGEページサポートの追加。

OpenSSL

  • alpn_protocols SSLコンテクストオプションの追加。

Reflection

  • ReflectionGenerator、ReflectionTypeクラスの追加。

 

廃止された基本機能

  • PHP4形式のコンストラクター(クラス名と同じ名前のコンストラクター)の廃止。
  • 非スタティックメソッドのスタティックコール。

 

関数/クラスの変更/追加

  • unserialize()の安全化オプションを追加し、許可するクラス名を指定をサポート。
    https://wiki.php.net/rfc/secure_unserialize
  • array_column()へオブジェクトサポートを追加。
  • dirname()へレベルオプションを追加。(dirname(dirname(__DIR__)) とdirname(__DIR__, 2)は同じ)
  • session_start()のパラメータとしてセッション設定を追加。読み込みのみ行うreadonlyオプションも利用可能。セッション変数に変更が無い場合に書き込みを省略するlazy_writeオプションも利用可能。

 

  • gmp_random_seed()の追加。
  • preg_replace_callback_array()の追加。
    https://wiki.php.net/rfc/preg_replace_callback_array
  • intdiv()の追加。
  • error_clear_last()の追加
  • ZipArchive::setCompressionIndex()、ZipArchive::setCompressionName()の追加。
  • ReflectionGenerator、ReflectionTypeクラスの追加

 

廃止されたSAPI(サーバーAPI)と拡張モジュール

多くのSAPIが廃止されていますが、メンテナンスされていない物、一般的には利用されていなかった物ばかりです。普通のユーザーには影響はありません。mysqlモジュールはメンテナンスされていませんでした。mysqliモジュールかPDOを利用してください。ereg関数は非バイナリセーフであり、基本的にバイナリセーフなPHP変数と一緒に利用するとセキュリティ問題の原因になるため廃止されました。

SAPI

  • sapi/aolserver
  • sapi/apache
  • sapi/apache_hooks
  • sapi/apache2filter
  • sapi/caudium
  • sapi/continuity
  • sapi/isapi
  • sapi/milter
  • sapi/nsapi
  • sapi/phttpd
  • sapi/pi3web
  • sapi/roxen
  • sapi/thttpd
  • sapi/tux
  • sapi/webjames

拡張モジュール

  • ext/mssql
  • ext/mysql
  • ext/sybase_ct
  • ext/ereg

Mhashは独立したモジュールではなくなりました。

 

新たに定義された主要な定数

  • PHP_INT_MINの追加。PHP_INT_MAXは以前から定義されている。
  • PCRE関数でのPREG_JIT_STACKLIMIT_ERROR定数を追加。JITのスタック制限を越えた場合に設定。

 

その他の変更

  • 浮動小数点型のNaN(数値以外)、INF(範囲を越えた値)を整数にキャストした場合、常に0とする。以前はプラットフォームによりキャスト結果が異なった。
  • 非オブジェクトに対する呼び出しがE_ERRORからE_RECOVERBLE_ERRORに変更され、エラーを処理/キャッチ可能となった。

 

PHP 7.0の互換性

主要な新機能や変更点を列挙すると「これだけ違って大丈夫なのか?」と思うかも知れません。しかし、廃止されたSAPIやモジュールは何年も前からメンテナンスが停止しており実質的に使えなかった物や長らく非推奨とされてきた物ばかりです。PHP 7.0ではPHP 5.xで動作していたコードの多くはそのまま動作します。互換性の面でいうとPHP 5.2からPHP 5.3へのアップグレードの方が問題は多かったと言えます。現在サポートされるPHP 5.5/5.6や5.3からPHP 7.0へのアップグレードは比較的容易です。

弊社が販売しているPHPバージョンアップテストツールであるPROVE for PHPも来年夏頃にPHP 7.0に対応予定です。PROVE for PHPを使えばバージョンアップ前後の動作の違いを内部レベルで検証できるので、より自信を持ってバージョンアップを行えます。

バージョンアップを計画する場合、最も注意が必要な点は拡張モジュールがPHP 7.0をサポートしているかどうかです。PHP 7は内部構造が大幅に変更されているため、PHP 7用のモジュールでないとコンパイルできません。サードパーティーモジュールに限らず、PECLモジュールでもPHP 7に対応していないモジュールが多数あります。これらのモジュールを利用していて、利用を中止できない場合はモジュールがPHP 7に対応するまで待つか、自分で対応させるしかありません。

PHPモジュールのPHP 7対応はPHP内部をよく知っている開発者であればそれほど困難ではありません。弊社にご相談いただくことも可能です。

 

まとめ

PHP 7.0の目玉はなんと言ってもその速度です。PHP 7.0とOpcacheを利用した場合、純粋なVM型のHHVMと同等の性能が期待できます。PHPプロジェクトによるPHPなので互換性の面でもHHVMに比べ優位です。

PHPのリリースサイクルからバージョンアップは欠かせません。バグフィックス/セキュリティフィックスリリースは毎月リリースされます。マイナー/メジャーバージョンアップも最低3年に一回は行う必要があります。テストコードを充実させたり、PROVE for PHPのようなツールを使った検証など、バージョンアップを容易に行える仕組みを整る必要があります。PHPのバージョンアップが楽しくなる、そんなツールにPROVE for PHPを育てるつもりです。