PHP

ここが変わった! PHP7.1で知っておきたい新機能まとめ

2017/01/12

Younes Rafie

31

Articles in this issue reproduced from SitePoint
Copyright © 2017, All rights reserved. SitePoint Pty Ltd. www.sitepoint.com. Translation copyright © 2017, KADOKAWA CorporationJapanese syndication rights arranged with SitePoint Pty Ltd, Collingwood, Victoria,Australia through Tuttle-Mori Agency, Inc., Tokyo

12月にリリースされたPHP 7.1はもうチェックしましたか? Nullable型が追加されたり、戻り値にVoid型を指定できるようになったり、新機能がてんこ盛り。注目の新機能をコードを交えてまとめました。

PHP 7が発表され、魅力的な機能が追加されました。最新のPHP 7.1では、さらに役立つ機能が追加されています。本記事では、その中から重要な機能を紹介します。変更のすべてはPHP RFCをチェックしてください。

ArgumentCountErrorの追加

以前のPHPのバージョンでは、定義されているよりも少ない引数でメソッドをコールすると、引数が足りないという警告が表示されました。

// PHP 5.6

function sum($a, $b)
{
    return $a + $b;
}

sum(); 
// Warning: Missing argument 1 for sum()
// Warning: Missing argument 2 for sum()

sum(3);
// Warning: Missing argument 2 for sum()

sum(3, 4);

引数が足りないという警告は上のような例ではあまり役に立たず、開発者はすべての引数がきちんとセットされているかを確認しなければなりませんでした。PHP 7.1ではArgumentCountErrorという例外が表示されるようになりました。

// PHP 7.1

function sum($a, $b)
{
    return $a + $b;
}

sum(); 
// Fatal error: Uncaught ArgumentCountError: Too few arguments to function sum(), 0 passed in /vagrant/index.php on line 18 and exactly 2 expected in /vagrant/index.php:13

sum(3); // skipped

sum(3, 4); // skipped 

Nullable型

PHP 7では引数と戻り値に型が定義できるようになりましたが、足りないところもありました。PHP 7.1ではNullable型によって、指定した型もしくはnullを許容できるようになりました。以下が例です。

function sum(int $a, int $b): ?int
{
    return $a + $b;
}

上の関数では、integerかnullを返します。もし関数内のロジックで不具合が生じても予期せぬnullエラーが発生することはありません。引数についても同様です。

function sum(?int $a, ?int $b): ?int
{
    if ($a == null || $b == null) {
        return null;
    }

    return $a + $b;
}

上のメソッドをコールするときに引数がない場合、例外が発生します。

var_dump(sum(null, null)); // NULL
var_dump(sum()); // throw ArgumentCountError exception

デフォルト値を定義しないときは、必ず引数を指定します。

ほかに覚えておきたいことは、メソッドをオーバーライドするとき、返り値の型にnullable型を追加できませんが、取り除けることです。引数はその逆です。nullable型は取り除けませんが、追加はできます。

interface Fooable {
    function foo(): ?Fooable;
}
interface StrictFooable extends Fooable {
    function foo(): Fooable; // valid
}

interface Fooable {
    function foo(): Fooable;
}
interface LooseFooable extends Fooable {
    function foo(): ?Fooable; // invalid
}

配列の分割代入

これまで配列の分割代入は、次のようにしていました。

list($a, $b, $c) = [1, 2, 3];
var_dump($a, $b, $c); // int(1) int(2) int(3)

しかし、以下のようにすると失敗します。なぜなら、抽出するキーを指定できず、関数はインデックスキーを使おうとするからです。

list($a, $b, $c) = ["a" => 1, "b" => 2, "c" => 3];
var_dump($a, $b, $c); // NULL NULL NULL

RFCでは、次のような分割代入の解決策が紹介されています。上のコードは以下のように変更できます。

list("a" => $a, "b" => $b, "c" => $c) = ["a" => 1, "b" => 2, "c" => 3];
var_dump($a, $b, $c); // int(1) int(2) int(3)

list関数を書けば、配列の分割代入でキーを指定できます。この手法はよく使われるため、新しい短縮表現が導入されました。以下が例です。

["a" => $a, "b" => $b, "c" => $c] = ["a" => 1, "b" => 2, "c" => 3];
var_dump($a, $b, $c); // int(1) int(2) int(3)

すごく簡単になりました。以下のように多次元配列でも動作します。

[[$a, $b], [$c, $d]] = [[1, 2], [3, 4]];
var_dump($a, $b, $c, $d); // int(1) int(2) int(3) int(4)

