こんにちは、暑さの余り既にクーラーが稼働し始めました、山本です。

さて、今回ですが昨年末に更新された、PHP7を取り扱ってみようと思います。

インストール

毎回お馴染みのインストールです。

当初、makeするのかな~と考えていたのですが、幸いなことにyumで入るようでした。

以下、Cent6でのインストールです。

rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
sudo yum -y install –enablerepo=remi –enablerepo=remi-php70 php

001

002

無事入りました。

タイプヒンティング

PHP5から採用された、引数の型をある程度制限出来るものです。

といっても、全ての型を制限出来るわけではありません。

ドキュメント

クラスは5で、arrayが5.1、callableが5.4と段階的に拡張されて来ました。

今回の7では、bool、float、int、stringの4つの型が対応されました。

早速見てみましょう。

<?php
  function test (int $foo)
  {
    var_dump($foo);
  }

  test(100);
  test(100.123);
  test(true);
  test('100');
[root@localhost test]# php 001.php
int(100)
int(100)
int(1)
int(100)

ん?

引数をintとしたはずなんですが。。。
floatも、boolも、stringも通ってますね。

キャストされていますが。。。
相変わらず、適当な 緩い仕様ですね。

一応、数字を含まない文字列ですと

  test('AAA');
[root@localhost test]# php 001.php
PHP Fatal error:  Uncaught TypeError: Argument 1 passed to test() must be of the type integer, string given, called in /home/adjust/test/001.php on line 7 and defined in /home/adjust/test/001.php:2
Stack trace:
#0 /home/adjust/test/001.php(7): test('AAA')
#1 {main}
  thrown in /home/adjust/test/001.php on line 2

ちゃんとエラーになりましたね。

float

<?php
  function test (float $foo)
  {
    var_dump($foo);
  }

  test(1);
  test(1.123);
  test(true);
  test('123');
  test('AAA');
[root@localhost test]# php 001.php
float(1)
float(1.123)
float(1)
float(123)
PHP Fatal error:  Uncaught TypeError: Argument 1 passed to test() must be of the type float, string given, called in /home/adjust/test/001.php on line 11 and defined in /home/adjust/test/001.php:2
Stack trace:
#0 /home/adjust/test/001.php(11): test('AAA')
#1 {main}
  thrown in /home/adjust/test/001.php on line 2

int同様に文字列だけエラーですね。

string

<?php
  function test (string $foo)
  {
    var_dump($foo);
  }

  test(1);
  test(1.123);
  test(true);
  test('123');
  test('AAA');
  test(null);
[root@localhost test]# php 001.php
string(1) "1"
string(5) "1.123"
string(1) "1"
string(3) "123"
string(3) "AAA"
PHP Fatal error:  Uncaught TypeError: Argument 1 passed to test() must be of the type string, null given, called in /home/adjust/test/001.php on line 12 and defined in /home/adjust/test/001.php:2
Stack trace:
#0 /home/adjust/test/001.php(12): test(NULL)
#1 {main}
  thrown in /home/adjust/test/001.php on line 2

stringはnullでエラーになりました。
というか、null以外エラーになるのかといった疑問もあるのですが。

bool

<?php
  function test (bool $foo)
  {
    var_dump($foo);
  }

  test('AAA');
  test('');
  test(1);
  test(0);
  test(null);
[root@localhost test]# php 001.php
bool(true)
bool(false)
bool(true)
bool(false)
PHP Fatal error:  Uncaught TypeError: Argument 1 passed to test() must be of the type boolean, null given, called in /home/adjust/test/001.php on line 11 and defined in /home/adjust/test/001.php:2
Stack trace:
#0 /home/adjust/test/001.php(11): test(NULL)
#1 {main}
  thrown in /home/adjust/test/001.php on line 2

こちらも、nullしかエラーになりませんね。

まあ、無いよりはましな気がします。

強い型付け

と思っていたのですが、厳密な型をチェックする機能もあるようです。

<?php
  declare(strict_types=1);

  function test (bool $foo)
  {
    var_dump($foo);
  }

  test(1);
[root@localhost test]# php 001.php
PHP Fatal error:  Uncaught TypeError: Argument 1 passed to test() must be of the type boolean, integer given, called in /home/adjust/test/001.php on line 9 and defined in /home/adjust/test/001.php:4
Stack trace:
#0 /home/adjust/test/001.php(9): test(1)
#1 {main}
  thrown in /home/adjust/test/001.php on line 4

で、説明を読んでみると

厳密な型チェックが適用されるのは、それを有効にしたファイル の中からの関数呼び出しだけです。
そのファイル内で宣言されている関数に適用されるわけではありません。
厳密な型チェックを有効にしていないファイルから、厳密な型チェックを有効にしたファイル内で定義された関数を呼び出した場合は、 呼び出し元の設定 (弱い型チェック) が適用されて、自動的に型変換を行います。

