既存アプリをPHP7へ移行する前にするべき6つのこと

PHP 7.0.0-RC6 がリリースされ、PHP7の正式リリースが迫っています(予定では2015/11/12)。今日は、既存アプリのPHP7への移行について整理しておきます。

(2015-12-04 追記) 12/3(日本時間では12/4の早朝)にPHP 7.0.0が 正式にリリース されました。

既存アプリをPHP7に移行する前には以下を実施するとよいです。

  1. テスト環境を構築する
  2. 拡張モジュールの対応状況を調べる
  3. ライブラリなどの対応状況を調べる
  4. 変更点に関するドキュメントを読む
  5. php7ccをかける
  6. コードを修正しテストする

6つあげてますが、結局はPHP7で「テストする」ということに尽きます。

テスト環境を構築する

PHP7の実行環境がなければ始まりません。Vagrantなどでテスト環境を構築できます。探せば色々あると思いますが、1つだけ紹介するとすれば、Rasmusさんのphp7devでしょうか。

参考:PHP7のテスト環境を構築するための少しだけ長い道のり

(2015-11-30 追記) 「PHP7の開発環境をVagrant+CentOS7上に30分で構築する」を書きました。

また、CentOS環境がすでにある場合は、RemiのRPMがすでにありますので、それを使うのが簡単でしょう。

テスト環境が用意できたら、PHPのバージョンを確認しましょう。

vagrant@php7dev:~/php-src$ php -v
PHP 7.0.1-dev (cli) (built: Oct 29 2015 10:14:58) ( NTS )
Copyright (c) 1997-2015 The PHP Group
Zend Engine v3.0.0-dev, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies

なお、本番環境の移行の前には、本番環境と同じ環境のステージング環境でテストしてください。

拡張モジュールの対応状況を調べる

PHP5用の拡張モジュール(エクステンション)はPHP7では動作しません。

PHP本体に含まれない拡張モジュールを使っている場合は、対応状況を確認する必要があります。

対応状況をまとめているプロジェクトが以下にあります。

この拡張モジュールがPHP7対応しているかどうかが最大の問題かも知れません。

ここに引っかかる例として、Phalconなどの拡張モジュールのフレームワークがあります。

Phalconについては、2.x系はZephirという拡張モジュールのコードを生成する言語で作成されており、ZephirがPHP7対応する必要があります。

ZephirのPHP7対応は初期段階であり、まだ完成はしていません。

(2015-12-10 追記) PhalconのPHP7への対応状況は

を参照してください。

ライブラリなどの対応状況を調べる

(PHPで書かれた)ライブラリやフレームワークがPHP7に対応していなければ、

  1. (あれば)対応したバージョンにアップグレードする
  2. 自分で対応させる
  3. 別の対応したライブラリに入れ替える

のいずれかを選択する必要があります。

フレームワークの対応状況はいまいちわかりづらいものもありますが。

