PHP8.6 / PHP8.5 / PHP8.4 / PHP8.3 / PHP8.2
2026/08/13、PHP8.6がフィーチャーフリーズしました。
言語機能に関わるような機能の追加・変更が締め切られたということです。
今後はデバッグを繰り返しながら完成度を高めていき、2026/11/19にPHP8.6.0がリリースされる予定です。
というわけでPHP8.6で実装されるRFCを見てみましょう。
RFC
Partial Function Application
部分適用です。
リンク先で様々な使い方を紹介しているので詳しくはそちらを見てください。
// 普通の関数
function substr5($str)
{
return substr($str, 0, 5);
}
echo substr5('abcdefg'); // abcde
// 部分適用
$f = substr(?, 0, 5);
echo $f('abcdefg'); // abcde
ある関数から、一部の引数だけを適用した関数を簡単に作成することができます。
PHPの場合、既に無名関数やアロー関数があるので、部分適用を単体で使ったところでコード量や書きやすさは実のところそんなに変わりません。
// 無名関数
$f = function ($str) {
return substr($str, 0, 5);
};
echo $f('abcdefg'); // abcde
// アロー関数
$f = fn ($str) => substr($str, 0, 5);
echo $f('abcdefg'); // abcde
それでは何故わざわざ導入されたかというと、それはもちろんパイプライン演算子のためです。
// PHP8.5
$len = random_bytes(10)
|> ( fn($x) => str_repeat($x, 2) )
|> ( fn($x) => str_replace($x, 'c', 'z') )
|> ( fn($x) => substr($x, 5) )
|> strlen(...);
// PHP8.6
$len = random_bytes(10)
|> str_repeat(?, 2)
|> str_replace(?, 'c', 'z')
|> substr(?, 5)
|> strlen(...);
相乗効果により、パイプラインを圧倒的に簡単に書くことが可能になります。
Partial Function Application: Handling of Optional Parameters
部分適用の補足RFCです。
// 例
function example(mixed $a, string $b = 'default', string $c = 'also optional') { }
// $bは必須になる
$c = example(?, ?);
// これと同一になる
$c = static fn (mixed $a, string $b) => example($a, $b);
// こちらではない
$c = static fn (mixed $a, string $b = 'default') => example($a, $b);
プレースホルダ?で部分適用した場合、そのパラメータは元の関数にデフォルト値があるかどうかに関わらず必須になります。
当初のRFCではデフォルト値を引き継ぐ予定だったみたいですが、これだと$thisを絡めた場合などに予期しない動作が起きる可能性があるとのことでした。
仕様変更ということになりますが、部分適用はまだリリースされていないので影響はゼロです。
Duration class
経過時間を表すTime\Durationクラスです。
Time\Duration::fromSeconds(1); // 1秒
正直これ単体だとそこまで便利ってほどでもというかんじですが、今後の展望がたいへん魅力的です。
// 1秒sleep
sleep(\Time\Duration::fromSeconds(1));
usleep(\Time\Duration::fromSeconds(1));
time_nanosleep(\Time\Duration::fromSeconds(1));
// 1時間後
$dt2 = $dt->add(\Time\Duration::fromHours(1));
また今後Time名前空間に、新しい日時ライブラリが搭載される予定だということです。
Polling API
I/O多重化のシステムコールを導入します。
現在、PHPでのIO監視にはstream_select()関数が使われますが、これはパフォーマンスが低く、同時に開けるファイル数も1024個までなど制限があります。
それに対して、こちらを使えばPHPでも数万接続に耐えるTCPサーバをつくったりできるようになります。
ということらしいんだけど、正直さっぱり全然わからない。
use Io\Poll\{Context, Event};
// バックエンド(epoll/kqueue/WSAPoll等)は自動選択される
$poll = new Context();
// サーバソケット
$server = stream_socket_server('tcp://0.0.0.0:8080', $errno, $errstr);
if (!$server) {
die("Failed to create server: $errstr\n");
}
stream_set_blocking($server, false);
// streamをラップ
$serverHandle = new StreamPollHandle($server);
// Polling APIにサーバソケットを追加
$poll->add($serverHandle, [Event::Read], ['type' => 'server']);
echo "Server listening on port 8080\n";
while (true) {
// イベントのあるWatcher
$watchers = $poll->wait(1);
foreach ($watchers as $watcher) {
$data = $watcher->getData();
if ($data['type'] === 'server' && $watcher->hasTriggered(Event::Read)) {
// クライアントから接続される
$handle = $watcher->getHandle();
if ($handle instanceof StreamPollHandle) {
$server = $handle->getStream();
$client = stream_socket_accept($server, 0);
if ($client) {
stream_set_blocking($client, false);
$clientHandle = new StreamPollHandle($client);
$poll->add($clientHandle, [Event::Read], ['type' => 'client']);
echo "New client connected\n";
}
}
} elseif ($data['type'] === 'client') {
$handle = $watcher->getHandle();
if ($handle instanceof StreamPollHandle) {
$stream = $handle->getStream();
if ($watcher->hasTriggered(Event::Read)) {
// クライアントからデータを読み込む
$buffer = fread($stream, 8192);
if ($buffer === false || $buffer === '') {
echo "Client disconnected\n";
$watcher->remove();
fclose($stream);
} else {
echo "Received: $buffer";
// エコーバック
fwrite($stream, "Echo: $buffer");
}
}
if ($watcher->hasTriggered(Event::HangUp) || $watcher->hasTriggered(Event::Error)) {
echo "Client connection error or hangup\n";
$watcher->remove();
fclose($stream);
}
}
}
}
}
使いこなせば、これまでのPHPではできなかった大規模なことができるようになると思われます。
が、私はこれ系を全く使わないのでさっぱりわからん。
#[\Override] for class constants
クラス定数にアトリビュートOverrideを記述できるようになります。
class Base {
protected const C = 'C';
}
class Child extends Base {
#[\Override] // OK
public const C = 'Changed';
}
class Demo {
#[\Override] // エラー
public const C = 'C';
}
PHP8.3でメソッドにOverrideが導入され、PHP8.5ではプロパティにも書けるようになりました。
それに引き続く、順当な拡張ですね。
ていうか#[\DelayedTargetValidation]アトリビュートで『ところでたとえば今後もしPHP8.6で定数へのOverrideアトリビュートが有効になったとしたら』とか言ったけどめちゃ予言になってるなこれ。
class P {
public const NAME = 'P';
}
class C extends P {
#[\DelayedTargetValidation]
#[\Override]
public const NAME = 'C';
}
Minimum supported versions for PHP 8.6
PHPがサポートするAutoconfの最低バージョンが2.71、MySQLが5.7.3、MariaDBが10.2.4になります。
PHP8.5現在、PHPがサポートするAutoconfの最低バージョンは2.68です。
ところで現在PHPがサポートするC言語のバージョンはC11ですが、AutoconfがC11をネイティブサポートしたのは2.71からということです。
ということでバージョンが合わされました。
またMySQL・MariaDBはCOM_RESET_CONNECTIONという安全な再接続コマンドが導入されたのがこのバージョンからということです。
なおMySQL5.7.3は2013/12/03、MariaDB10.2.4は2017/02/17リリースで、さらにどちらもとっくにEOLなので、今さらこんなバージョン使ってる人なんていないでしょう。
Deprecate returning values from __construct() and __destruct()
コンストラクタ・デストラクタから値をreturnすることが非推奨になります。
class Foo
{
public function __construct()
{
return 123; // Deprecated: Returning a value from a constructor is deprecated
}
public function __destruct()
{
return 123; // Deprecated: Returning a value from a destructor is deprecated
}
}
PHP9.0からはコンパイルエラーになる予定です。
そもそも何を返したところでコンストラクタの返り値はインスタンスになるわけで、これまでも全く意味のない返り値でした。
そのため表記と現実のずれをなくすために禁止されます。
ちなみに2020年にもほぼおなじRFCが提出されましたが、当時は却下されていました。
enum SortDirection
型安全なソート順です。
PHPのソート関数は、array_multisortではSORT_ASC・SORT_DESC、scandirではSCANDIR_SORT_ASCENDING・SCANDIR_SORT_DESCENDINGみたいなことになっています。
さらにsortやrsortに至ってはわざわざ個別関数です。
そんなわけで列挙型SortDirectionが追加されました。
enum SortDirection {
case Ascending;
case Descending;
}
ただこのRFCは単にENUMが追加されただけで、実際array_multisort($arr, SortDirection::Ascending);って書けるのかというとそれは今後の課題だということです。
これにはがっかりだよ。
Debugable Enums
ENUMにマジックメソッド__debugInfo()が追加されました。
PHP8.1でENUMが実装された際、使用可能なマジックメソッドは__call()・__callStatic()・__invoke()だけと決定されました。
ENUMは状態を持たないので、__construct()なんかは実装しようがないですね。
しかし__debugInfo()は状態を持たないし欲しいんだけど、という希望があったため導入されました。
enum Foo: string {
case Bar = "Baz";
public function __debugInfo() {
return [__CLASS__ . '::' . $this->name . ' = ' . $this->value];
}
}
var_dump(Foo::Bar);
/*
enum(Foo::Bar) (1) {
[0]=>
string(14) "Foo::Bar = Baz"
}
*/
Add Locale::getDisplayKeyword and Locale::getDisplayKeywordValue functions
関数Locale::getDisplayKeyword()・Locale::getDisplayKeywordValue()が追加されます。
これは単にICUのuloc_getDisplayKeyword()・uloc_getDisplayKeywordValue()を呼び出しているだけのラッパーです。
function locale_get_display_keyword(
string $keyword,
?string $displayLocale = null
): string|false {}
function locale_get_display_keyword_value(
string $locale,
string $keyword,
?string $displayLocale = null
): string|false {}
この手のライブラリに追随するだけのメソッドや定数追加は、これまでRFCなしで行われるのが普通だったのですが、今回はなんでかRFCを通すようにしたようです。
Oniguruma maintenance end and end of mbregex
mb_ereg関数が削除されます。
PHP8.6でE_DEPRECATEDになり、PHP9で削除される予定です。
mb_ereg('(abc)(.*)', 'abc', $match); // Deprecated: Function mb_ereg() is deprecated
PHPの正規表現はpreg_matchとmb_eregの2種類があるのですが、後者はライブラリとしてonigurumaを使っています。
しかしonigurumaが開発終了してしったため、Linux等のOSからも削除されることになりました。
そこでPHPも対応を終了します。
DocComments For Function Parameters
関数の引数にDocCommentを書いて取り出せるようになります。
function search(
/** Terms to search for in database */
string $query,
/** Maximum number of entries returned */
int $num = 10
) {
return ['foo', 'bar', 'qux'];
}
foreach (new ReflectionFunction("search")->getParameters() as $p) {
echo $p->name . ": " . $p->getDocComment() . "\n";
}
/* ↓の2行が出力される
query: /** Terms to search for in database */
num: /** Maximum number of entries returned */
*/
引数以外のクラス・プロパティ・関数等は元々対応していたのですが、引数には対応していなかったので合わせたものです。
Add Form Feed in Trim Functions
trim関数は、第二引数の$charactersのデフォルト値が" \n\r\t\v\x00"です。
これにフォームフィード"\f"を追加して、" \n\r\t\v\x\f00"にします。
他の言語でもtrim対象であるため、それに合わせたものとなります。
これは機能追加というより単に対象漏れのバグといったほうが近いかもしれませんね。
grapheme_strrev: strrev for grapheme cluster
strrevのマルチバイト版です。
$str = '大👨👩👦👦家族';
var_dump(strrev($str)); // 文字化けする
var_dump(grapheme_strrev($str)); // "族家👨👩👦👦大"
strrevはマルチバイトに対応しておらず、そしてmb_strrevは何故か存在しません。
ということで全員一致でさくっと受理されました。
Add "clamp()" function
引数の値を、上限下限の範囲内に収めてくれます。
CSharp、Java、CSSなどにあるclampと同じものです。
clamp(50, min: 0, max: 100); // 50 範囲内ならそのまま
clamp(-10, min: 0, max: 100); // 0 最小値になる
clamp(150, min: 0, max: 100); // 100 最大値になる
clamp (
new \DateTimeImmutable ( '2025-08-01' ),
new \DateTimeImmutable ( '2025-08-15' ),
new \DateTimeImmutable ( '2025-09-15' )
); // 2025-08-15 DateTimeにも使える
PHPならパーセンテージとか、日付を範囲内に収めるとかの処理に使えるのではないでしょうか。
ちなみに昔ほぼ同じRFCが提出されていたのですが、そちらは提案者が多忙で放棄されていました。
isReadable/Writeable Reflection methods
PHP8.4で非対称可視性が設定できるようになりましたが、それを取得するリフレクションメソッドが実装されました。
class A {
public private(set) static int $a = 42;
}
$r = new ReflectionProperty('A', 'a');
var_dump($r->isReadable(null)); // true
var_dump($r->isWritable(null)); // false
Readonly Property Defaults
readonlyプロパティにデフォルト値を設定できるようになります。
readonlyプロパティはPHP8.1で導入されましたが、当時はデフォルト値を許可する意味がなかったので対応されませんでした。
しかしその後PHP8.4でreadonlyプロパティがprovate(set)からprotected(set)になったため、有望な用途が生まれました。
abstract class ParentRule{
public readonly int $priority = 1;
}
final class ChildRule extends ParentRule{
public readonly int $priority = 2;
}
var_dump(new ParentRule()->priority); // int(1)
var_dump(new ChildRule()->priority); // int(2)
有望か?
よくわからない。
Allow Object Property Writes on Objects Referenced by Constants
定数に割り当てたインスタンスのプロパティを変更可能にします。
const OBJ = new stdClass();
OBJ->counter = 0;
OBJ->counter++;
// PHP8.5 Fatal error: Cannot use temporary expression in write context
// PHP8.6 OK
もちろん定数自体の再割り当ては禁止のままです。
他の言語もだいたいこんなかんじの動作ですね。
New function mysqli_quote_string
mysqliにエスケープ関数mysqli_quote_stringを追加します。
$sql = sprintf('SELECT id FROM foo WHERE name=%s' , $mysqli->quote_string($value));
$result = $mysqli->query($sql);
これmysqli_real_escape_stringと何が違うんだよって話ですが、引用符が自動で追加されるだけみたいです。
$sql = sprintf('SELECT id FROM foo WHERE name="%s"', $mysqli->real_escape_string($value));
実はこれSQLインジェクション脆弱性があります。
さあ何処が間違っているでしょうか。
このようにせっかくエスケープ関数を使ったのにうっかりミスが起こることがあるので、文字列結合を使いつつもなるべく安全性の高いエスケープが実装されました。
PDO::quote()と同じ立ち位置の関数です。
もちろん常にプレースホルダを使うべきであり、この関数を使うのは『実行されるはずのSQLを画面表示したいphpMyAdmin』など一部の用途に限られます。
ちなみにMySQLのマニュアルを見ていたらmysql_real_escape_string_quote()とかいう素敵極まりない名前の関数がありました。
TLS Session Resumption Support for Streams
TLSセッションの機能強化です。
これまでもstream_socket_enable_crypto等でTLS接続が可能でしたが、これはリクエストをまたぐと途切れてしまいます。
次回リクエストに接続時したい場合、再度ハンドシェイクから必要となって時間がかかるなどの制限がありました。
これをTLS接続を開いたまま、リクエストをまたいで使い回すことができるようになりました。
// 接続の保存ファイル
$sessionFile = '/tmp/tls_session_api.pem';
// 前回接続があれば取得
$previousSession = null;
if (file_exists($sessionFile)) {
try {
$previousSession = OpenSSLSession::import(file_get_contents($sessionFile));
if (!$previousSession->isResumable()) {
$previousSession = null;
}
} catch (OpenSSLException $e) {
$previousSession = null;
}
}
// ストリーム
$context = stream_context_create([
'ssl' => [
'peer_name' => 'api.example.com',
'session_data' => $previousSession,
'session_new_cb' => function($stream, OpenSSLSession $session) use ($sessionFile) {
file_put_contents($sessionFile, $session->export());
}
]
]);
// 接続実行
$fp = stream_socket_client('tls://api.example.com:443', context: $context);
前回の接続をストリームオプションsession_data・session_new_cbに渡すことで有効化されます。
この$fpは、前回の接続があればハンドシェイクをやりなおさずに、そのまま使い回すということです。
ファイルを手動で保存する必要があるのはちょっと微妙。
Limit maximum number of filter chains
フィルタを連続して書ける数を16個に制限します。
file_get_contents("php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|...[20個のフィルタ].../resource=php://temp");
// Deprecated: Using more than 16 filters in a php://filter URL is deprecated
このフィルタを大量に積み重ねることによりサーバ上のファイルを読み取ったりできるということです。
よく考えつくなこんなの。
まあそもそもユーザ入力値をフィルタに使うなという話ではありますが、万一問題があった場合でも危険性を減らすための軽減策として導入されました。
Stream Error Handling Improvements
ストリームのエラー処理を改善します。
fopenは失敗したらE_WARNINGが出る、stream_socket_clientは失敗したら引数$error_code・$error_messageにエラー値が入る、stream_socket_serverはfalseを返すだけ、と見事にバラバラです。
そこで様々なストリームに共通で利用できるエラーモードを追加します。
$context = stream_context_create([
'stream' => [
'error_mode' => StreamErrorMode::Exception,
]
]);
try {
$src = fopen('source.txt', 'r', false, $context);
$dst = fopen('dest.txt', 'w', false, $context);
stream_copy_to_stream($src, $dst, null, 0, $context);
} catch (StreamException $e) {
echo "Copy failed: " . $e->getMessage() . "\n";
}
ストリーム生成時にStreamErrorMode::Exceptionを渡すと、エラーを一貫して例外として扱えるようになります。
オプションには現在と同じ動作のデフォルト値StreamErrorMode::Error、黙って失敗するStreamErrorMode::Silentがあるようです。
他にも列挙型StreamErrorCodeで原因が特定できたり、関数stream_last_errors()で最後に発生したエラーを取得できたりするみたい。
Followup Improvements for ext/uri
PHP8.5でURIを正しく扱うことのできる機能が追加されました。
この提案は当時からだいぶ大規模だったので、意図的に省かれた仕様が幾つかありました。
今回の提案はそのフォローアップで、ext/uriの使い勝手をさらに良くする機能追加です。
$uri1 = Uri\Rfc3986\Uri::parse("");
$uri2 = $uri1
->withScheme("https")
->withUserInfo("user:pass") // エラーになる
->withHost("example.com"); // 先にドメインを書かないといけない
$uri2 = $uri1
->withScheme("https")
->withHost("example.com")
->withUserInfo("user:pass") // こっちはOK
withXXX()メソッドは、実行するたびにそのURLが正しいかをチェックするので、たとえばドメインより先にパスワードを入れると不正なURLになってしまいます。
そんなわけで書く順番に気をつけないといけないし、毎回チェックが走るからパフォーマンスも悪いしとあまりよくない設計でした。
またRFC3986では""は有効なURLなので上記のように一からURLを作ることが可能だけど、WHATWG URLでは無効なので、""からURLを組み立てることができません。
ということでビルダーパターンが導入されます。
$urlBuilder = new Uri\WhatWg\UrlBuilder()
->setScheme("https")
->setUsername("user")
->setPassword("pass")
->setHost("example.com")
->setPort(8080)
->setPath("/foo/bar")
->setQuery("a=1&b=2"]
->setFragment("section1")
$url = $urlBuilder->build(); // ここで初めてチェックが走る
echo $url->toAsciiString(); // https://user:pass@example.com:8080/foo/bar?a=1&b=2#section1
最後の時点で適切であればよいので、途中の順番を気にしなくてよくなりました。
またURLのチェックもbuild時の一回だけなので、効率もアップしました。
こんなかんじでいくつかの便利機能が追加されます。
$uri = new Uri\Rfc3986\Uri("https://example.com");
echo $uri->getUriType(); // Uri\Rfc3986\UriType::Uri
$url = new Uri\WhatWg\Url("https://[2001:db8::1]/");
echo $uri->getHostType(); // UrlHostType::IpV6
$url = new Uri\WhatWg\Url("https://2130706433/");
echo $url->getAsciiHost(); // 127.0.0.1
Display Function Arguments in Errors
エラーメッセージに引数が表示されるようにします。
chown("/", "calvin");
// PHP8.5 Warning: chown(): Operation not permitted
// PHP8.6 Warning: chown('/', 'calvin'): Operation not permitted
原因調査や対応がやりやすくなります。
ini設定error_include_argsを0にすると、この機能を無効にして元のままの動作にします。
またSensitiveParameterアトリビュートは、もちろんこの設定より優先されます。
SNMP Improvement
SNMPを機能拡張します。
AES192・AES256への対応、SNMP MIBのリセット、出力フォーマットの追加などが含まれます。
まあSNMPが何なのかわかってないので変更内容もよくわからんのだがな!
Add pack()/unpack() support for endianness modifiers on integers
pack()・unpack()に整数のフォーマット修飾子を追加します。
// リトルエンディアン
$data = pack('s<l<q<', -258, -16909060, -72340172838076673);
// ビッグエンディアン
$data = pack('s>l>q>', -258, -16909060, -72340172838076673);
// リトルエンディアン16ビット2つ + ビッグエンディアン32ビット2つ
$data = pack('s<2l>2', 258, -2, 16909060, -16909060);
修飾子>でビッグエンディアン、<でリトルエンディアンを表します。
実はこれまで符号付き64ビットリトルエンディアンを表す方法がなかったのが、l<と書けるようになります。
// PHP8.5
$unpackToSignedInt = static function (string $v) {
$unpacked = unpack('va/Cb/cc', $v);
return ($unpacked['c'] << 24) | ($unpacked['b'] << 16) | $unpacked['a'];
};
// PHP8.6
$value = unpack('l<', $binaryData)[1];
既に書式の存在する符号なし16ビット整数などについては、vとS<がイコールになります。
この記号はPerlの組み込み関数packとからそのまま持ってきたものだそうです。
ややこしすぎる!
Add pack()/unpack() endianness modifiers for floating-point numbers
↑とほぼ全く同じで、浮動小数の対応フォーマットを追加します。
// リトルエンディアン
$data = pack('f<d<', 3.14159, 2.71828);
// ビッグエンディアン
$data = pack('f>d>', 3.14159, 2.71828);
そもそもpackとか全く使わんから使いどころもわかりませぬ。
Closure optimizations
クロージャの最適化。
class Foo {
public $closure;
public function __construct() {
$this->closure = fn($a, $b) => $a + $b;
}
}
メソッド内でクロージャを宣言した場合、自動的に$thisがバインドされます。
これによって循環参照が発生し、インスタンスを破棄したあともガベージコレクションが起こらずメモリに残ったままになります。
これではたいへん無駄なので、なるべくGCが動くように内部処理を効率化します。
具体的には、内部で$thisを使用していないクロージャは内部的にstatic扱いにする、だそうです。
他にも実質同じクロージャは同じにするといった最適化が入るみたいです。
function test() {
return function () {};
}
test() === test();
// PHP8.5 false
// PHP8.6 true
微妙に後方互換性のない変更が入りますが、これで壊れるコードってのはほぼ無いんじゃないかな。
Secure Session Configuration Defaults
INI設定session.use_strict_mode=1がデフォルトになりました。
PHPではPHPSESSID=5f4dcc3b5aa765d61d8327deb882cf99のようなCookieでセッションIDを管理しています。
サーバ側はPHPSESSIDを受け取ったら、サーバ内から/tmp/sess_5f4dcc3b5aa765d61d8327deb882cf99みたいなファイルを探してそれを$_SESSIONに展開します。
これによって、PHPではリクエストをまたいでのデータの継続を実現しています。
この仕組み自体はPHP以外のサーバサイド言語でも同じで、一般的な機能となっています。
ただひとつ、PHPには他言語にない特徴があります。
CookieをPHPSESSID=hogeと適当に書き変えて送ったら、そのCookieで新たなセッションを始めてしまうんですよね。
かつてはこれとは別に、URLにセッションIDを埋め込む設定use_trans_sidというものが存在しました。
これは昔は携帯電話などCookieが使えないブラウザが普通に存在していたためです。
ところが、use_strict_modeとuse_trans_sidを組み合わせることで容易にセッションハイジャックできるという問題が発覚したため、use_trans_sidはPHP5.3でデフォルトオフになり、PHP8.3では設定そのものが削除されました。
use_strict_modeはデフォルトオンのまま残っていたのですが、今回ようやくデフォルトオフになります。
これによって、『存在しないPHPSESSIDが送られてきたら無視する』という、他言語と同じ動作になります。
実はこの問題四半世紀前からやっていたのですが、ようやく決着しそうですね。
Deprecations for PHP 8.6
昔から残っていた微妙な挙動や、曖昧な挙動を廃止します。
PHP8.6で非推奨、PHP9で削除となる見込みです。
function readonly() {
return "foo";
}
const _ = "bar";
var_dump(readonly(), _);
// PHP8.5 foo, bar
// PHP8.6 E_DEPRECATED
PHPのキーワードとして使われているはずのテキストが関数名や定数名として使えます。
これらを使用禁止にします。
function test(){
try {
throw new Exception();
} finally {
return "hoge";
}
}
test();
// PHP8.5 "hoge"
// PHP8.6 E_DEPRECATED
これ、catchしていないのに例外が行方不明になってしまいます。
といって、returnを無視すると今度は逆にfinallyブロックに書いたのに実行されない、となりかねません。
そのため、finallyブロックで値を返すことを禁止します。
<?php
$text = 'SJIS�̕���'; // SJISで"SJISの文字"と書いてある
mb_convert_variables('UTF-8', 'SJIS', $text);
var_dump($text);
// PHP8.5 "SJISの文字"
// PHP8.6 E_DEPRECATED
mb_convert_variablesは、対象を引数で渡すのではなく、渡す側の変数値が直接書き換わるという、極めて意味のわからない設計になっています。
そのため、関数自体を削除してmb_convert_encodingへの移行を促します。
他にもたくさんありますが、ほとんどは元々あんまり使われていない関数やおかしな仕様の整理なので、ひっかかることはそれほどないと思います。
感想
なんといっても一番大きなのは部分適用でしょう。
パイプライン演算子との合わせ技で、今後のPHPの書き味が大きく変わるのではないでしょうか。
しかし、それ以外にもけっこう大きな機能が入っているように見受けられます。
特に非同期、ネットワーク関連の機能強化が目立ちますね。
私はバックエンドサーバ的なものとかを作ったりしないので正直あまりぴんと来ないのですが、そういう人たちにとってはとても大きな期待のかかるバージョンなのではないかと思います。