今年11月にPHP 8.5がリリースされました。PHPは、WordPressを使用するサイトを含むウェブの大部分を長年支えているサーバーサイドスクリプト言語です。

このリリースにより、PHPコミュニティが毎年メジャーアップデートを提供し、その後2年間のアクティブサポートを行うという年間スケジュールへの取り組みも、2年目に突入しました。

PHP 8.5はまだ新しいバージョンですが、Kinstaでは毎年恒例のPHPベンチマークを実施し、主要なCMS/フレームワークでその性能を検証しました。

PHPアプリケーションをバージョン8.5に移行する予定がある方は、まずはPHP 8.5の新機能を把握することをおすすめします。この記事では、コード改善に活用できるかもしれない新機能や、非推奨となる機能など、注目したいPHP 8.5の変更点をご紹介します。

PHP 8.5の新機能と改善点

まずは、PHPのコードベースに新たに追加された機能をご紹介します。これらの変更は通常、将来的にPHPのリリースへ組み込むことを目的とした RFC(Request for Comments)として提案され、承認されることで導入されます。

パイプ演算子による関数呼び出しの連結

パイプ演算子(|>)を使うと、関数呼び出しを左から右へとつないでいくことができます。これは、JavaScriptに慣れている人には馴染みのある書き方です。このパイプは、各ステップで1つの値を次へと受け渡しながら処理を進めます。

これまでは、同様の処理を行うために関数をネストしたり、返り値を段階的に別の関数へ渡すという方法をとる必要がありました。

以下は、パイプ演算子の簡単な使用例です。

$text = ' New-in-php-8.4 ';

$result = $text
    |> trim(...)
    |> (fn($str) => str_replace('4', '5', $str))
    |> (fn($str) => str_replace('-', ' ', $str))
    |> strtoupper(...);

var_dump($result);
// string(14) "NEW IN PHP 8.5"

なお、上記ではtrim()strtoupper()の呼び出しに、PHP 8.1で導入されたfirst-class callable(第一級callable)(...) を使用しています。

先ほどのパイプ処理は1行にもできますが、この演算子の大きな利点は可読性の向上です。また、上記のコードは、これらの処理を(逆順で)ネストして書くと、以下のようにも表現できます。次のようにも表現できます。

$text = " New-in-php-8.4 ";

$result = strtoupper(
    str_replace(‘-, ' ',
        str_replace('4', '5', 
            trim($text)
         )
     )
);

あるいは、以前のバージョンでは以下のような書き方もあります。

$text = " New-in-php-8.4 ";

$result = trim($text);
$result = str_replace('4', '5', $result);
$result = str_replace(‘-, ' ', $result);
$result = strtoupper($result);

新たなURI拡張によるURLの解析

URL(厳密にはURI) は ウェブナビゲーションに欠かせない存在ですが、PHP 4の頃から提供されている parse_url() は、形式の崩れた入力に弱く、サイトのアドレスを操作・検証しようとした際にエラーを引き起こすことがあることでも知られています。

PHP 8.5ではURL 解析を改善するため、RFC 3986WHATWG URL標準をそれぞれサポートするuriparserライブラリとLexborライブラリが取り込まれています。

この新しい URI 拡張を使えば、次のように uriparser ライブラリを呼び出して処理を開始できます。

$uri = new UriRfc3986Uri("https://kinsta.com/blog/php-8-5/"); 

echo $uri->getScheme();       // https
echo $uri->getHost();         // kinsta.com
echo $uri->getPath();         // /blog/php-8-5

または、以下のようにLexbor WHATWG URLライブラリを選択することも可能です。

$uri = new UriWagWgUrl("https://kinsta.com/blog/php-8-5/"); 

echo $uri->getScheme();       // https
echo $uri->getUnicodeHost();  // kinsta.com
echo $uri->getAsciiHost();    // kinsta.com
echo $uri->getPath();         // /blog/php-8-5

上記は、かなり基本的な例です。PHP 8.5のURI拡張が利用している2つのライブラリには共通する機能もありますが、いくつか重要な違いもあります。

大きな違いのひとつは、RFC 3986ライブラリが URI の生の(raw)表現と正規化されたデコード済み(normalized-decoded)表現の両方を扱える点です。これはパーセントエンコードされた入力や出力を扱う際に便利です。

例えばブラウザ上では、以下2つのURIは同一のものとして扱われます。

