残り一年! PHP4からPHP5への移行

第1回移行前の基礎知識

2007/7/14にPHP4のメンテナンス終了日がアナウンスされ、通常のメンテナンスが2007/12/31まで、セキュリティフィックスが2008/8/8までと発表されました。本連載では、これを踏まえてPHP4.4からPHP5.2への移行について解説します。PHP5には便利な新しい機能が多く追加されていますがここではそれらには触れず、移行についてのみ解説します。PHP4からPHP5に移行する場合に必要のない項目はほとんど解説しません。PHP5の新しい機能についての解説ははじめてのPHP言語プログラミング入門⁠技術評論社)などのPHP5の入門書を参照してください。

特に記述がない場合、PHP4はPHP4.4.x、PHP5はPHP5.2.xを意味します。

PHP4とPHP5の違い

PHP4とPHP5は言語仕様が異なる言語ですが、PHP5はPHP4の上位互換言語です。PHP4とPHP5両方で動作するコードを記述することは難しくありませんが、一部の非互換部分が移行の障害になる場合があります。

主なPHP4とPHP5の違い

  • オブジェクト変数がオブジェクトへのハンドルとなった
  • オブジェクト指向プログラミングサポートが向上した
  • XML関連モジュールが入れ替えられた
  • PECL[1]に移動されたり削除されたモジュールがある

基本的な違いで困る場合もあります。その一例が新しい予約語です。PHP5はオブジェクト思考プログラミング機能が拡張されたため、abstract, public, private, protecedなどが予約語として追加されています。

最終回の「セキュリティ関連の違い」でも一部紹介しますが、PHP4はメンテナンスされているとはいえPHP5に比べて積極的にメンテナンスされてきませんでした。このため、セキュリティ上重要な意味を持つ機能追加であっても、PHP4には追加されていない場合が多くありました。

この記事はPHP4からPHP5への移行をテーマに記述していますが、移行後により安全に利用できるようセキュリティ上重要な変更を最後に解説します。

PHP4とPHP5のバージョン

執筆時点でメンテナンス中のPHP4のバージョンは4.4.7, PHP5は5.2.3です。このバージョン以外のPHPはセキュリティ上の問題も未修整と考えてかまいません。

PHPのバージョンは最初のバージョン番号がメジャーバージョン番号です。このバージョン番号が変わると言語エンジンレベルでの仕様が大幅に変わります。

2つ目のバージョン番号はマイナーバージョン番号です。マイナーバージョンが変わると新しい機能や関数が追加されたり、下位互換性のない修正が行われます。

3つ目のバージョン番号はバグフィックスバージョン番号です。このバージョン番号が変わるとバグが修正されます。基本的には互換性がない変更は行わないことになっています。しかし、エラーとすべき状態でエラーを発生させていなかったのでエラーを発生するよう追加されるなど、プログラムの動作に影響する修正が行われることもあります。

PHPはPHP4、PHP5ともに、新しいマイナーバージョンがリリースされると以前のリリースはメンテナンス対象外になります。つまり、PHP4.4、PHP5.2がリリースされている現在は、PHP4.3、PHP5.1以下はメンテナンスされていません。新しいマイナーバージョンにはセキュリティフィックスが含まれていることがほとんどです。新しいマイナーバージョンがリリースされた場合、できるだけ早くバージョンアップする必要があります。

筆者は新しいマイナーバージョンがリリースされた場合、移行期間の確保のために直前のバージョンについて最低1年はセキュリティフィックスはリリースすべきと考えていますが、残念ながらメンテナンスされていません。Linuxなどのディストリビュータは独自に古いバージョンのメンテナンスも行っていますが、十分とは言えない状態です。安全性が重要なサイトはアプリケーションがPHPの脆弱性によって危険な状態でないか十分注意する必要があります。PHP5.2系を利用するほうがより安全です。

PHPのモジュールとバージョン間の互換性

PHPのモジュールは内部的にZendエンジン(Zend Module API番号、Zend Extension API番号)とPHP本体(PHP API番号)のAPIバージョン番号を保持しています。さらにコンパイル時のデバッグオプションの有効・無効、Zendスレッドセーフティ(ZTSモード)の有効・無効もモジュール内の情報として保持しています。PHP4からPHP5ではZendエンジンのバージョン、PHP本体のAPIバージョン番号も更新されています。

PHPはモジュールをロードする場合、これらのバージョン番号とコンパイルオプションが一致した場合のみモジュールをロードします。つまりPHP4用にコンパイルしたモジュールはコンパイルし直さないとPHP5では利用できません。