フレームワーク バージョン 状況
BEAR.Sunday 1.0 対応済み
CakePHP 3.1.5 対応(CakePHP 3.1.5 Released
2.7以降 対応?ただし、Stringクラスを使っている場合はCakeTextクラスに置換が必要
CodeIgniter 3.0 対応
FuelPHP 1.8-dev(1.7.4以降) 対応。ただし、Errorクラスをカスタマイズしている場合はErrorhandlerクラスに変更が必要
Symfony メンテされているバージョン 対応済み(Symfony achieves 100% PHP7 compatibility
Yii 2.0 対応(PHP 7 released
1.1.17以降 対応(PHP 7 released

なお、PEARについては、1.10.0で対応しています。

ライブラリやフレームワークが対応しているからといって安心してはいけません。問題はアプリが対応しているかどうかです。

変更点に関するドキュメントを読む

まず読むべきはPHPマニュアルです。全部読みましょう。

既存アプリで結構引っかかりそうなのがクラス名の衝突です。名前空間を使ってないと衝突の可能性があり、衝突したらアプリを修正するしかありません。以下が使えないクラス名の一覧です。

使えなくなったクラス名

  • bool
  • int
  • float
  • string
  • NULL
  • TRUE
  • FALSE

将来使えなくなるクラス名

  • resource
  • object
  • mixed
  • numeric

新しいクラス名(つまりユーザ定義できない)

  • IntlChar
  • ReflectionGenerator
  • ReflectionType
  • SessionUpdateTimestampHandlerInterface
  • Throwable
  • Error
  • TypeError
  • ParseError
  • AssertionError
  • ArithmeticError
  • DivisionByZeroError

次に読むべきは、PHP 7.0 UPGRADE NOTESです。PHPマニュアルよりも細かいことが記載されています。必要に応じて読みましょう。

php7ccをかける

次にPHP 7 Compatibility Checkerをかけます。

(2015-12-04 追記) php7cc以外にも「PHP 7 Migration Assistant Report (MAR)」というのもありました。

ここでは、php7ccをComposerからインストールします。

$ composer global require sstalle/php7cc

以下のように実行します。--extensionsオプションで拡張子を指定できます。

$ php7cc --extensions=php,ini fuel/core/

除外したいフォルダがある場合は、--exceptオプションで指定します。

$ php7cc --except=vendor --except=core/tests fuel/

上記では、fuel/vendorfuel/core/testsフォルダが除外されます。

例えば、以下のようなレポートが出力されます。

File: /home/vagrant/fuel-1.8-dev/fuel/core/vendor/phpseclib/Net/SSH2.php
> Line 1734: Function argument(s) returned by "func_get_args" might have been modified
    func_get_args();


...


File: /home/vagrant/fuel-1.8-dev/fuel/core/classes/database/mysql/result.php
> Line 23: Removed function "mysql_num_rows" called
    mysql_num_rows($result);
> Line 30: Removed function "mysql_free_result" called
    mysql_free_result($this->_result);
> Line 36: Removed function "mysql_data_seek" called
    mysql_data_seek($this->_result, $offset);
> Line 62: Removed function "mysql_fetch_object" called
    mysql_fetch_object($this->_result);
> Line 67: Removed function "mysql_fetch_object" called
    mysql_fetch_object($this->_result, $this->_as_object);
> Line 72: Removed function "mysql_fetch_assoc" called
    mysql_fetch_assoc($this->_result);


...


File: /home/vagrant/fuel-1.8-dev/fuel/core/classes/database/query/builder/insert.php
> Line 96: Function argument(s) returned by "func_get_args" might have been modified
    func_get_args();


...


File: /home/vagrant/fuel-1.8-dev/fuel/core/classes/session.php
> Line 72: Check that callbacks that are passed to "session_set_save_handler" and return false or -1 (if any) operate correctly
    session_set_save_handler(function ($savePath, $sessionName) {
    }, function () {
    }, function ($sessionId) {
        $_SESSION = \Session::get();
        $_SESSION['__org'] = $_SESSION;
    }, function ($sessionId, $data) {
        $org = isset($_SESSION['__org']) ? $_SESSION['__org'] : array();
        unset($_SESSION['__org']);
        if ($remove = array_diff_key($org, $_SESSION)) {
            \Session::delete(array_keys($remove));
        }
        empty($_SESSION) or \Session::set($_SESSION);
    }, function ($sessionId) {
        \Session::destroy();
    }, function ($lifetime) {
    });


File: /home/vagrant/fuel-1.8-dev/fuel/packages/orm/classes/model/nestedset.php
> Line 812: Possible array element creation during by-reference assignment
    $tracker[$index] =& $tree[$this->{$pk}];
> Line 867: Possible array element creation during by-reference assignment
    $tracker[$index + 1] =& $tracker[$index]->_custom_data[$children][$treenode->{$pk}];
> Line 871: Possible array element creation during by-reference assignment
    $tracker[$index + 1] =& $tracker[$index][$children][$treenode->{$pk}];

Checked 375 files in 26.485 seconds

コードを修正しテストする

php7ccで報告された点を確認しつつ、必要ならコードを修正し、テストしてください。

vagrant@php7dev:~/fuel-1.8-dev$ php oil test
Tests Running...This may take a few moments.
PHPUnit 5.0.8 by Sebastian Bergmann and contributors.

...............................................................  63 / 389 ( 16%)
............................................................... 126 / 389 ( 32%)
............................................................... 189 / 389 ( 48%)
............................................................... 252 / 389 ( 64%)
............................................................... 315 / 389 ( 80%)
............................................................... 378 / 389 ( 97%)
...........                                                     389 / 389 (100%)

Time: 653 ms, Memory: 14.00Mb

OK (389 tests, 483 assertions)

ドキュメントされていないバグと思われる動作がもしあれば、https://bugs.php.net/ を調べて、報告がなければとりあえずバグ報告しましょう。

Rasmusさんの教え

PHPカンファレンスでRasmusさんは、自分は本番環境にPHP 7.0.0は使わないと言っていました。しかし、7.1まで待つ必要はないとも。

参考

Date: 2015/10/30

Tags: php, php7