以前のPHPでは、 rawurldecode()rawurlencode()(これらもRFC 3986に準拠)を使って処理を始めるのが一般的でしたが、PHP 8.5では、URIのコンポーネントがエンコードされているかどうかに関わらず、最初からすべてを適切に扱えるようになっています。

以下は、新しいParsing APIの背後にあるRFCから抜粋した例です。

$uri = new UriRfc3986Uri("https://%61pple:p%61ss@ex%61mple.com/foob%61r?%61bc=%61bc");
  
echo $uri->getRawUserInfo();  // %61pple:p%61ss
echo $uri->getUserInfo();     // apple:pass
 
echo $uri->getRawUsername();  // %61pple
echo $uri->getUsername();     // apple
 
echo $uri->getRawPassword();  // p%61ss
echo $uri->getPassword();     // pass
 
echo $uri->getRawHost();      // ex%61mple.com
echo $uri->getHost();         // example.com
 
echo $uri->getRawPath();      // /foob%61r
echo $uri->getPath();         // /foobar
 
echo $uri->getRawQuery();     // %61bc=%61bc
echo $uri->getQuery();        // abc=abc

新たな拡張でWHATWG URLライブラリを使用する場合、すべてのURIは生として扱われるため、別の表現形式を扱うための専用関数は用意されていません。ただし、このライブラリはURIでよく使われるASCIIとUnicodeの文字を相互に変換することができます。

新max_memory_limit INI ディレクティブでメモリ制限をより厳密に管理

「大きな力には大きな責任が伴う」と言いますが、PHPアプリケーションが使用できるサーバーメモリ量をどこまで許容するかを決めることも、その責任の一つ。割り当て可能なメモリを超えてプロセスが消費してしまえば、アプリケーションがクラッシュする原因になります。

一般的なPHP環境にはphp.iniが含まれており、そこでは各PHPプロセス(またはスレッド)が使用できるメモリの上限を設定するためのディレクティブが定義されています。例えば、128 MBのメモリ上限を設定する場合、INIファイルには以下のように記述します。

// php.ini
memory_limit 128M

いくつかのサーバーでは、PHPアプリケーションの開発者がコード内のini_set()を使用してmemory_limitをその場で上書きすることができます。

ini_set(‘memory_limit’, ‘256M’);
 
// 最大256MBのメモリを必要とするコードを起動

 ini_set('memory_limit', '-1')のように、-1を指定して完全にメモリ制限を外すこともできます。

しかし、実行環境となるサーバーのメモリ構成を十分に理解していない開発者が memory_limitを上書きするのはリスクがあります。複数のPHPスレッドが、利用可能なメモリ総量を超えてメモリを消費しようとすると、実行時に予告なくアプリケーションがクラッシュする可能性があります。

PHP 8.5では、max_memory_limitというINIディレクティブが追加され、これが “ハードリミット” として機能します。つまり、コード内でinit_set()を使ってmemory_limitを変更できる環境であっても、この上限値を突破することはできません。

以下は、PHP 8.5のphp.iniにおける設定例です。

// php.ini
max_memory_limit 256M
memory_limit 128M

max_memory_limitが256 MBの場合、PHPコードでは以下のようになります。

ini_set('memory_limit', '256M');  // OK
ini_set('memory_limit', '512M');  // 警告を出して処理を中断
ini_set('memory_limit', '-1');    // 警告を出して処理を中断

上記のように512 MB(あるいは無制限)のようなメモリ上限を設定しようとしても、php.ini に指定された max_memory_limitの値を上限として使用し、警告を発します(警告メッセージは、PHPエラー報告設定によって、画面に表示されたりログに記録されたりする)。

PHP 8.5を使用する際は、ini_get('max_memory_limit')のように ini_get()を使って最大メモリ制限が定義されているかを確認し、その戻り値に応じて処理を調整するのが賢明です。なお、PHP 8.5より前のバージョンでは、この呼び出しは問題なくfalseを返します。

配列の先頭または末尾の値を取得

実は、PHPには配列の先頭・末尾の値を取得するための関数がこれまで用意されていませんでした。

とはいえ、PHP 7.3以降には先頭キーや末尾キーを取得する関数が追加されていたため、 array_key_first()array_key_last() を使ってキーを取得し、そのキーを使って値にアクセスする、といった方法で対応はできました。

$array = ["One", "Two", "Three"];

echo $array[array_key_first($array)]; // "One"