ソースが公開されているPECLモジュールやサードパーティーモジュールでPHP5をサポートしていれば、モジュールをPHP5用にコンパイルすればPHP5でも利用できます。しかし、バイナリのみ提供されている商用モジュールなどはPHP5用のモジュールをベンダーから入手しなければなりません。

PHPをソースからビルドしてインストールしたり、PHPパッケージの開発用パッケージ(RPMを利用するの多くのLinuxディストリビューションの場合はphp-develパッケージ)をインストールしている場合は、モジュールのソースディレクトリから以下のようなコマンドでPHPモジュールをコンパイルしてインストールできます。

$ /path/to/phpize # phpizeコマンドは必ずターゲットとなるPHPバージョンに付属のphpizeコマンドを使用する
$ ./configure # --with-module_name または --enable-module_nameなどのオプションが必要な場合もある。詳しくは./configure --helpで
$ make # コンパイルする
$ make install # モジュールディレクトリにインストールする

あとはphp.iniを変更し、インストールしたモジュールをロードするように設定してWebサーバを再起動すれば、新しくインストールしたモジュールが利用できるようになります。

PHPバージョンの判別

順次移行するために、PHP4とPHP5の両方でプログラムが同じように動作するようバージョンごとに違うコードを実行したい場合は、PHP_VERSION定数とversion_compare関数を利用します。システム定義定数のPHP_VERSIONにPHPのバージョン番号が定義されおり、version_compare関数で比較できます。

例:PHP 4.4またはPHP 5.2以上で実行
<?php
if (version_compare(PHP_VERSION, '4.4', '>') && version_compare(PHP_VERSION, '5.0', '<')) {
   require_once('code_for_php4.php');
} else if (version_compare(PHP_VERSION, '5.2', '>=')) {
   require_once('code_for_php5.php');
} else {
   // それ以外は実行停止
   die('You need PHP 5.2 or later');
}
?>

このようなコードをアプリケーションの初期化ファイルなどに入れておけば、誤って古いシステムで実行されることを防げます。

PHPのコマンドラインバイナリとCGIバイナリ

PHPのコマンドラインバイナリ(CLIバイナリ)とCGI実行用のCGIバイナリの名称は、CLIバイナリが導入された際の混乱で様々な名前がつけられています。PHPプロジェクトのソースディストリビューションをデフォルト状態でコンパイルすると、⁠php」バイナリはCLIバイナリ、⁠php-cgi」がCGIバイナリになるよう設定されています。しかし、PHP環境をコンパイルしてユーザに提供しているディストリビュータには、⁠php」バイナリがCGI、⁠php-cli」がCLIバイナリとなるように調整していることがあります[2]⁠。

PHP5パッケージは「php」がCLI、⁠php-cgi」がCGIバイナリとなるようにパッケージングしているディストリビュータが多いと思います。バッチ処理などをPHPで実行している場合、⁠php」バイナリが意図しているバイナリであるか確認する必要がある場合があります。

例:XAMPP for Windowsのphp.exeのバージョン確認
C:\xampp\php\php4>php.exe -v
PHP 4.4.7 (cgi-fcgi) (built: May  4 2007 13:30:00)
Copyright (c) 1997-2007 The PHP Group
Zend Engine v1.3.0, Copyright (c) 1998-2004 Zend Technologies
    with Zend Extension Manager v1.2.0, Copyright (c) 2003-2007, by Zend Technologies
    with Zend Optimizer v3.2.4, Copyright (c) 1998-2007, by Zend Technologies

この実行例では

PHP 4.4.7 (cgi-fcgi)

と、php.exeがPHP4のCGIバイナリであることが分かります。

新しい予約語

PHP4と比べると、PHP5には以下の予約語が追加されました。予約語は関数名や定数名に利用できません。利用されている場合はParse Errorが発生します。エラーが発生する場所は定義された場所ではなく、利用された場所で発生します。

例:予約語の定数名
<?php
define('interface', 123);
echo interface; // ここでエラーが発生
?>

PHP5からの予約語一覧

exception final php_user_filter interface implements public private protected abstract clone try catch

予約語を定数名・関数名に利用している場合、名前を変えるしか方法はありません。

定義済み定数

PHP5には新しい定義済み定数が追加されています。モジュールも定数を定義します。定義済み定数の種類や数は読み込まれたモジュールの種類や数によって異なります。ここでは一部のPHP4とPHP5の定義済み定数を紹介します。実際にどのような定数が定義されているかは自分の環境で確かめてください。ここで利用しているphp4/php5コマンドは、それぞれPHP4、PHP5のCLI(コマンドラインバイナリ)を呼び出すシェルスクリプトになっています。

