PHP 7の次のマイナーリリースであるPHP 7.4は、2019年11月28日にリリースされました。そこで今日は、PHPをより速くより信頼性の高いものにした最もエキサイティングな新しい機能についてご説明します。

最新情報)現在、PHP 8.1(正式リリース)がすべてのプランでサポートされており、PHP 7.4のサポートは終了しています。KinstaがサポートするPHPバージョンは、8.1, 8.2 , 8.3です。

PHP 7.4がパフォーマンスとコードの読みやすさを大幅に向上させたとしても、JITの追加の提案はすでに承認されているため、PHPのパフォーマンスにとって重要なマイルストーンとなるのは明らかにPHP 8です。

とにかく、今日はPHP 7.4に期待している最も興味深い機能と変更点のいくつかをご紹介します。ちなみに、スケジュールは下記のとおりでした。

  • 2019年6月6日:PHP 7.4 Alpha 1
  • 2019年7月18日:PHP 7.4 Beta 1 :機能フリーズ
  • 2019年11月28日:PHP 7.4 GAリリース

公式のRFCページで機能と変更点の完全なリストも確認できます。

PHP 7.4の新機能

本記事では、PHP 7.4の最終リリースの予定されている変更点と新機能の一部についてご説明します。

array_mergeを忘れましょう!PHP 7.4で配列式のスプレッド演算子が登場します

PHP 5.6以降で利用可能な、引数のアンパックは配列やTraversableオブジェクトを引数リストにアンパックするための構文です。次の例のように、配列またはTraversableをアンパックするには、先頭に…(3点リーダー)を付ける必要があります。

function test(...$args) { var_dump($args); }
test(1, 2, 3);

このPHP 7.4のRFCでは、この機能を配列定義に拡張することが提案されています。

$arr = [...$args];

配列式のスプレッド演算子の最初の利点はフォーマンス改善であるようです。RFCの文書には次のように記載されています。

スプレッド演算子は、array_mergeよりもパフォーマンスがいいはずです。 理由は、array_mergeが関数であるのに対し、スプレッド演算子が言語構造であることだけでなく、コンパイル時の最適化が定数配列のパフォーマンスを改善することです。

スプレッド演算子の重要な利点は、array_merge関数は配列のみをサポートしていることに対して、全てのTraversableオブジェクトをサポートしていることです。

下記は、配列式のスプレッド演算子の例です。

$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
var_dump($fruits);

PHP 7.3以前でこのコードを実行した場合、解析エラーが発生しました。

Parse error: syntax error, unexpected '...'(T_ELLIPSIS), expecting ']' in /app/spread-operator.php on line 3

一方、PHP 7.4は配列を返します。

array(5) {
	[0]=>
	string(6) "banana"
	[1]=>
	string(6) "orange"
	[2]=>
	string(5) "apple"
	[3]=>
	string(4) "pear"
	[4]=>
	string(10) "watermelon"
}

RFCには、同じ配列を複数回拡張できると記載されています。さらに、通常のエレメントはスプレッド演算子の前後に追加できるため、配列内のどこにでもスプレッド演算子が使用できます。したがって、次のコードは予想どおりに機能します。

$arr1 = [1, 2, 3];
$arr2 = [4, 5, 6];
$arr3 = [...$arr1, ...$arr2];
$arr4 = [...$arr1, ...$arr3, 7, 8, 9];

関数に返された配列を直接新しい配列にアンパックすることもできます。

function buildArray(){
	return ['red', 'green', 'blue'];
}
$arr1 = [...buildArray(), 'pink', 'violet', 'yellow'];

PHP 7.4では次の配列が出力されます。

array(6) {
	[0]=>
	string(3) "red"
	[1]=>
	string(5) "green"
	[2]=>
	string(4) "blue"
	[3]=>
	string(4) "pink"
	[4]=>
	string(6) "violet"
	[5]=>
	string(6) "yellow"
}

ジェネレータの構文も利用可能です。

