読者です 読者をやめる 読者になる 読者になる

Connehito開発者ブログ

Connehito Engineer's Blog

PHP7にCakePHP2.xが来るぞ!!!!!

f:id:connehito:20160205104322j:plain

来るぞ!!!

ご無沙汰しております。
コネヒトでPHPを書いている金城 / @o0h_です。
タイトルの通りですが、CakePHP 2.xが次期バージョンの2.8でPHP7に対応!(!)という話を小耳に挟んだので、喰いついてみました。

PHP7 Compatibility!!!

f:id:connehito:20160204202829p:plain

CakePHP、PHP5時代と次世代への断絶

新しい世界・・・・怖いのかな・・・めちゃくちゃ大変なの??
という事には、(実際に使うかは別としても)興味を持つ人も多いのではないかな!!!と思いました。
かくいう私もその1人、さればとGithubを眺めながら「何が起きたか」を探って参りました。
CakePHPが通った道のりを紐解くことで、「PHP7に飛び乗るための覚悟」を少し育めたらなと思います。

結論: 「CakePHPが7対応のためにやった事のほとんどは、テストコードの修正」

実は「2.8 PHP7 compatability by dereuromark · Pull Request #7840 · cakephp/cakephp · GitHub」という、そのものずばりなプルリクエストがあり*1、今回はそこで行われた修正や議論を読んでみました。

結論から言うと、「少なくともフレームワーク本体の痛みの凄そうな改修は行われておらず*2、対象箇所は「PHPUnitをグリーンにする」というのが主であるという印象です。 *3

大きく分けて、対応ポイントは3つに絞られるのかなという風に感じました。
それぞれについて、ここで振り返ってみようと思います。

1, 新たに導入されたErrorクラス

これまでのExceptionとは別に、新たに「Error」というクラスが導入されました。
PHP: Error - Manual
何が起きるか?

  1. catch Exceptionで捕まらないものが出てくる
  2. logging用の関数などでのタイプヒンティングにてExceptionが使われていると危険

具体的に扱われている修正がこちら。

タイプヒンティングの修正、及びアノテーションの修正(@paramにParseErrorの明示)が行われています。

