【PHP7.4】PHP7.4がリリースされたので新機能全部やる

[PHP8.1](https://qiita.com/rana_kualu/items/e350b8ff80f929626eb9) / [PHP8.0](https://qiita.com/rana_kualu/items/c110cb244c3ee38c6859) / [PHP7.4](https://qiita.com/rana_kualu/items/50f9f5735321fe995ff5)

2019/11/28にPHP7.4.0が[リリースされました](https://www.php.net/)。
ということで、ここでは[ドキュメント化されている](https://www.php.net/manual/ja/migration74.php)新機能や変更点を片端から試してみます。

これら以外にもドキュメント化するほどでもない軽微な変更が多々入っているはずですし、単なるバグ修正も山ほどあるのですが、今回はそのあたりには触れません。
把握しきれていませんしね。

# インストール

古いXAMPPが入っていたらディレクトリまるごと削除。
最新の[XAMPP](https://www.apachefriends.org/jp/index.html)をインストール。
[Windows版PHP](https://windows.php.net/download/)から`VC15 x64 Thread Safe`をダウンロード。
解凍したディレクトリを`path\to\xampp\php`にまるごと上書きコピペ。
`php.ini-development`を`php.ini`にコピー。
`php.ini`の`extension_dir`をエクステンションが入ってるディレクトリへのフルパスに変更し、`mbstring`やら`gmp`あたりの必要なエクステンションのコメントアウトを外す。
XAMPPコントロールパネルからApacheを起動して`phpinfo()`とかを表示してPHP7.4.0になっていたら成功。

Linux？ Mac？ Docker？
あなたなら環境構築くらい自力でできるっしょ。


# 新機能とか

## [プロパティ型指定](https://qiita.com/rana_kualu/items/9b4502d229d19635ef34)

プロパティに型が指定できるようになりました。

```php
class HOGE{
	public int $i = 0;
	public string $s = '';
	public ?object $obj;
}

$c = new HOGE();
$c->i = 1;
$c->s = 'string';
$c->obj = new stdClass();

$c->i = 'string'; // Uncaught TypeError: Typed property HOGE::$i must be int, string used
```

PHP7.4最大の特徴といっていいでしょう。


## [アロー関数](https://qiita.com/rana_kualu/items/7fbdd520a7e355599f7d)

アロー関数が使えるようになりました。

```php
$square = fn($x) => $x ** 2;
var_dump($square(2), $square(-5)); // 4, 25
```

ちょっとした使い捨て関数を書くときなどに便利。

```php
$a = 1;
$hoge = fn() => ++$a;

echo $hoge(); // 2
echo $a; // 1
```

PHPではアロー関数の外にある変数は汚染されません。

また、アロー関数の実装に伴い`fn`が予約語になります。
今後`function fn(){}`といった文は書けなくなります。


## [FFI](https://qiita.com/rana_kualu/items/38c9917f634bc7767f1b#foreign-function-interface)

PHP内に他言語を書けるようになりました。

```php
$a = FFI::new("int[10]");
for ($i = 0; $i < 10; $i++) {
	$a[$i] = $i;
}

$p = FFI::cast("int*", $a);
var_dump($p[0]); // 0
var_dump($p[2]); // 2
```

脳が混乱する。
あと複雑な文を入れるとすぐエラーになるんだけどCのエラーなのかPHPのエラーなのかわからなくなる。


## [プリローディング](https://qiita.com/rana_kualu/items/afe544b0f5680e81fabc)

php.iniを設定。

```ini:php.ini
opcache.preload="path\to\preload.php"
```

`opcache.preload`には"プリロードする対象ファイル"ではなく、"プリロードする対象ファイルを読み込むファイル"を指定します。

```php:preload.php
// preload_cache.phpをプリロードする
opcache_compile_file('path\to\preload_cache.php');
```

プリロードさせたい中身は`opcache_compile_file`で呼ばれているファイルに書きます。

```php:preload_cache.php
function h(string $str):string{
	return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
```

Apacheを再起動すると、`h()`がサーバ上のどこからでも使えるようになります。

```php
var_dump(h('a<b>c')); // a&lt;b&gt;c

function h(){} // Fatal error: Cannot redeclare h()
```

関数`h()`がネイティブ関数……のようなものになりました。


## [NULL合体代入演算子](https://qiita.com/rana_kualu/items/e15a9b7c12f175380244)

NULL合体代入演算子`??=`が導入されました。

```php
$arr = [
    null,  // "hoge"
    false, // false
    true,  // true
    '',    // ""
    0,     // 0
    [],    // []
];
foreach($arr as $i){
    $i ??= 'hoge';
    echo $i;
}

$x ??= 'hoge';
echo $x; // "hoge"

$x = $x ?? 'hoge'; // これと同じ
```

変数が未定義もしくはnullであれば、NULL合体代入演算子の後ろの値になります。
それ以外であれば元のままになります。
`Undefined variable`のE_NOTICEを出さない最低限の初期化が簡単に行えます。


## [HASHエクステンションの常時有効化](https://qiita.com/rana_kualu/items/38c9917f634bc7767f1b#always-available-hash-extension)

```php
var_dump(hash_algos()); // ['md2', 'md4', 'md5', …]
```

PHP7.3までは`--disable-hash`オプションで無効にできました。
PHP7.4以降は無効にすることができません。


## [Password Hashing Registry](https://qiita.com/rana_kualu/items/38c9917f634bc7767f1b#password-hash-registry)

エクステンションが独自のハッシュアルゴリズムを追加できるようになりました。
それに伴い、現在使用可能なハッシュアルゴリズムを確認する[password_algos](https://www.php.net/manual/ja/function.password-algos.php)関数が追加されました。

```php
var_dump(password_algos()); // ['2y', 'argon2i', 'argon2id']
```

今後追加アルゴリズムは出てくるでしょうか。


## [openssl_random_pseudo_bytesの改善](https://qiita.com/rana_kualu/items/38c9917f634bc7767f1b#improve-openssl_random_pseudo_bytes)

[openssl_random_pseudo_bytes](https://www.php.net/manual/ja/function.openssl-random-pseudo-bytes.php)関数が異常時に例外を発生するようになりました。

```php
openssl_random_pseudo_bytes(-1); // Fatal error: Uncaught Error: Length must be greater than 0
```

PHP7.3までは例外が出ずにfalseが返ってきていました。


## [mb_str_split](https://qiita.com/rana_kualu/items/38c9917f634bc7767f1b#mb_str_split-split-multibyte-string)

[mb_str_split](https://www.php.net/manual/ja/function.mb-str-split.php)関数が追加されました。

```php
$x = mb_str_split('aAａＡ1１!！あ𩸽', 1);
var_export($x);// ['a', 'A', 'ａ', 'Ａ', '1', '１', '!', '！', 'あ', '𩸽']
```

半角全角入り交じりでも正常に分割してくれます。
これは素晴らしいですね。

```php
$x = mb_str_split('がが👨‍👩‍👦‍👦', 1);
var_export($x);// ['が', 'か', '゙', '👨', '‍', '👩', '‍', '👦', '‍', '👦' ]
```

ZWJシーケンスも[きれいに分割してくれる](https://qiita.com/_sobataro/items/47989ee4b573e0c2adfc#%E6%9B%B8%E8%A8%98%E7%B4%A0%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%81%AB%E9%96%A2%E3%81%99%E3%82%8B-unicode-90-%E4%BB%A5%E9%99%8D%E3%81%A8-80-%E4%BB%A5%E5%89%8D%E3%81%AE%E9%81%95%E3%81%84)模様。


## mbstring.regex_retry_limit

[mbstring.regex_retry_limit](https://www.php.net/manual/ja/mbstring.configuration.php)ディレクティブが追加されました。
デフォルト値は1000000です。

```php
$pattern = '(.*)*^';
$subject = '1234567890123456789012345678901234567890';
mb_ereg($pattern, $subject, $regs); // false
```

マルチバイト正規表現において、マッチ回数が一定値を超えたら検索を打ち切ります。
[pcre.backtrack_limit](https://www.php.net/manual/ja/pcre.configuration.php)と同じく、[ReDoS](https://en.wikipedia.org/wiki/ReDoS)攻撃を防ぐためのものです。

ところでPCRE正規表現のエラーは[preg_last_error](https://www.php.net/manual/ja/function.preg-last-error.php)で取れるんだけど、mb_ereg系のエラーはどうやって調べればいいんだろう。


## [ReflectionReference](https://qiita.com/rana_kualu/items/38c9917f634bc7767f1b#reflection-for-references)

[ReflectionReference](https://www.php.net/manual/ja/class.reflectionreference.php)クラスが追加されました。

```php
$ary = [0, 1, 2];
$ref1 =& $ary[1];
unset($ref1);
$ref2 =& $ary[2];

var_dump(ReflectionReference::fromArrayElement($ary, 0)); // null
var_dump(ReflectionReference::fromArrayElement($ary, 1)); // null
var_dump(ReflectionReference::fromArrayElement($ary, 2)); // ReflectionReference
```

リファレンスかどうかを調べることができます。
が、なんか思っていたのとちがって、何故か配列にしか使うことができないみたいです。


## [数値セパレータ](https://qiita.com/rana_kualu/items/d72854f9aec6d7cf8bc6)

数値リテラルを`_`で区切れるようになりました。

```php
<?php
echo 149_597_870_700; // 149597870700
echo 0x42_72_6F_77_6E; // 0x42726F776E
echo 0b01010100_01101000_01100101_01101111; // 0b01010100011010000110010101101111

echo 1__2; // Parse error: syntax error
```

人の目で見てわかりやすくするためのもので、値は`_`が無い状態と全く同じです。

`_`の使用条件は"数値に挟まれてないといけない"であり、先頭や末尾、小数点や進数記号の前後などには付けられません。
また`__`と連続させることもできません。

## [__toString()が例外を出せる](https://qiita.com/rana_kualu/items/5976b2b4361e6e559644)

マジックメソッド[__toString()](https://www.php.net/manual/ja/language.oop5.magic.php#object.tostring)が中から例外を出せるようになりました。

```php
class HOGE{
	public function __toString()
	{
		throw new Exception('hoge');
	}
}

echo new HOGE(); // Fatal error: Uncaught Exception: hoge
```

PHP7.3までは`Method HOGE::__toString() must not throw an exception`のFatal errorになっていました。


## [共変戻り値・反変パラメータ](https://qiita.com/nishimura/items/0d886870044759e6d5f4)

子クラスでパラメータの型を広げ、返り値の型を狭めることができるようになりました。

```php
class BASE{}
class EXTEND extends BASE{}

class A{
	public function make(EXTEND $param) : BASE{
		return new BASE();
	}
}

class B extends A{
	/** @Override */
	public function make(BASE $param) : EXTEND{
		return new EXTEND();
	}
}

(new A())->make(new EXTEND());
(new B())->make(new BASE());
```

PHP7.3までは親クラスと子クラスの型は全く同じでないとならず、異なっていると`Declaration must be compatible`のFatal errorが出ていました。


## [引数アンパック](https://qiita.com/rana_kualu/items/993fff9662726a004660#spread-operator-in-array-expression)

```php
$a = [1, 2];
$b = 3;
$c = new ArrayObject([4, 5]);

[$a, $b, $c]; // [[1, 2], 3, ArrayObject(4, 5)]

[...$a, $b, ...$c]; // [1, 2, 3, 4, 5]
```

[可変長引数](https://qiita.com/rana_kualu/items/206d786a2be7942c6dc3#variadic-functions-via-)や[関数呼び出し](https://qiita.com/rana_kualu/items/206d786a2be7942c6dc3#argument-unpacking-via-)という特殊な場所でだけ使えていた引数アンパックを、普通に配列中で使えるようになります。
余計なマージとかを行わず単純に配列をくっつけたいときに便利です。

数値キーは無視されて連番が振り直されます。
連想配列を渡すと`Cannot unpack array with string keys`のFatal errorになります。


## [弱い参照](https://qiita.com/rana_kualu/items/993fff9662726a004660#weakrefs)

[WeakReference](https://www.php.net/manual/ja/class.weakreference.php)クラスが追加されました。

```php
$dummy = new stdClass();
$wr = WeakReference::create($dummy);

$wr->get(); // $dummy
unset($dummy);
$wr->get(); // null
```

毎回インスタンス使い捨てのPHPでどういうときに使えばいいのか、正直わかりません。
[WeakRef](https://www.php.net/manual/ja/class.weakref.php)との違いもわかりません。


## [マジックメソッド`__serialize`/`__unserialize`](https://qiita.com/rana_kualu/items/993fff9662726a004660#new-custom-object-serialization-mechanism)

マジックメソッド[`__serialize`/`__unserialize`](https://www.php.net/manual/ja/language.oop5.magic.php)が追加されました。
`__sleep`および[Serializable](https://www.php.net/manual/ja/class.serializable.php)にかわる、新たなシリアライズのメカニズムです。

```php:unserializeに失敗するバグ
class ObjectWithReferences
{
    protected $var1;
    protected $var2;

    public function __construct()
    {
        $this->var1 = new StdClass();
        $this->var2 = $this->var1;
    }
}

class WrapperObject implements Serializable
{
    private $obj;

    public function __construct($obj)
    {
        $this->obj = $obj;
    }

    public function getObject()
    {
        return $this->obj;
    }

    public function serialize()
    {
        unserialize(serialize(new \StdClass)); // ???
        return serialize($this->obj);
    }

    public function unserialize($serialized)
    {
        $this->obj = unserialize($serialized);
    }
}

$wrapper = new WrapperObject(new ObjectWithReferences());
var_dump($wrapper->getObject()); // ObjectWithReferences

$wrapper = unserialize(serialize($wrapper)); // Notice: unserialize(): Error at offset 82 of 83 bytes
var_dump($wrapper->getObject()); // false
```

これは[バグレポ](https://bugs.php.net/bug.php?id=66052)に上がっていた例です。
`WrapperObject::serialize`メソッドに何もしない`serialize/unserialize`がありますが、これが入っていると何故か`unserialize`に失敗します。
新たなシリアライズシステムではこのような問題が起こりません。

```php:__serialize
class WrapperObject
{
    private $obj;

    public function __construct($obj)
    {
        $this->obj = $obj;
    }

    public function getObject()
    {
        return $this->obj;
    }

    public function __serialize(): array
    {
        return (array) $this->obj;
    }

    public function __unserialize(array $data)
    {
        return $this->obj = (object) $data;
    }
}

$wrapper = new WrapperObject(new ObjectWithReferences());
var_dump($wrapper->getObject()); ObjectWithReferences

$wrapper = unserialize(serialize($wrapper));
var_dump($wrapper->getObject()); // stdClass
```

こっちならとても簡単。
ただし受け渡しは配列で行わないといけないので、そのあたりは手動で実装が必要になります。
上記例は手抜きしているので元に戻りません。

なお`Serializable`と`__serialize/__unserialize`両方を入れた場合は`__serialize/__unserialize`だけが動きます。


## unserialize max_depth

[unserialize](https://www.php.net/manual/ja/function.unserialize.php)関数にオプション`max_depth`が追加されました。

```php
$array = [ 1=>[ 2=>[ 3=>[ 4=>[ 5 ] ] ] ] ];

$ser = serialize($array);

unserialize($ser, []); // [ 1=>[ 2=>[ 3=>[ 4=>[ 5 ] ] ] ] ]
unserialize($ser, ['max_depth'=>1]); // false unserialize(): Maximum depth of 1 exceeded
```

名前からすると深い階層を無視するオプションのように見えますが、実際は`max_depth`を超える階層が存在したら`unserialize`自体が失敗します。


## [PEAR](https://qiita.com/rana_kualu/items/cda171324e98124c8d23)

PEARがデフォルトでインストールされなくなります。

```php
require_once("Auth/Auth.php"); // failed to open stream: No such file or directory
```

手動でインストールすれば当然ながら今後も使用可能です。
またコンパイルオプション`--with-pear`を指定することでもインストールできますが、このオプションは非推奨で、今後削除される可能性があります。


## Curl

PHPというより、同梱されるCurlのバージョンに依るものです。

```php
$cfile = new CURLFile('https://www.google.com/images/srpr/logo1w.png','image/png','testpic');
```

libcurlのバージョンが`7.56.0`以降であれば、[CURLFile](https://www.php.net/manual/ja/class.curlfile.php)にURLを指定できます。

また定数[CURLPIPE_HTTP1](https://www.php.net/manual/ja/curl.constants.php)がE_DEPRECATEDになりました。
[libcurlでdeprecateになったため](https://curl.haxx.se/libcurl/c/CURLMOPT_PIPELINING.html)です。
libcur`7.62.0`以降は使えなくなります。


## FILTER_VALIDATE_FLOAT

検証フィルタ[FILTER_VALIDATE_FLOAT](https://www.php.net/manual/ja/filter.filters.validate.php)がオプション`min_range`/`max_range`に対応し、FILTER_VALIDATE_INTと同じ挙動になりました。

```php
filter_var(10.1, FILTER_VALIDATE_FLOAT, [
	'options'=>[
		'min_range' => 1,
		'max_range' => 10,
	]
]); // false
```

むしろ何故[今まで対応していなかった](https://qiita.com/mpyw/items/5a7242269f51dfabc973)のだろう。


## IMG_FILTER_SCATTER

GDに[画像フィルタ定数](https://www.php.net/manual/ja/function.imagefilter.php)IMG_FILTER_SCATTERが追加されました。

```php
$img = imagecreatefrompng('image.png');
imagefilter($img, IMG_FILTER_SCATTER , 3, 5);
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
```

点描のようなかんじに画像をぼかします。
第三、第四引数でぼかし度合いを調整できます。

```php
imagefilter($img, IMG_FILTER_SCATTER, 3, 10); // 左
imagefilter($img, IMG_FILTER_SCATTER, 1, 100); // 右
```

![image01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/62ee8aca-2810-55ff-01b9-f0645091db4a.png)


## 正規表現フラグPREG_OFFSET_CAPTURE / PREG_UNMATCHED_AS_NULL

[preg_replace_callback](https://www.php.net/manual/ja/function.preg-replace-callback.php)および[preg_replace_callback_array](https://www.php.net/manual/ja/function.preg-replace-callback-array.php)が、正規表現フラグPREG_OFFSET_CAPTUREとPREG_UNMATCHED_AS_NULLを受け取るようになりました。

```php
$subject = 'abcdedcba';
$pattern = '|.c.|';

$callback = function($matches){
	// 第5引数が無い場合、 $matches = ['bcd'] / ['dcb']
	// PREG_OFFSET_CAPTUREがある場合、 $matches = ['bcd', 1] / ['dcb', 5]
    return '';
};

preg_replace_callback($pattern, $callback, $subject, -1, $count, PREG_OFFSET_CAPTURE);
```

PREG_OFFSET_CAPTUREがあると、マッチした位置も一緒にコールバック関数に渡ってきます。
引数の形が変わることに注意しましょう。

PREG_UNMATCHED_AS_NULLは、サブパターンがマッチしなかったときにnullで埋めて送られてきます。

```php
preg_match('/(A)|(B)|(C)/', 'B', $matches, PREG_UNMATCHED_AS_NULL); // $matches = ["B", null, "B", null]

preg_match('/(A)|(B)|(C)/', 'B', $matches); // $matches = ["B", "", "B"]
```

よくわからないので詳細は[コメント](https://qiita.com/rana_kualu/items/50f9f5735321fe995ff5#comment-81b8f0944bb28fa470a9)を参照してください。



## PDO DSN

PDOのDSNにユーザ名userとパスワードpasswordを書けるようになりました。

```php
$dsn = 'mysql:dbname=test;host=127.0.0.1;user=testuser;password=testpass';
$pdo = new PDO($dsn);
```

元々Postgresだけ対応していたのが、MySQLなどその他のデータベースにも書けるようになったとのことです。
これは地味に便利では。

DSNとコンストラクタが両方指定された場合はコンストラクタが優先されます。


## PDO ?のエスケープ

SQLの構文中において、`?`を`??`でエスケープできるようになりました。

```php
$sql = "SELECT * FROM my_table WHERE my_col ?? 'my_key'";
```

SQL構文中で`?`と書くとプレースホルダと解釈されてしまいますが、それを回避することができます。
これまでPostgresの[?演算子](https://www.postgresql.org/docs/9.5/functions-json.html)を書くことができなかったため、その対策です。

文字列値としては、これまでもこれからも普通に書けます。


## strip_tags

[strip_tags](https://www.php.net/manual/ja/function.strip-tags.php)関数の第二引数を配列で渡せるようになりました。

```php
$str = '<a><b><i><u>テキスト</u></i></b></a>';
strip_tags($str, ['b', 'u']); // <b><u>テキスト</u></b>
```

むしろ今までできなかったのかよ。

なお、配列で渡す場合はタグの括弧は不要です。


## array_merge

[array_merge](https://www.php.net/manual/ja/function.array-merge.php)関数と[array_merge_recursive](https://www.php.net/manual/ja/function.array-merge-recursive.php)関数の第一引数を省略できるようになりました。

```php
array_merge(); // []
```

空の配列が返ります。
おそらくスプレッド構文に空の配列を渡してしまったとき用。

```php
$arr = [];
array_merge(...$arr); // PHP7.3まではE_WARNING
```


## proc_open

[proc_open](https://www.php.net/manual/ja/function.proc-open.php)関数の第一引数を配列で渡せるようになりました。

```php
proc_open(['php', '-r', 'echo "Hello World\n";'], $descriptors, $pipes);
```

何がうれしいのかってOSコマンドインジェクションを考えなくて済むようになります。

また、第二引数がリダイレクタとnullに対応しました。

```php
proc_open($cmd, [2 => ['redirect', 1]], $pipes); // 2>&1
proc_open($cmd, [2 => ['null']], $pipes); // 2>null
```

わりとexecってやっちゃうタイプなので、個人的にはあまり使わない関数です。


## [pcntl_unshare](https://qiita.com/sj-i/items/0c041fa6276d81d6dad5#pmmaga-implement-pcntl_unshare)

[pcntl_unshare](https://www.php.net/manual/ja/function.pcntl-unshare.php)関数が追加されました。
[他プロセスと共有しているコンテキストを分離する](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/unshare.2.html)とかなんとからしいのだけど、Windowsでは動かないのでよくわかりません。


## SplPriorityQueue

[SplPriorityQueue::setExtractFlags](https://www.php.net/manual/ja/splpriorityqueue.setextractflags.php)に0を渡すと即座に例外を出すようになりました。

```php
$queue = new SplPriorityQueue();
$queue->setExtractFlags(0); // Fatal error: Uncaught RuntimeException: Must specify at least one extract flag
$queue->insert('A', 1);
$queue->top();
```

PHP7.3までは、`setExtractFlags`した時点ではエラーは起こらず、その後`top`したところでFatal errorが起きていました。


## MB_ONIGURUMA_VERSION

定数[MB_ONIGURUMA_VERSION](https://www.php.net/manual/ja/mbstring.constants.php)が追加されました。

```php
echo MB_ONIGURUMA_VERSION; // 6.9.3
```

正規表現エンジン鬼車のバージョンがわかるようになります。


## 鬼車

鬼車がPHP本体にバンドルされなくなりました。
かわりにlibonigを導入しなければならないそうです。

手元のXAMPPにはそんなファイルがなかったのですが、mb正規表現は普通に動いていました。
どうして動いているのかはわかりません。


## get_declared_classes

[get_declared_classes](https://www.php.net/manual/ja/function.get-declared-classes.php)関数が、まだインスタンス化されていない無名クラスを返さなくなりました。

```php
$class1 = new class {};
var_dump(get_declared_classes()); // $class2は入ってない
$class2 = new class {};
```

PHP7.3までは一覧に`$class1`も`$class2`も出てきていました。
PHP7.4以降は`$class1`しか出てきません。


## imagecreatefromtga

[imagecreatefromtga](https://www.php.net/manual/ja/function.imagecreatefromtga.php)関数が追加されました。

```php
$img = imagecreatefromtga('image.tga');
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
```

誰得にも程があるのではないか。


## ファイル末尾の`<?php`

```text
最後に改行がない
<?php
```

ファイル末尾に改行を入れずに`<?php`とだけ書くと、これまでは`<?php`という文字列と解釈されていました。
すなわち`<?php`という文字列がHTMLに出力されていました。
PHP7.4以降はPHP開始タグと解釈されるようになります。
その後が何もないので、実質的には何もしません。


## STREAM_OPTION_READ_BUFFER

includeやrequireをストリームで使用している場合、[streamWrapper::stream_set_option](https://www.php.net/manual/ja/streamwrapper.stream-set-option.php)が`STREAM_OPTION_READ_BUFFER`オプション付きで呼ばれるようになりました。
カスタムストリームラッパーを自作している場合、それに対する実装が必要です。


## 定数PASSWORD_XXXの値変更

定数[PASSWORD_XXX](https://www.php.net/manual/ja/password.constants.php)の定数値が変更になりました。

```php
echo PASSWORD_ARGON2ID; // argon2id
```

例としてPASSWORD_DEFAULTは1からnullに、PASSWORD_ARGON2IDは3から"argon2id"になります。
[正しい実装](https://qiita.com/rana_kualu/items/3ef57485be1103362f56#%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E3%81%AE%E8%AA%8D%E8%A8%BC)をしているかぎりは影響ありません。


## fread / fwrite

[fread](https://www.php.net/manual/ja/function.fread.php) / [fwrite](https://www.php.net/manual/ja/function.fwrite.php)が失敗時にfalseを返すようになりました。

```php
$fp = fopen('/path/to/dummy', 'a');
fread($fp, 100); // false
```

PHP7.3までは0や""が返ってきていました。


## DateIntervalの曖昧な比較

DateIntervalの曖昧な比較ができなくなりました。

```php
new DateInterval('P1D') == new DateInterval('P1D'); // Warning: Cannot compare DateInterval objects
```

E_WARININGが発生し、たとえ同じ値であろうとも常にfalseが返ってきます。
PHP7.3までは異なる値であろうが常にtrueとなっていました。

厳密な比較は常にfalseで、警告も発生しません。

```php
$dti = new DateInterval('P1D');
$dti2 = $dti;
var_dump($dti == $dti2, $dti === $dti2); // true, true
```

同じオブジェクトであれば当然trueであり、E_WARININGも発生しません。


## リフレクションのシリアライズ

リフレクションをserializeするとFatal errorが発生するようになりました。

```php
$ref = new ReflectionClass(new stdClass());
serialize($ref); // Serialization of 'ReflectionClass' is not allowed
```

リフレクションのシリアライズはこれまでもサポートされておらず、壊れることがありました。
PHP7.4では明確に禁止されます。


## get_object_vars

[get_object_vars](https://www.php.net/manual/ja/function.get-object-vars.php)に[ArrayObject](https://www.php.net/manual/ja/class.arrayobject.php)を突っ込んでも、値が取れなくなりました。

```php
$arr = new ArrayObject([1, 2, 3]);
get_object_vars($arr); // []
```

PHP7.3までは`[1, 2, 3]`が返ってきました。
これによって[ReflectionObject::getProperties](https://www.php.net/manual/ja/reflectionclass.getproperties.php)、[Iterator](https://www.php.net/manual/ja/class.iterator.php)等が影響を受けます。

`(array)$arr`キャストは影響を受けず、値を取得することができます。


## get_mangled_object_vars

[get_mangled_object_vars](https://www.php.net/manual/ja/function.get-mangled-object-vars.php)関数が追加されました。

```php
class A {
    public $pub = 1;
    protected $prot = 2;
    private $priv = 3;
}
class B extends A {
    private $priv = 4;
}

$obj = new B;
$obj->dyn = 5;
$obj->{"6"} = 6;

get_mangled_object_vars($obj); // [ ["Bpriv"]=> int(4), ["pub"]=> int(1), ["*prot"]=> int(2), ["Apriv"]=> int(3), ["dyn"]=> int(5), [6]=> int(6) ]get_object_vars($obj); // [ ["pub"]=> int(1), ["dyn"]=> int(5), [6]=> int(6) ]
```

get_object_varsとだいたい同じですが、privateな値までナチュラルに取ってきます。
いいのかこれ？

なお、ArrayObjectの値はこちらを使っても取得できません。


## Countable SimpleXMLElement

[SimpleXMLElement](https://www.php.net/manual/ja/class.simplexmlelement.php)が[Countable](https://www.php.net/manual/ja/class.countable.php)をimplementsしました。

```php
$xmlstr = <<<XML
<a>
  <bs>
    <b>1</b>
    <b>2</b>
    <b>3</b>
  </bs>
</a>
XML;

$xml = new SimpleXMLElement($xmlstr);
echo count($xml); // 1
echo count($xml->bs); // 1
echo count($xml->b); // 3
```

実は[ずっと昔から](https://bugs.php.net/bug.php?id=65215)`Countable`でもないのにcountできていたので、単に現状を実態に合わせたというものです。


## sapi_windows_set_ctrl_handler

[sapi_windows_set_ctrl_handler](https://www.php.net/manual/ja/function.sapi-windows-set-ctrl-handler.php)関数が追加されました。

```php
sapi_windows_set_ctrl_handler(
    function (int $evt) {
        if ($evt === PHP_WINDOWS_EVENT_CTRL_C) {
            echo 'Ctrl+C pressed.';
            exit();
        } elseif ($evt === PHP_WINDOWS_EVENT_CTRL_BREAK) {
            echo 'Ctrl+Break pressed.';
            exit();
        }else{
            echo 'What pressed???' , $evt;
        }
    }
);

while (1) {
    usleep(100);
}
```

WindowsのCLIで`Ctrl+C`と`Ctrl+Break`をハンドリングできます。

WindowsのCLIでPHPを動かしてる人なんてどんだけ居るんだよ(鏡を見つつ)。


## [OpenSSL](https://qiita.com/sj-i/items/0fb80b1cf214160405f0#dasprid-add-openssl_x509_verify-function)

[openssl_x509_verify](https://www.php.net/manual/ja/function.openssl-x509-verify.php)関数が追加されました。

```php
$crt = <<<CRT
-----BEGIN CERTIFICATE-----
MIIDbDCCAtWgAwIBAgIJAK7FVsxyN1CiMA0GCSqGSIb3DQEBBQUAMIGBMQswCQYD
VQQGEwJCUjEaMBgGA1UECBMRUmlvIEdyYW5kZSBkbyBTdWwxFTATBgNVBAcTDFBv
cnRvIEFsZWdyZTEeMBwGA1UEAxMVSGVucmlxdWUgZG8gTi4gQW5nZWxvMR8wHQYJ
KoZIhvcNAQkBFhBobmFuZ2Vsb0BwaHAubmV0MB4XDTA4MDYzMDEwMjg0M1oXDTA4
MDczMDEwMjg0M1owgYExCzAJBgNVBAYTAkJSMRowGAYDVQQIExFSaW8gR3JhbmRl
IGRvIFN1bDEVMBMGA1UEBxMMUG9ydG8gQWxlZ3JlMR4wHAYDVQQDExVIZW5yaXF1
ZSBkbyBOLiBBbmdlbG8xHzAdBgkqhkiG9w0BCQEWEGhuYW5nZWxvQHBocC5uZXQw
gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMteno+QK1ulX4/WDAVBYfoTPRTz
e4SZLwgael4jwWTytj+8c5nNllrFELD6WjJzfjaoIMhCF4w4I2bkWR6/PTqrvnv+
iiiItHfKvJgYqIobUhkiKmWa2wL3mgqvNRIqTrTC4jWZuCkxQ/ksqL9O/F6zk+aR
S1d+KbPaqCR5Rw+lAgMBAAGjgekwgeYwHQYDVR0OBBYEFNt+QHK9XDWF7CkpgRLo
Ymhqtz99MIG2BgNVHSMEga4wgauAFNt+QHK9XDWF7CkpgRLoYmhqtz99oYGHpIGE
MIGBMQswCQYDVQQGEwJCUjEaMBgGA1UECBMRUmlvIEdyYW5kZSBkbyBTdWwxFTAT
BgNVBAcTDFBvcnRvIEFsZWdyZTEeMBwGA1UEAxMVSGVucmlxdWUgZG8gTi4gQW5n
ZWxvMR8wHQYJKoZIhvcNAQkBFhBobmFuZ2Vsb0BwaHAubmV0ggkArsVWzHI3UKIw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCP1GUnStC0TBqngr3Kx+zS
UW8KutKO0ORc5R8aV/x9LlaJrzPyQJgiPpu5hXogLSKRIHxQS3X2+Y0VvIpW72LW
PVKPhYlNtO3oKnfoJGKin0eEhXRZMjfEW/kznY+ZZmNifV2r8s+KhNAqI4PbClvn
4vh8xF/9+eVEj+hM+0OflA==
-----END CERTIFICATE-----
CRT;

$rightKey = <<<KEY
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLXp6PkCtbpV+P1gwFQWH6Ez0U
83uEmS8IGnpeI8Fk8rY/vHOZzZZaxRCw+loyc342qCDIQheMOCNm5Fkevz06q757
/oooiLR3yryYGKiKG1IZIiplmtsC95oKrzUSKk60wuI1mbgpMUP5LKi/Tvxes5Pm
kUtXfimz2qgkeUcPpQIDAQAB
-----END PUBLIC KEY-----
KEY;

$wrongKey = <<<KEY
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArbUmVW1Y+rJzZRC3DYB0
kdIgvk7MAday78ybGPPDhVlbAb4CjWbaPs4nyUCTEt9KVG0H7pXHxDbWSsC2974z
dvqlP0L2op1/M2SteTcGCBOdwGH2jORVAZL8/WbTOf9IpKAM77oN14scsyOlQBJq
hh+xrLg8ksB2dOos54yDqo0Tq7R5tldV+alKZXWlJnqRCfFuxvqtfWI5nGTAedVZ
hvjQfLQQgujfXHoFWoGbXn2buzfwKGJEeqWPbQOZF/FeOJPlgOBhhDb3BAFNVCtM
3k71Rblj54pNd3yvq152xsgFd0o3s15fuSwZgerUjeEuw/wTK9k7vyp+MrIQHQmP
dQIDAQAB
-----END PUBLIC KEY-----
KEY;

openssl_x509_verify($crt, $rightKey); // 1
openssl_x509_verify($crt, $wrongKey); // 0
openssl_x509_verify($crt, 'hoge');    // -1
```

[X.509証明書](https://qiita.com/n-i-e/items/35cba71d04b9123e676c)のチェックができます。
こんなことわざわざPHPでするなよって気もしますが。


## idn_to_utf8

[idn_to_utf8](https://www.php.net/manual/ja/function.idn-to-utf8.php)および[idn_to_ascii](https://www.php.net/manual/ja/function.idn-to-ascii.php)関数の引数variantのデフォルト値がINTL_IDNA_VARIANT_UTS46になりました。

```php
echo idn_to_utf8('xn--2-kq6aw43af1e4y9boczagup'); // 中島第2駐輪場
```

そういえば日本語ドメイン最近見ませんね。


## token_get_all

[token_get_all](https://www.php.net/manual/ja/function.token-get-all.php)関数が、不正な文字列に[トークンシンボル](https://www.php.net/manual/ja/tokens.php)T_BAD_CHARACTERを返すようになりました。

```php
$tokens = token_get_all('<?php ' . chr(0));
echo token_name($tokens[1][0]); // T_BAD_CHARACTER
```

これまでは解釈できない文字は単に無視されていたのですが、そのような文字を含めて正確にパースできるようになりました。

正しくないソースを正しく解析しても仕方ない気がしますが。


## [pkg-config](https://www.php.net/manual/ja/migration74.other-changes.php#migration74.other-changes.pkg-config)

設定を`pkg-config`に寄せていくために、多くのコンパイルオプションがディレクトリ指定できなくなります。
例として[Curl](https://www.php.net/manual/ja/curl.installation.php)は、コンパイルオプション`--with-curl=path/to/dir`とすることでディレクトリを指定することができました。
PHP7.4以降は`--with-curl`にはオプションを設定できず、ディレクトリ指定することができません。

また`-with-png-dir`のような、直接的にディレクトリを指定するオプションは削除されます。

まあ、最近は自力でインストールとかあまりしませんけどね。


## [演算子+-.の優先順位変更](https://qiita.com/rana_kualu/items/db7ae541016bd0b02122)

演算子`+-`と`.`を並べて使うとE_DEPRECATEDが起こるようになりました。
PHP8において演算子`.`の優先順位を下げるための布石です。

```php
$a = 1;
$b = 2;

echo "sum: " . $a + $b; // Deprecated: The behavior of unparenthesized expressions containing both '.' and '+'/'-' will change in PHP 8
```

PHP7.3までは前から順に`("sum: " . $a) + $b`と解釈され、答えは2になっていました。
PHP7.4では、解釈は同じですが同時にE_DEPRECATEDが発生します。

PHP8以降では`"sum: " . ($a + $b)`と解釈されて、答えは"sum: 3"になります。


## [三項演算子のネスト制限](https://qiita.com/rana_kualu/items/0de1e406f72c0ef86432)

解釈が一意に定まらない三項演算子のネストはPHP7.4でE_DEPRECATEDに、PHP8でエラーになります。

```php
1 ? 2 : 3 ? 4 : 5; // Deprecated: Unparenthesized `a ? b : c ? d : e` is deprecated.
(1 ? 2 : 3) ? 4 : 5; // ok 解釈が一意
1 ? 2 : (3 ? 4 : 5); // ok 解釈が一意
1 ? 2 ? 3 : 4 : 5; // ok 解釈が一意
1 ?: 2 ?: 3; // ok 解釈が一意
```

その後の予定は未定ですが、おそらくPHP9あたりで他言語と同じ解釈に変更されると思われます。


## [波括弧による文字列アクセス](https://qiita.com/rana_kualu/items/6ac293f238c4e6000720)

```php
$string = 'ABCDEFG';

echo $string[1]; // B
echo $string{2}; // Deprecated: Array and string offset access syntax with curly braces is deprecated
```

文字列に波括弧でアクセスするとE_DEPRECATEDが発生します。
PHP7.3まではエラーが出ずにアクセス可能でした。

PHP7.4リリース時点では、今後削除される予定はありません。


## [角括弧による文字列以外へのアクセス](https://qiita.com/rana_kualu/items/eef7c214b8bf692a5eeb)

```php
$int = 1234567980;
echo $int[1]; // Notice: Trying to access array offset on value of type int

$null = null;
echo $null[0]; // Notice: Trying to access array offset on value of type null
```

int、float、bool、null、resourceに角括弧でアクセスするとE_NOTICEが発生します。
これまではエラーは出ませんでしたが、値はnullでした。

配列やstringへの`[]`アクセスは当然ながら今後も可能です。


## htmlentities

[htmlentities](https://www.php.net/manual/ja/function.htmlentities.php)関数に、UTF-8以外のマルチバイト文字列を渡すとE_NOTICEが発生するようになります。

```php
htmlentities('abc', ENT_COMPAT, 'Shift_JIS'); // htmlentities(): Only basic entities substitution is supported for multi-byte encodings
```

PHP7.1.25以降はE_STRICTが発生し、それ以前は何のエラーも起こりませんでした。
どういう意図なのかはよくわかりません。


## parent without parent

親クラスのないクラスで`parent`を使うと、そのメソッドが呼ばれなくてもコンパイル時にE_DEPRECATEDが発生するようになります。

```php
class Dummy{
    public function hoge(){
        return parent::hoge();
    }
}

// Deprecated: Cannot use "parent" when current class scope has no parent
```

PHP7.3までは、メソッドを呼ばないかぎり何も起きませんでした。

メソッドを呼び出すと、PHP7.3でも7.4でも当然Fatal errorが発生します。


## BCMath

[BCMath](https://www.php.net/manual/ja/book.bc.php)関数に数値形式でない文字列を渡すとE_WARNINGが発生するようになりました。

```php
echo bcadd("2", "3a"); // Warning: bcadd(): bcmath function argument is not well-formed
```

PHP7.3までは何も出ませんでした。

値そのものは、昔から0として扱われていました。
上記例でいうと、出力はPHP5時代からずっと"2"のままです。
解釈が通常のPHP関数と異なるので少々わかりにくいですね。


## base_convert

[base_convert](https://www.php.net/manual/ja/function.base-convert.php)関数に無効な文字が渡された場合、E_DEPRECATEDが発生するようになりました。

```php
base_convert('012', 2, 10); // Deprecated: Invalid characters passed for attempted conversion, these have been ignored
```

PHP7.3までは何のエラーも出ませんでした。
いずれにせよ、途中に出てくる無効な値は単に無視されます。

base_convertのほか、[hexdec](https://www.php.net/manual/ja/function.hexdec.php)、[octdec](https://www.php.net/manual/ja/function.octdec.php)、[bindec](https://www.php.net/manual/ja/function.bindec.php)にも同じ変更が入ります。
単に全ての関数が同じ内部関数[_php_math_basetozval](https://github.com/php/php-src/blob/php-7.4.0/ext/standard/math.c#L915)を使っているからという理由ですが。


## [socket_addrinfo_lookup](https://qiita.com/sj-i/items/afad3d28515195c2e363#viest-deprecate-ai_idn_allow_unassigned-and-ai_idn_use_std3_ascii_rules)

[socket_addrinfo_lookup](https://www.php.net/manual/ja/function.socket-addrinfo-lookup.php)関数において、引数AI_IDN_ALLOW_UNASSIGNEDとAI_IDN_USE_STD3_ASCII_RULESがE_DEPRECATEDになりました。
そもそもマニュアルが日本語化されてすらいないほど、この関数自体が使われていません。


## Deprecate LDAP

[ldap_control_paged_result_response](https://www.php.net/manual/ja/function.ldap-control-paged-result-response.php)と[ldap_control_paged_result](https://www.php.net/manual/ja/function.ldap-control-paged-result.php)がE_DERECATEDになりました。
かわりに[ldap_search](https://www.php.net/manual/ja/function.ldap-search.php)を使えということだそうです。

また、`nsldap`と`umich_ldap`のサポートが削除されました。

何のことだかさっぱりわかりません。


## Deprecate is_real

[is_real](https://www.php.net/manual/ja/function.is-real.php)関数および`(real)`キャストはPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。

```php
is_real(1); // Deprecated: Function is_real() is deprecated
is_float(1); // OK
```

is_realではなく[is_float](https://www.php.net/manual/ja/function.is-float.php)を使いましょう。


## Deprecate Magic quotes

[get_magic_quotes_gpc](https://www.php.net/manual/ja/function.get-magic-quotes-gpc.php)および[get_magic_quotes_runtime](https://www.php.net/manual/ja/function.get-magic-quotes-runtime.php)関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。

```php
get_magic_quotes_gpc(); // Deprecated: Function get_magic_quotes_gpc() is deprecated
get_magic_quotes_runtime(); // Deprecated: Function get_magic_quotes_runtime() is deprecated
```

Magic quotesはPHP5.4で滅びました。


## array_key_exists

[array_key_exists](https://www.php.net/manual/ja/function.array-key-exists.php)関数がオブジェクトを受け付けなくなりました。

```php
array_key_exists(1, new stdClass()); // Deprecated: array_key_exists(): Using array_key_exists() on objects is deprecated
array_key_exists(1, []); // OK
```

array_key_existsの引数は`array`ですが、下位互換性のために`object`も受け入れていました。
PHP7.4でE_DEPRECATEDになり、PHP8ではエラーになります。


## Deprecate FILTER_SANITIZE_MAGIC_QUOTES

定数[FILTER_SANITIZE_MAGIC_QUOTES](https://www.php.net/manual/ja/filter.filters.sanitize.php)はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。

```php
filter_var('s', \FILTER_SANITIZE_MAGIC_QUOTES); // Deprecated: filter_var(): FILTER_SANITIZE_MAGIC_QUOTES is deprecated
filter_var('s', \FILTER_SANITIZE_ADD_SLASHES); // OK
```

FILTER_SANITIZE_MAGIC_QUOTESとFILTER_SANITIZE_ADD_SLASHESは全く同じですが、magic_quotesという不穏当な表現を消去するために削除されます。


## Deprecate ReflectionFunction::export

[Reflector::export](https://www.php.net/manual/ja/reflector.export.php)はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。

```php
ReflectionFunction::export('_'); // Deprecated: Function ReflectionFunction::export() is deprecated
echo new ReflectionFunction('_'); // OK
```

`Reflector::export`は引数がvoidなのに、継承した各クラスは引数を受け取ります。
これはおかしいのでどうにかしなければならなかったのですが、単純に`export`が削除されることになりました。


## mb_strrpos

[mb_strrpos](https://www.php.net/manual/ja/function.mb-strrpos.php)関数の第三引数に文字エンコーディングを渡せなくなります。

```php
mb_strrpos('haystack', 'needle', 'UTF-8'); // Deprecated: mb_strrpos(): Passing the encoding as third parameter is deprecated
mb_strrpos('haystack', 'needle', 0, 'UTF-8'); // OK
```

mb_strrposは、歴史的経緯により第三引数でも文字エンコーディングを受け取ることが可能でした。
この書き方はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。


## implode

[implode](https://www.php.net/manual/ja/function.implode.php)関数の引数を逆にできなくなります。

```php
implode([1, 2], ','); // Deprecated: implode(): Passing glue string after array is deprecated
implode(',', [1, 2]); // OK
implode([1, 2]); // OK ','区切りと同じ
```

implodeは、歴史的経緯により第一引数と第二引数を逆に渡すことが可能でした。
この書き方はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。
引数がひとつでの場合は今後も受け付けますが、これも使用しない方が無難でしょう。


## クロージャ

クロージャから`$this`を削除できなくなります。

```php

(new class{
    public function dummy(){
        return function () {isset($this);};
    }
})->dummy()->bindTo(null); // Deprecated: Unbinding $this of closure is deprecated
```

クラスメソッドでクロージャを定義すると勝手に`$this`が入ってきます。
PHP7.3までは、これを無理矢理削除することができました。
PHP7.4でE_DEPRECATEDになり、PHP8でエラーになります。


## Deprecate hebrevc

[hebrevc](https://www.php.net/manual/ja/function.hebrevc.php)関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。

```php
hebrevc('a'); // Deprecated: Function hebrevc() is deprecated
```

テキストに直接HTMLタグを書き込むという不適切な仕様が入っているためです。
かわりに` nl2br(hebrev($str))`を使いましょう。


## Deprecate convert_cyr_string

[convert_cyr_string](https://www.php.net/manual/ja/function.convert-cyr-string.php)関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。

```php
convert_cyr_string('str', 'k', 'i'); // Deprecated: Function convert_cyr_string() is deprecated
```

非常に古い関数であるため、文字コードの指定方法が他の関数と一貫していません。
今後はiconvやmb_convert_encodingを使いましょう。


## Deprecate money_format

[money_format](https://www.php.net/manual/ja/function.money-format.php)関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。

```php
money_format('%i', 123456789); // Fatal error: Uncaught Error: Call to undefined function money_format()
```

> money_format() は Windows では 定義されていません。

おふぅ。

このようにmoney_formatは環境によって使用できなかったり、ロケールによって結果が変わったりします。
今後は[NumberFormatter::formatCurrency](https://www.php.net/manual/ja/numberformatter.formatcurrency.php)等を使いましょう。


## Deprecate ezmlm_hash

[ezmlm_hash](https://www.php.net/manual/ja/function.ezmlm-hash.php)関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。

```php
ezmlm_hash('test@example.com'); // Deprecated: Function ezmlm_hash() is deprecated
```

そもそも[この関数](https://ja.wikipedia.org/wiki/Ezmlm)を使っている人はいるのだろうか。


## Deprecate restore_include_path

[restore_include_path](https://www.php.net/manual/ja/function.restore-include-path.php)関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。

```php
restore_include_path(); // Deprecated: Function restore_include_path() is deprecated
```

set_xxx関数をを多重に積んだ場合、[restore_error_handler](https://www.php.net/manual/ja/function.restore-error-handler.php)や[restore_exception_handler](https://www.php.net/manual/ja/function.restore-exception-handler.php)等は変更をひとつ前の状態に戻すのに対し、restore_include_pathだけはいきなり初期値に戻します。
動作が異なっており混乱の元であるため削除されます。
今後は`ini_restore('include_path')`を使いましょう。


## Deprecate allow_url_include

[allow_url_include](https://www.php.net/manual/ja/filesystem.configuration.php#ini.allow-url-include)ディレクティブはPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。

```ini
allow_url_include = On
```

Apacheを起動すると、エラーログに`PHP Deprecated:  Directive 'allow_url_include' is deprecated in Unknown on line 0`が入ります。
allow_url_includeは`require_once('http://example.com/')`と書けるようになるという、ヤバみしかない機能なので絶対にオンにしてはいけません。



# 感想

変更多過ぎぃ！

いやあPHP7.4やばいですね。
重量級だけでもプロパティ型指定・アロー関数・プリローディング・FFIなどがあって、これだけでも文法や書き方が大きく変化する勢いです。
さらに上記リストには、単なるバグ修正は含まれていません。
そこまで含めたら、とんでもない量の変更になってしまいますね。

PHPは、特にPHP7以降はモダンな書き方をごりごり取り入れていて、きちんと使えば相当に堅牢で厳格な記述を行うことが可能です。
ただ同時に古い書き方も相変わらず可能で、しかも古い書式のほうが圧倒的に文献が多いせいでなかなか新しい文法が広まらないという現状は残念なところです。
新機能を使おうにも未だにPHP5系しか使えないようなサーバも存在したりしますし、そもそもCentOS7のデフォルトPHPが未だに5.4という地雷ですからね[^1]。
もっと知識や環境を新陳代謝して使える仕組みが欲しいところです。
まあ、ほんのちょっとでも古い書き方をしようものなら全方位から銃弾が飛んでくるJavaScriptのような世界がいいかって言われたらそれも嫌ですけどね。

さすがにいきなり仕事でプロパティ型指定を書き始めたりはしませんが、一年後には大抵の場所で使えるようになっている、くらいに程々に普及するくらいには進んでほしいところです。


[^1]: 2019年9月リリースのCentOS8でようやくPHP7.2になった。