[["b" => $b], ["c" => $c]] = [["a" => 1, "b" => 2], ["c" => 3, "d" => 4]];
var_dump($b, $c); // int(2) int(3)

Iterable型

iterableの擬似型が追加され、配列型とiterableな値を作るTraversableインターフェイスがつながりました。例を次にあげます。

// PHP 5.6

function dump(array $items)
{
    var_dump($items);
}

dump([2, 3, 4]);
dump(new Collection());
array(3) {
  [0]=>
  int(2)
  [1]=>
  int(3)
  [2]=>
  int(4)
}

Catchable fatal error: Argument 1 passed to dump() must be of the type array, object given...

このようなケースでは、関数がiterableな値を受け取らず、エラーを返します。新しい機能のおかげで、iterableな値を手動でアサートしなくても記述できます。

// PHP 7.1

function dump(iterable $items)
{
    var_dump($items);
}

dump([2, 3, 4]);
dump(new Collection());
array(3) {
  [0]=>
  int(2)
  [1]=>
  int(3)
  [2]=>
  int(4)
}
object(Collection)#2 (0) {
}

コールバックからクロージャーをつくる関数

fromCallableは、クロージャーオブジェクトをコールバックから作れる高性能なメソッドです。以下が例です。

$callback = Closure::fromCallable([$this, 'fn']);

戻り値のVoid型

これは私のお気に入りの新機能です。PHP 7で戻り値に型を設定できる機能が追加されましたが、関数がなにも返さないと強制的にnullを返すようになってしまいます。新機能ではこの点が改善されました。

function dump($object): void
{
    var_dump($object);
}

戻り値のVoid型によってreturn文を取り除くか、return;のようにして、空の値を返すこともできます。

クラス内定数のアクセス修飾子サポート

小さな変更ですが、オブジェクト指向プログラミングにおいて、カプセル化するのに重要な機能です。以下のようにクラス内の定数にアクセス修飾子を付けられるようになりました。

class Post
{
    protected const PUBLISHED = 1;
    protected const DRAFT = 2;
    protected const TRASHED = 3;

    // ...
}

複数タイプの例外をキャッチできる

Javaのようなほかの言語で提供されている、1つのcatchブロック内で複数のタイプの例外をキャッチできる機能です。重複したコードを避けられます。以下に例を紹介します。

// ...

try {
    $user->payMonth($month);
} catch (UserSuspendedException $ex) {
    DB::rollBack();

    return redirect()
            ->back()
            ->withInput()
            ->withErrors([$ex->getMessage()]);
} catch (PaidMonthException $e) {
    DB::rollBack();

    return redirect()
            ->back()
            ->withInput()
            ->withErrors([$ex->getMessage()]);
}

// ...

新しいcatchブロックを使えば、この重複したコードを取り除けます。

// ...

try {
    $user->payMonth($month);
} catch (PaidMonthException | UserSuspendedException $ex) {
    DB::rollBack();

    return redirect()
            ->back()
            ->withInput()
            ->withErrors([$ex->getMessage()]);
}

// ...

数値演算で文字列にエラーが出る

PHPを始めたとき、数値演算で文字列が許容されていて驚いたと思います。なぜ許容されているかと言えば、PHPが文字列を値とするWebをいつも扱ってきたからです。

// PHP 5.6

var_dump("1" + "2");
var_dump("1" + "a string");
var_dump("1" + "2  with a string");
int(3)
int(1)
int(3)
// PHP 7.1

var_dump("1" + "2");
var_dump("1" + "a string");
var_dump("1" + "2  with a string");
int(3)

Warning: A non-numeric value encountered in /vagrant/index.php on line 17
int(1)

Notice: A non well formed numeric value encountered in /vagrant/index.php on line 18
int(3)

今後は、数値ではないときは警告が表示され、正しいフォーマットではないとき(2 a string = 2)は通知されます。

今回紹介したものはすべてではありません、すべての変更はRFC listをチェックしてください。

※本記事はThomas Puntが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。

(原文:What’s New and Exciting in PHP 7.1?

[翻訳:萩原伸悟/編集:Livit

Copyright © 2017, Younes Rafie All Rights Reserved.

Younes Rafie

Younes Rafie

モロッコ出身のフリーランスのWeb開発者、技術系ライター・ブロガーです。JAVA、J2EE、JavaScriptでの共同作業経験があり、専門はPHP言語です。

Loading...