function generator() {
	for ($i = 3; $i <= 5; $i++) {
		yield $i;
	}
}
$arr1 = [0, 1, 2, ...generator()];

しかし、参照渡しの配列をアンパックすることはできません。次の例をご確認ください。

$arr1 = ['red', 'green', 'blue'];
$arr2 = [...&$arr1];

参照によって配列をアンパックしようとすると、PHPは次の解析エラーを返します。

Parse error: syntax error, unexpected '&' in /app/spread-operator.php on line 3

とにかく、最初の配列の要素が参照によって保存されている場合、2番目の配列にも参照によって保存されます。下記はその一例です。

$arr0 = 'red';
$arr1 = [&$arr0, 'green', 'blue'];
$arr2 = ['white', ...$arr1, 'black'];

一方、PHP 7.4では下記が返されます。

array(5) {
	[0]=>
	string(5) "white"
	[1]=>
	&string(3) "red"
	[2]=>
	string(5) "green"
	[3]=>
	string(4) "blue"
	[4]=>
	string(5) "black"
}

スプレッド演算子の提案は43対1の投票で可決しました。

矢印関数2.0(ショートクロージャ)

PHPでは、無名関数は非常に冗長で、作成かつ保守しにくいものと見なされています。このRFCでは、PHPコードを大幅に整理できる矢印関数(ショートクロージャ)の短くて明確な構文の導入を提案されています。

次の例をご確認ください。

function cube($n){
	return ($n * $n * $n);
}
$a = [1, 2, 3, 4, 5];
$b = array_map('cube', $a);
print_r($b);

PHP 7.4では、より簡潔な構文を使用でき、上記の関数は次のように書き直すことができます。

$a = [1, 2, 3, 4, 5];
$b = array_map(fn($n) => $n * $n * $n, $a);
print_r($b);

現在、無名関数(クロージャ)は、以下に示すように、use言語構成のおかげで変数を親のスコープから引き継ぐことができます。

$factor = 10;
$calc = function($num) use($factor){
	return $num * $factor;
};

しかしPHP 7.4では、親スコープで定義された変数は暗黙に値として表示されます。(暗黙的な値によるスコープバインディング)そのため、上記の関数全体を1行で書くことができます。

$factor = 10;
$calc = fn($num) => $num * $factor;

親スコープで定義された変数は、矢印関数でuse($var)の使用と同様に使用できます。親スコープの変数が変更できません。

この新しい構文では、読みやすくて保守しやすいコードが作成できるため、本言語の大幅な改善項目に違いありません。パラメータ型と戻り値の型宣言、デフォルト値、可変長引数リスト(可変長関数)、参照渡しなどを使用することもできます。ショートクロージャはまた、クラスメソッドでも使用できます。それに、通常のクロージャと同様に$this変数さえ使用可能です。

このRFCは51対8の投票で可決したため、本機能はPHP 7.4で導入されるでしょう。

null 合体演算子

PHP 7で導入された合体演算子??)は、三項演算子とisset()を組み合わせる場合に便利です。この演算子は、もし第一オペランドが非 NULL の値であればそれを返し、 そうでない場合は第二オペランドを返します。 下記はその一例です。

$username = $_GET['user'] ?? 'nobody';

このコードの機能は非常に簡単です。リクエストパラメータを探し取得し、存在しない場合はデフォルト値を設定します。上記の行の意味は明らかですが、RFCのこの例のようにもっと長い変数名があったとしたらどうでしょうか?

$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';

長い目で見れば、上記のコードは保守しにくいかもしれません。したがって、開発者がより分かりやすいコードを書けるようになることを目的として、このRFCnull 合体演算子??=)の導入を提案しています。すれで、上記のコードではなく、次のようなものを書くことができます。

$this->request->data['comments']['user_id'] ??= 'value';

左側のパラメータの値がnullの場合は、右側のパラメータの値が使用されます。
合体演算子は比較演算子ですが、??=は代入演算子ですので、ご注意ください。

この提案は37対4の投票で可決しました。