PHP4
[yohgaki@dev php4-php5-migration]$ ./php4 -r "print_r(get_defined_constants());"
Array
(
    [E_ERROR] => 1
    [E_WARNING] => 2
    [E_PARSE] => 4
    [E_NOTICE] => 8
    [E_CORE_ERROR] => 16
    [E_CORE_WARNING] => 32
    [E_COMPILE_ERROR] => 64
    [E_COMPILE_WARNING] => 128
    [E_USER_ERROR] => 256
    [E_USER_WARNING] => 512
    [E_USER_NOTICE] => 1024
    [E_ALL] => 2047
以下省略
PHP5
[yohgaki@dev php4-php5-migration]$ ./php5 -r "print_r(get_defined_constants());"
Array
(
    [E_ERROR] => 1
    [E_RECOVERABLE_ERROR] => 4096 ←新しい定数
    [E_WARNING] => 2
    [E_PARSE] => 4
    [E_NOTICE] => 8
    [E_STRICT] => 2048 ←新しい定数
    [E_CORE_ERROR] => 16
    [E_CORE_WARNING] => 32
    [E_COMPILE_ERROR] => 64
    [E_COMPILE_WARNING] => 128
    [E_USER_ERROR] => 256
    [E_USER_WARNING] => 512
    [E_USER_NOTICE] => 1024
    [E_ALL] => 6143
以下省略

定数名の衝突はまれとは思いますが、もし同じ名前を利用している場合、プログラム中の定数名を変更しなければなりません。

この例でもPHP5には新しくE_RECOVERBLE_ERRORが追加され、E_ALLの値が変更され、エラー処理関連で重要な変更が行われていることがわかります。これは後の項目で詳しく解説します。

無効な文字列オフセットアクセス

PHP5から無効な文字列オフセットへのアクセスはE_ERRRORエラーが発生します。

例:無効な文字列オフセットへのアクセス
<?php
$str = 'abc';
unset( $str{1} );
echo $str;
?>
例:PHP5で実行
[yohgaki@dev php4-php5-migration]$ ./php5 invalid_str_offset.php

Fatal error: Cannot unset string offsets in /home/yohgaki/php4-php5-migration/invalid_str_offset.php on line 3

PHP4では問題なく実行できていたスクリプトが途中で実行を停止する場合もあるので、注意が必要です。

エラー処理

PHP5には新しくE_STRICTエラーとE_RECOVERBLE_ERRORエラーの2つエラーレベルが追加されています。

E_STRICTエラーは構文上問題はないが推奨できない使い方や、プログラマが意図していない処理である場合に発生するエラーです。PHP5の以前のバージョンではclass定義中のvar宣言でもE_STRICTエラーが発生したため、PHP4用に書かれたコードに対してE_SCRICTエラーを有効にして利用することは不可能でした。PHP5.1.3からvar宣言でE_STRICTエラーは発生しないように変更されました。

E_RECOVERBLE_ERRORはPHP5.2.0から追加されたエラーレベルです。このエラーレベルはE_ERRORに設定されているエラーの中でも、プログラムの実行を中止しなくても言語エンジンに不整合が発生しないエラーの場合に発生するエラーです。このエラーはset_error_handlerで設定したエラーハンドラで処理できるので、エラーページを表示するなど行儀よくプログラムの実行を終了できるようになりました。筆者はPHPにパッチを当ててE_ERRORもエラーハンドラで終了させるようにしていましたが、このパッチが必要なケースは少なくなると思います。

新しいエラーレベルが追加されたと同時にE_ALLの値も変更されました。PHP4のE_ALLは2047でしたが、6413に変更されています。新しいE_ALLはE_STRICTを除くすべてのエラーレベルが有効に設定されます(PHP6ではE_STRICTも含めて有効となる予定⁠⁠。

E_STRICTを有効にするとプログラミングエラーを発見できる場合もあります。基本的にはE_STRICTも有効に設定する方がお勧めです。

例:php.ini設定例
error_reporting = E_ALL | E_STRICT

httpd.confなどでerror_reportingを設定する場合、PHPの定数は利用できません。このため数値で設定します。PHP 5.2と同じくE_ALLに設定するには

例:httpd.confや.htaccessで設定する場合
php_admin_value error_reporting 6143

と設定しなければなりません。E_STRICTも有効にする場合は

php_admin_value error_reporting 8191

とします。

アプリケーションが動作するためにはエラーハンドラの書き換えは必要ないです。システムのポリシーとして全てのエラーは補足し記録している場合などは、エラーハンドラの書き換えが必要になります。

まとめ

今回はPHP5に移行に必要な基礎知識を解説しました。

次回はPHP4.4未満のユーザ向け情報とオブジェクトの仕様変更の一部を解説します。

おすすめ記事

記事・ニュース一覧