- * @param Exception $exception The exception to render.
+ * @param Exception|ParseError $exception The exception to render.
- public static function handleException(Exception $exception) {
+ public static function handleException($exception) {

2, 型宣言

Errorクラスなどと違い、全く新しいか・・?と言われると、以前から同様の機構はありましたが。

http://php.net/manual/ja/functions.arguments.php#functions.arguments.type-declaration

注意: 型宣言は、PHP 5 ではタイプヒンティングとも呼ばれていました。

ということなので、呼び方には注意したいですね^^

機能名もさることながら、「既存コードの移行」を考えると注視すべきは次の部分だと思います。

PHP 5 では、このエラーは recoverable fatal error でした。 PHP 7 では、この場合に TypeError 例外をスローします。

catchが(略

これに関して、対応しているのは「PHPUnitのテストの書き方について」。
Allowing tests to run on PHP 7 · cakephp/cakephp@48e018e · GitHubをご覧ください。
かいつまむと、ここで取り沙汰されているのは「渡されている引数の型が違ったら例外を投げる」という内容のテスト
=> 「期待されていない型を渡す」「例外が吐かれていればOKとする」という記述。

が、しかし、PHP7からは前述のとおり型宣言に反する引数は「TypeError」をもたらすものであり、これは従来とは異なる挙動です。
すなわち、(PHPUnitフレームワーク側が対応していない限り*4 )適切にハンドリングされてテストフレームワークの持つ例外が返される〜という事が期待できません。
そこで、「一旦キャッチして、改めて(明示的に)throw new PHPUnit_Framework_Errorする」という対処が為されました。

その際に、新たに導入されたインターフェイス「Throwable」が用いられています。

PHP: Throwable - Manual

何かというと「throwできるクラスが継承されるもの」であり、すなわち「Exception」「Error」の共通の性質といえます。
これらの内容を踏まえて記述されるのが以下のコード。

 try {
        $this->auth->controller(new StdClass());
    } catch (Throwable $t) {
        throw new PHPUnit_Framework_Error($t);
    }

この辺りは実際にコードを見てもらえると分かりやすいと思うので、ぜひ先に引っ張ってきたリンク先をご覧ください。

3,16進数文字列の扱い変更

文字列の扱いに関する変更が入っており、具体的には'0x00'というような値が「文字列」として扱われ瑠葉になりました。
例えば関連する変更は次のURLですので、ご覧ください。
Fixes: A non well formed numeric value encountered - php 7 · cakephp/cakephp@34b4261 · GitHub
以前までのchr('0x97')としていた箇所が、「文字列を渡すな!!」ということでnoticeを発するようになりました。

オマケ: Stringクラス

最後に紹介するのは、実は最新安定版の2.7.9では既に取り入れられている内容であり、元を正せばCake3からバックポートされているアイディアです。
Rename String class to CakeText. Backport from 3.0 by dereuromark · Pull Request #5559 · cakephp/cakephp · GitHub

このエントリーではhttps://github.com/cakephp/cakephp/pull/7807:#7807での変更内容だけを対象に振り返っていますが、「PHP7対応」という名目上での重要事項・特徴的といえる変更の1つがこちらです。
ので、「オマケ」として一緒に言及しておこうと思いました。

何かというと、型宣言でスカラを扱えるようにあったことで予約語が増えた=Stringというクラスが定義できなくなった、という問題の回避です。

function hoge(String $s) {
  return $s;
}
// hoge ([1, 2, 3]) でTypeError

みたいな事が出来る様になるわけですね。メッチャ嬉しい・・・ 公式のドキュメントはこちら

とはいえ、互換性保持のためにStringクラスも残存し、PHP5.xでの利用に関しては気にしなくてよい内容かと思われます。
cakephp/String.php at 63093e1d30fc00a5fc2ff89aa6973775459ab8c7 · cakephp/cakephp · GitHub
覗いてみると、StringクラスはCakeTextを継承したサブクラスとなり、自身に独自の実装はなし+StringTestの設置は省略(CakeTextTestでその責務を担っているので)という状況になっています。
なるほどですね。

まとめ

実際に動かしているアプリケーションの移行する、となると(当然ですが)もっと骨のある変更が多いかとは容易に想像できます。
が、仮に「フレームワーク自体を頑張って最新版に追従させていた」・・という状況からであれば、さほど大きなジャンプアップはないように個人的に感じました。

いずれにせよ、公式のマイグレーションガイドが参考になりそう(充実している、かつ目が回るほどの分量でもない)なのでココを見つつ頑張る〜という感じになるかと思います。
PHP: PHP 5.6.x から PHP 7.0.x への移行 - Manual

他方で、慣れ親しんでいるフレームワークの修正や議論といった足跡を辿るのは、対応した内容の意図や"感じ"を掴む上で興味深いと感じました。


末筆ながら、PHP・CakePHP及びそれに携わる皆様方の益々のご繁栄とご発展を弊社一同お祈り申し上げます云々

最後まで目を通していただいて、ありがとうございました。
コネヒトでは、今まさにPHP7やCakePHP3*5の実践導入のための策略を練っており && 実際にプロジェクトを稼働させている最中でございます。 それらを実現する文化的・組織的な下地として、次のような要素が欠かせないと私個人では理解しております

  • 柱が自社サービスであること(リスクをとりやすい、リアルタイムに監視・対応をとりやすい)
  • CTO以下devメンバーが最新の技術や環境へのあまねく興味を抱き、憧れの眼差しを向けていること
  • 「個々人の成長が組織やプロダクトの成長に直結する」という事を理解し実感していること
  • 小回りの利くスケールでチームが動いていること
  • etc

今のうちの環境にはこれらが揃っているのではないか?と。
「ちょっとやってみたい!!」「それってどういう意味!」と思ってくれた方、カジュアルな気持ちでお話を聞きにきてみてください! 弊社CTOがお迎えします!

それでは、Happy Valentine's Day!!!!🍣🍣🍣

*1:2.8以前に、「PHP7対応してみね?」という議論が発生しています。最終結論として、このバージョンで対応することが決まったという流れ

*2:ドキドキしながら覗き込んだのですが、刺激の少ない改修・・と言えなくもなく。

*3:もちろん、それ以前のバージョンや2.8ブランチの他のコミットでクリアされた問題も少なからずあるでしょうし、このバージョン自体がまだ安定版としてリリースされていないという事は触れておきます。

*4:まだ内容を見ていないですが、5.0.0での対応というアナウンスが出ています: Release Announcement for PHPUnit 5.0.0 · sebastianbergmann/phpunit Wiki · GitHub

*5:ここまで散々2.xのことを書いたのに!