型付きプロパティ2.0

引数の型宣言(タイプヒント)を使用すると、関数またはクラスメソッドに渡されると予想される変数の型を指定できます。型宣言は、PHP 5以降で利用可能な機能で、PHP 7.2以降では、objectデータ型でも使用できるようになりました。PHP 7.4では、ファーストクラスのプロパティ型宣言のサポートを追加することにより、型宣言が一歩前進しました。簡単な例をあげると、

class User {
	public int $id;
	public string $name;
}

voidcallableを除いて、すべての型がサポートされています。

public int $scalarType;
protected ClassName $classType;
private ?ClassName $nullableClassType;

RFCでは、voidcallableがサポートされていない理由が説明されています。

void型は不便で、不明なところがあるため、サポートされていません。

callable型は、動作が文脈によるものであるため、サポートされていません。

したがって、boolintfloatstringarrayobjectiterableselfparent、任意のクラス名またはインタフェース名、およびnull許容型(?型)を安全に使用できます。

型は静的プロパティに使用できます。

public static iterable $staticProp;

varと組み合わせてもかまいません。

var bool $flag;

デフォルトのプロパティ値を設定することは可能です。もちろん、デフォルトのプロパティ値は宣言されたプロパティ型と一致する必要があり、デフォルトのnull値を設定できるのはnull値を許容されるプロパティのみです。

public string $str = "foo";
public ?string $nullableStr = null;

一つの宣言内のすべてのプロパティに同じ型が適用されます。

public float $x, $y;

プロパティ型を間違えるとどうなりますか? 次のコードをご確認ください。

class User {
	public int $id;
	public string $name;
}

$user = new User;
$user->id = 10;
$user->name = [];

上記のコードでは、文字列プロパティ型を宣言しましたが、プロパティ値として配列を設定しました。この場合は、次の致命的エラーが発生します。

Fatal error: Uncaught TypeError: Typed property User::$name must be string, array used in /app/types.php:9

このRFCは70対1の投票で可決しました。

弱い参照

このRFCにより、PHP 7.4では弱い参照クラスが導入されます。これによりプログラマは、オブジェクト自体が破壊されるのを防ぐことのできないオブジェクトへの参照を持ち続けることができます。

現在のPHPではpecl-weakrefなどの拡張機能を使用して弱い参照がサポートされています。とにかく、新しいAPIは文書化されたWeakRefクラスとは異なります。

下記は、この提案の著者であるNikita Popovによる一例です。

$object = new stdClass;
$weakRef = WeakReference::create($object);

var_dump($weakRef->get());
unset($object);
var_dump($weakRef->get());

最初のvar_dumpobject(stdClass)#1 (0) {}を出力しますが、2番目のvar_dumpは参照されたオブジェクトが破棄されている為NULLを出力します。

このRFCは28対5の投票で可決しました。

共変の戻り型と反変のパラメータ

変性とは型コンストラクタの型が派生型に与える影響を記述するクラス階層のプロパティです。型コンストラクタは次のようなものがあります。

  • 不変:スーパータイプの型が派生型の型を制約する場合。
  • 共変:型の順序付けが変わらない場合。(具体的なものの順)
  • 反変:順序を並べ替える場合。(一般的なものの順)

PHPの現時点でのパラメータと戻り型はほとんど不変です。例外は少ないです。このRFCでは、パラメータ型と戻り型で共変性と反変性を許可することが提案されています。また、コード例もいくつか挙げられています。

下記は、共変の戻り型の一例です。

interface Factory {
	function make(): object;
}

class UserFactory implements Factory {
	function make(): User;
}

下記は、反変の戻り型の一例です。

interface Concatable {
	function concat(Iterator $input); 
}
 
class Collection implements Concatable {
	// accepts all iterables, not just Iterator
	function concat(iterable $input) {/* ...*/}
}

PHP 7.4の共変性と反変性の詳細については、RFCをご参照ください。

このRFCは39対1の投票で可決しました。