とてもPHPぽい挙動ですね。

致命的エラー

今回のバージョンより、致命的エラーもcatch出来るようになったそうです。

try
{
  test();
}
catch(Exception $e)
{
  var_dump($e->getMessage());
}
[adjust@localhost test]$ php 001.php 
PHP Fatal error:  Uncaught Error: Call to undefined function test() in /home/adjust/test/001.php:4
Stack trace:
#0 {main}
  thrown in /home/adjust/test/001.php on line 4

で試してみたんですが。。。

拾いませんね、なんでしょう。

Throwable
  Exception
    ErrorException
  Error
    ArithmeticError
    AssertionError
    DivisionByZeroError
    ParseError
    TypeError

で、ドキュメントを見てみるとどうやらエラーはExceptionではなく、Errorでcatchする仕様のようです。

なので、こうすると

try
{
  test();
}
catch(Error $e)
{
  var_dump($e->getMessage());
}
[adjust@localhost test]$ php 001.php 
string(33) "Call to undefined function test()"

しっかりと、catchしてくれました。

まあ、使う機会もあるだろうと思っていたのですが、

try
{
  require null;
}
catch(Error $e)
{
  var_dump($e->getMessage());
}
[adjust@localhost test]$ php 001.php 
PHP Warning:  require(): Filename cannot be empty in /home/adjust/test/001.php on line 4
PHP Fatal error:  require(): Failed opening required '' (include_path='.:/usr/share/pear:/usr/share/php') in /home/adjust/test/001.php on line 4

拾わないエラーもあるようですね。

PHP 7 changes how most errors are reported by PHP. PHP 5 で使われていたこれまでのエラー報告メカニズムを使うかわりに、 大半のエラーを Error 例外としてスローするようになったのです。

だそうです。

無名クラス

無名関数は5.3から対応していましたが、今回のバージョンからはクラスも対応ですね。

$foo = new class {};
var_dump($foo);
[adjust@localhost test]$ php 001.php 
object(class@anonymous)#1 (0) {
}

その場限りのクラスを作りたいってのも、わりとあるのでこの拡張はうれしいですね。

あと、即時実行も可能でした。

(new class {
  public function test()
  {
    echo 'test';
  }
})->test();

[adjust@localhost test]$ php 001.php 
string(4) "test"

宇宙船演算子

Rubyのイメージが強かったんですが、PHPに実装されたようです。

  var_dump(1<=>1);
  var_dump(0<=>1);
  var_dump(1<=>0);
[adjust@localhost test]$ php 001.php 
int(0)
int(-1)
int(1)

usortなど、ソートで使うときに便利そうですね。

??演算子

Null合体演算子も追加されました。

まあ、三項演算子での省略も出来るんですが

echo isset($foo) ?: 'undefined';

とでもした場合、$fooの中身ではなく、issetの評価の結果が返るため
今ひとつ使い勝手が良くありませんでした。

が、今回の拡張により便利になった気がします。

echo $foo ?? 'undefined';
[adjust@localhost test]$ php 001.php
undefined

配列を用いた定数

例えば、設定ファイル。

その設定ファイルの中に、ズラズラと連なるdefineの群れ。
統一感のあるファイルの中に、和を乱すarrayの記述。
おまけにそれにGlobalとか書いてあったりと、誰がどう見ても美しくない記述です。

設定をdefineでやるな、という意見もあるんでしょうけれども
既にそれで構築されてシステムだったりすることもあったりで
全く使わないって訳にはいかないものだったりします。

そういった時、配列もdefineで持てるととても便利ですね。

define ('FOO', ['a' => 10, 'b' => 20]);
var_dump(FOO);
[adjust@localhost test]$ php 001.php 
array(2) {
  ["a"]=>
  int(10)
  ["b"]=>
  int(20)
}

対応状況

とりあえず、WordPressを動かしてみました。

003

何事もなかったかのように動くので、拍子抜けしてしまいました。

あと、計測はしていないのですが、体感で結構処理が早くなった気がします。

他に、フレームワークも試してみます。

  • symfony 1.4
    008
  • symfony 2.8
    006
  • symfony 3.0
    007
  • cake 2
    004
  • cake 3
    005

古いバージョンのものまで含めても、動きますね。

まとめ

他にも、拡張されたものや、廃止されたもの等があるのですが、きりがないのでこの辺で締めます。

感想ですが、メジャーバージョンアップのわりに仕様の変更があるようには感じませんでした。
内部的には、メモリの持ち方などかなり変更があったようですが。

また、下位互換も維持しており、インストールも簡単に出来ました。

速度的にも早くなっていますし、新しくインストールする際にはPHP7を採用というのもありだと思いました。