PHP 8.5では、この手順が省略され、新たに追加された array_first()array_last()を使って、値に直接アクセスできるようになりました。

使い方はとてもシンプルです。

$array = ["One", "Two", "Three"];

echo array_first($array);  // "One"
echo array_last($array);   // "Three"
echo array_last([]);       // null

上の例では、空の配列はnullを返しますが、それだけでは配列全体が空であることは確認できません。

echo array_last([1, 2, null]); // null

関数の返り値を使用するための注意喚起

PHP 8.5では、#[\NoDiscard]属性が追加されました。この属性は「この関数の戻り値は無視すべきではない」と示すためのものです。PHPは、戻り値が実際にどこかで利用されているかを確認し、もしまったく使われていなければ警告を発します。

以下は簡単な例です。

#[NoDiscard("このmessageプロパティは組み込みの警告メッセージに追加されます")]
function foo(): string {
    return 'bar';
}

// 警告
// 関数 foo() の戻り値は利用されることが期待されています
// この message プロパティは組み込みの警告メッセージに追加されます
foo();

// これは警告を発生させません
$result = foo();

// (void) キャストでも同様に警告は回避できます
(void) foo();

上の例では、最初の呼び出しで関数の戻り値がまったく使われていないため、警告が発生します。戻り値を$resultに代入したり、voidキャストを行った場合には、値が消費されたと判断され、警告は出ません。

PHP 8.5にこの属性を導入したRFCでは、上記のような単純なケースよりも、もっと実用的なユースケースが提示されています。たとえば、単なる成功/失敗のフラグ以上に複雑なエラー情報を返す重要な関数があり、その結果を戻り値で伝えることが最も適切な場面などが挙げられています。

属性に関するその他の改善点

#[NoDiscard]属性に加えて、属性メタデータ機能にも以下のような改善点があります。

  • 属性を定数に付与できるように

  • #[\Override]属性がプロパティに適用できるように

  • #[\Deprecated]属性はトレイトと定数に使用可能

  • 新たな#[\DelayedTargetValidation]属性を使うと、不正な対象に属性を付けた場合でも、コアや拡張が出すコンパイル時エラーを抑制可能

PHP 8.5の非推奨機能と削除項目

PHPの各バージョンでは、将来的に削除される予定の機能が「非推奨(デプリケート)」として発表されます。非推奨になった機能を使用すると警告が表示され、削除後は致命的エラーを引き起こす可能性があります。

PHP 8.5で非推奨となった、または削除された機能は以下のとおりです。

  • shell_exec() のエイリアスとして使われていたバッククォート演算子(`command`)が非推奨に

  • 非標準のキャスト名s (boolean)(integer)(double)(binary)が非推奨になり、代わりにそれぞれ(bool)(int)(float)(string)を使用

  • エンジン内部の前提を壊すため、disable_classes INI設定が削除

  •  case文をコロンではなくセミコロンで終える書き方が非推奨に

  • nullを配列のオフセットとして使用すること、またarray_key_exists()に渡すことが非推奨になり、代わりに空文字 ""を使用

  • class_alias()で「array」と「callable」をクラスエイリアス名として使うことは不可

  •  __sleep()__wakeup()はソフトデプリケートになり、代わりに __serialize()__unserialize()を使用

  • NANを他の型にキャストすると警告が出るように

  • 配列以外の値(nullを除く)を []や list()で展開しようとすると警告が出るように

  • float(またはfloatに見える文字列)をintにキャストできない場合、警告が出るように

まとめ

PHP 8.5の主な新機能と改善点をご紹介しました。パイプ演算子や改良されたURIパーサーは、多くの開発者にとって便利な機能になりそうです。また、array_first()array_last()など、ありそうでなかった関数が追加された点も注目に値します。

PHPの新たなリリースには、毎回数百もの変更が含まれています。PHP 8.5の詳細を確認するには、PHP Groupが公開している公式GitHubリポジトリをご覧ください。

Kinstaでは、WordPress専用マネージドクラウドサーバーでPHP 8.5をご利用いただけるよう、現在準備を進めています。サポート開始後、MyKinstaの「PHP設定」セクションから、バージョンアップ可能になります。

Steve Bonisteel Kinsta

Kinstaのテクニカルエディター。救急車や消防車を追いかける記者としてキャリアをスタート。1990年代後半からインターネット関連の技術情報を担当している。