プリロード

Dmitry Stogovによるこの提案では、パフォーマンスを大幅に向上させるはずであるため、特に興味いです。プリロードとは、モジュールの初期化時にライブラリとフレームワークをOPCacheにロードするプロセスです。(PHPのライフサイクルについてのはこちらをご参照ください。)

PHPのライフサイクル
PHPのライフサイクル(画像の出典:PHP Internals

Dmitryがプリロードのが概要を次のように説明しています。

アプリケーションコードが実行される前のサーバー起動時に、特定のPHPファイルをメモリにロードし、その内容を今後そのサーバーが処理するすべてのリクエストで「恒久的に利用可能」にすることができます。これらのファイルで定義されているすべての関数とクラスは、内部のものと同様に、すぐにリクエストで使用できます。

これらのファイルはサーバの起動時にロードされ、アプリケーションの実行前に実行され、今後のリクエストに使用できるようになるようです。パフォーマンスの面では素晴らしいことです。

プリロードは特定のphp.iniディレクティブopcache.preloadによって制御されます。 このディレクティブはサーバー起動時にコンパイルされ実行されるPHPスクリプトを指定します。このファイルはファイルを追加するかopcache_compile_file()関数を使用することにより追加のファイルをプリロードするためにも使用できます。(詳細についてはPHPの文章をご参照ください。)

しかし、欠点があります。 RFCで次の内容が明確にされています。

プリロードされたファイルはopcacheメモリに永久にキャッシュされたままになります。該当のソースファイルを変更しても、サーバーを再起動しないと何も影響ありません。

ただし、プリロードファイルで定義されているすべての関数は、PHPの関数テーブルとクラステーブルに恒久的にロードされたままになり、今後のすべてのリクエストで利用可能なままになります。これはパフォーマンスの大幅な改善につながるでしょう。(改善の程度が場合によって異なる場合があるかもしれません。)

プリロードの概要については公式のRFCページをご確認ください。

オブジェクトの新しい独自のシリアライズメカニズム

これは、過半数の投票によって承認されたNikita Popovによるもう一つの提案です。

現在、PHPにおけるオブジェクトの独自のシリアライズメカニズムは2つあります。

  • __sleep()__wakeup()__sleep()および__wakeup()マジックメソッド
  • Serializable インターフェイス

Nikitaによると、上記のどちらにも、複雑で信頼性の低いコードにつながる問題があるようです。詳細についてはRFCをご参照ください。新しい独自のシリアライズメカニズムには、2つの既存のメカニズムを組み合わせた新しいマジックメソッドが2つ(__serialize()__unserialize())あり、これにより上記の問題を防ぐことができるはずでしょう。

このRFCは20対7の投票で可決しました。

推奨されなくなるもの

以下はPHP 7.4で推奨されなくなる予定の機能と関数の一覧です。完全なリストについては、PHP 7.4 Upgrade Notesをご参照ください。

連結演算子の優先順位の変更

現在のPHPでは、 「+」と 「-」の算術演算子と「.」の文字列演算子は左結合で、優先順位は同じです。(演算子の優先順位についてはこちらをご参照ください。)

下記はその一例です。

echo "sum: " .$a + $b;

PHP 7.3では、このコードでは以下の警告が生成します。

Warning: A non-numeric value encountered in /app/types.php on line 4

これは、結合時の評価が左から右に行われるためです。次のコードを書くのと同じです。

echo ("sum: " .$a) + $b;

このRFCでは、演算子の優先順位を変更して、「.」の優先順位を「+」と「 – 」演算子よりも低くして、加算と減算が常に文字列連結の前に行われるようにすることが提案されています。コードの該当の行は次のものと同等になります。

echo "sum: " .($a + $b);

この提案には2段階があります。

  • バージョン7.4以降、PHPが「+」、「-」や「.」付きのかっこで囲まれていない数式を検出すると、非推奨通知が発生します。
  • これらの演算子の優先順位の実際の変更は、PHP 8で追加される予定です。

両方の提案は過半数の投票によって承認されました。

左結合三項演算子の非推奨

PHPでは、他の多くの言語とは異なり、三項演算子は左結合です。Nikita Popofによると、複数の言語を扱っているプログラマにとってはこれが混乱を招くことがあります。

現在のPHPでは次のコードが正しいです。

$b = $a == 1 ? 'one' : $a == 2 ? 'two' : $a == 3 ? 'three' : 'other';

次のように解釈されます。

$b = (($a == 1 ? 'one' : $a == 2) ? 'two' : $a == 3) ? 'three' : 'other';

意図したとは違う動作になってしまい、エラーにつながる可能性があります。そのため、このRFCでは、三項演算子に対する左結合の使用を非推奨にし、開発者に括弧を使用してもらうことを提案されています。

この提案にも2段階があります。

  • PHP 7.4以降では、明示的に括弧を使用しないネストした三項演算が検出されると非推奨通知が発生します。
  • PHP 8.0以降では、コンパイル時エラーが発生します。

この提案は35対10の投票で可決しました。

PHP 7.4のWordPressユーザーへの影響は?

PHPは、Web上で最も広く使用されているサーバーサイドのプログラミング言語です。W3Techsによると、2019年12月2日現在にPHPは、サーバーサイドのプログラミング言語がわかっているウェブサイトの78.9%で使用されています。

PHPの使用状況(2019年12月)
PHPの使用状況(2019年12月)

残念なことに、サーバサイドのプログラミング言語が知られている全ウェブサイトの44.0%ではPHP 5が使用されていますPHP 7.0と7.1を使用しているユーザーの数も考慮すると、大多数のウェブサイトがサポートのないPHPバージョンを使用していることが分かります。

サポートのあるPHPのバージョン
サポートのあるPHPのバージョン(画像の出典:Supported Versions

WordPressの正式分析ページによると、本稿執筆の時点では、WordPressユーザーの64%以上がまだPHPのサポートのないバージョンを使用しています。わずか13%がPHP 7.3を使用しており、最新バージョンのPHP 7.4はまだ登場していません。ご覧のとおり、ユーザーの23%以上の大多数はまだPHP 5.6を使用しています。

WordPressにての各PHPバージョンの使用率
WordPressにての各PHPバージョンの使用率

できればWordPressの公式要件に従って、サポートされているバージョンのPHPをホスティング会社に依頼することを強くお勧めします。この記事の執筆時点(2019年5月)で、WordPressの要件は次のとおりです。

  • PHP バージョン 7.3 以上。
  • MySQL バージョン 5.6 以上、または MariaDB バージョン 10.1 以上
  • HTTPS 対応

PHP 7のパフォーマンス

PHP 7の方がはるかに高速であることが証明されているため、上記のデータはパフォーマンスの観点からも分かりにくいです。次に分析データを示します。

当社も独自のPHP 7.3のPHPパフォーマンスベンチマークを作成しました。そこで、PHP 7.3ではPHP 5.6と比較して1秒間に約3倍のトランザクション(リクエスト)が実行できることがわかりました。早いうちにPHP 7.4のベンチマークも公開する予定です!

WordPress 5.0 のPHPベンチマーク
WordPress 5.0 のPHPベンチマーク
  • WordPress 5.0 PHP 5.6のベンチマーク結果:91.64リクエスト/秒
  • WordPress 5.0 PHP 7.0のベンチマーク結果:206.71リクエスト/秒
  • WordPress 5.0 PHP 7.1のベンチマーク結果:210.98リクエスト/秒
  • WordPress 5.0 PHP 7.2のベンチマーク結果:229.18リクエスト/秒
  • WordPress 5.0 PHP 7.3のベンチマーク結果:253.20リクエスト/秒🏆

第三者プラグインまたはテーマをすべて試験して正常に機能しているかを確認する理由で更新していない方も多いかもしれませんが、単純にまだできていないという方も多いでしょう。

ご利用のPHPバージョンを確認する

ご利用のPHPのバージョンが不明な方は、PingdomまたはGoogle ChromeのDevtoolsのようなツールを使って、簡単に確認できます。最初のHTTPリクエストヘッダにはバージョンが表示されているはずです。

Pingdomを使用してPHPのバージョンを確認する
Pingdomを使用してPHPのバージョンを確認する

結果が出るかは、ホスティング会社がX-Powered-Byヘッダーの値を変更していないことによります。しかし、Kinstaを含む多くのホスティング会社がセキュリティ上の理由からその値を変更します。したがってPHPバージョンが表示されないことがあります。その場合、WordPress 5.2以降では、新しいサイトヘルスツールを使用できます。「ツール」→「サイトヘルス」→「情報」にアクセスすると、「サーバー」セクションにサーバーのPHPバージョンが表示されます。

WordPressのサイトヘルスツールでPHPのバージョンを確認する
WordPressのサイトヘルスツールでPHPのバージョンを確認する

あるいは、WordPress管理ダッシュボードのフッターに基本的なサーバー情報を表示する、Version Infoなどの無料のプラグインをインストールすることもできます。または、FTP経由でファイルをアップロードすることも、ホスティング会社に依頼することできます。

PHP 7.4に更新する

Kinstaでは、PHP 7.4の最新版をご利用いただけます。または、WordPressウェブサイトをローカルでテストすること、PHPの複数のバージョンをコマンドラインで試験できるDockerなどの環境でスクリプトを確認するもできます。

簡単にテストを行うには、Kinstaで本番サイトにより近いものになるステージング環境を作成します。MyKinstaダッシュボードでサイトをクリックし、環境をステージングに切り替えます。

WordPressのステージング環境を作成する
WordPressのステージング環境を作成する

次に、「ツール」メニューで、PHPエンジンをPHP 7.4に切り替えて、コード、サードパーティ製のプラグイン、利用中のテーマなどの互換性テストを開始しできます。

以前のPHPのバージョンアップと同様に、当社のPHP自己修復機能は完全にPHP 7.4対応です。どの理由でもPHPが停止した場合、自動的に再起動します。再起動しても問題が解決しない場合は、当社の監視システムが問題の原因を調査するために当社のシステム管理チームに警告を発します。

PHP 7.4への切り替え
PHP 7.4への切り替え

PHP 7.4のKinstaでのリリース.についてはこちらをご参照ください。

DockerにPHP 7.4をインストールして実行する

Dockerをご利用しますか?幸いなことに、PHP 7.4を手動でコンパイルして設定する必要はありません。ご利用のシステムにDockerが既にインストールされている場合は、非公式のPHP-FPM 7.4 Docker Imageをインストールし、数秒でコマンドラインからテストを実行するだけです。

Nginx Docker Imageのインストール
Nginx Docker Imageのインストール

PHP 7.4のコードをブラウザで実行したい方は、NginxまたはApacheのイメージファイルをインストールする必要があります。心配いりません。 開発者の指示に従ってください。DockerのImageページからコマンドをコマンドラインツールにコピーして貼り付けるだけです。

まとめ

本記事では、PHP 7.4のリリースで予想される多数の変更点について説明しました。公式のRFC文書と機能の完全なリストについては、以下の資料をご確認ください。

PHP 7.4とそのリリースに関する最新情報を常にお知らせするようにします。Kinstaのお客さまには、今すぐでもPHPの最新版に切り替えていただけます。

PHP 7.4をインストール、テストする予定ですか?お好みの新機能は何ですか? コメントでも書いて、ご経験について教えてください。

Carlo Daniele Kinsta

ウェブデザインとフロントエンド開発をこよなく愛し、WordPress歴は10年以上。イタリアおよびヨーロッパの大学や教育機関とも共同研究を行う。WordPressに関する記事を何十件も執筆しており、イタリア国内外のウェブサイトや雑誌に掲載されている。詳しい仕事情報はXとLinkedInで公開中。