2019/11/28にPHP7.4.0がリリースされました。
ということで、ここではドキュメント化されている新機能や変更点を片端から試してみます。
これら以外にもドキュメント化するほどでもない軽微な変更が多々入っているはずですし、単なるバグ修正も山ほどあるのですが、今回はそのあたりには触れません。
把握しきれていませんしね。
インストール
古いXAMPPが入っていたらディレクトリまるごと削除。
最新のXAMPPをインストール。
Windows版PHPから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?
あなたなら環境構築くらい自力でできるっしょ。
新機能とか
プロパティ型指定
プロパティに型が指定できるようになりました。
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最大の特徴といっていいでしょう。
アロー関数
アロー関数が使えるようになりました。
$square = fn($x) => $x ** 2;
var_dump($square(2), $square(-5)); // 4, 25
ちょっとした使い捨て関数を書くときなどに便利。
$a = 1;
$hoge = fn() => ++$a;
echo $hoge(); // 2
echo $a; // 1
PHPではアロー関数の外にある変数は汚染されません。
また、アロー関数の実装に伴いfn
が予約語になります。
今後function fn(){}
といった文は書けなくなります。
FFI
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のエラーなのかわからなくなる。
プリローディング
php.iniを設定。
opcache.preload="path\to\preload.php"
opcache.preload
には"プリロードする対象ファイル"ではなく、"プリロードする対象ファイルを読み込むファイル"を指定します。
// preload_cache.phpをプリロードする
opcache_compile_file('path\to\preload_cache.php');
プリロードさせたい中身はopcache_compile_file
で呼ばれているファイルに書きます。
function h(string $str):string{
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
Apacheを再起動すると、h()
がサーバ上のどこからでも使えるようになります。
var_dump(h('a<b>c')); // a<b>c
function h(){} // Fatal error: Cannot redeclare h()
関数h()
がネイティブ関数……のようなものになりました。
NULL合体代入演算子
NULL合体代入演算子??=
が導入されました。
$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エクステンションの常時有効化
var_dump(hash_algos()); // ['md2', 'md4', 'md5', …]
PHP7.3までは--disable-hash
オプションで無効にできました。
PHP7.4以降は無効にすることができません。
Password Hashing Registry
エクステンションが独自のハッシュアルゴリズムを追加できるようになりました。
それに伴い、現在使用可能なハッシュアルゴリズムを確認するpassword_algos関数が追加されました。
var_dump(password_algos()); // ['2y', 'argon2i', 'argon2id']
今後追加アルゴリズムは出てくるでしょうか。
openssl_random_pseudo_bytesの改善
openssl_random_pseudo_bytes関数が異常時に例外を発生するようになりました。
openssl_random_pseudo_bytes(-1); // Fatal error: Uncaught Error: Length must be greater than 0
PHP7.3までは例外が出ずにfalseが返ってきていました。
mb_str_split
mb_str_split関数が追加されました。
$x = mb_str_split('aAaA11!!あ𩸽', 1);
var_export($x);// ['a', 'A', 'a', 'A', '1', '1', '!', '!', 'あ', '𩸽']
半角全角入り交じりでも正常に分割してくれます。
これは素晴らしいですね。
$x = mb_str_split('がが👨👩👦👦', 1);
var_export($x);// ['が', 'か', '゙', '👨', '', '👩', '', '👦', '', '👦' ]
ZWJシーケンスもきれいに分割してくれる模様。
mbstring.regex_retry_limit
mbstring.regex_retry_limitディレクティブが追加されました。
デフォルト値は1000000です。
$pattern = '(.*)*^';
$subject = '1234567890123456789012345678901234567890';
mb_ereg($pattern, $subject, $regs); // false
マルチバイト正規表現において、マッチ回数が一定値を超えたら検索を打ち切ります。
pcre.backtrack_limitと同じく、ReDoS攻撃を防ぐためのものです。
ところでPCRE正規表現のエラーはpreg_last_errorで取れるんだけど、mb_ereg系のエラーはどうやって調べればいいんだろう。
ReflectionReference
ReflectionReferenceクラスが追加されました。
$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
リファレンスかどうかを調べることができます。
が、なんか思っていたのとちがって、何故か配列にしか使うことができないみたいです。
数値セパレータ
数値リテラルを_
で区切れるようになりました。
<?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()が例外を出せる
マジックメソッド__toString()が中から例外を出せるようになりました。
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になっていました。
共変戻り値・反変パラメータ
子クラスでパラメータの型を広げ、返り値の型を狭めることができるようになりました。
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が出ていました。
引数アンパック
$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]
可変長引数や関数呼び出しという特殊な場所でだけ使えていた引数アンパックを、普通に配列中で使えるようになります。
余計なマージとかを行わず単純に配列をくっつけたいときに便利です。
数値キーは無視されて連番が振り直されます。
連想配列を渡すとCannot unpack array with string keys
のFatal errorになります。
弱い参照
WeakReferenceクラスが追加されました。
$dummy = new stdClass();
$wr = WeakReference::create($dummy);
$wr->get(); // $dummy
unset($dummy);
$wr->get(); // null
毎回インスタンス使い捨てのPHPでどういうときに使えばいいのか、正直わかりません。
WeakRefとの違いもわかりません。
マジックメソッド__serialize
/__unserialize
マジックメソッド__serialize
/__unserialize
が追加されました。
__sleep
およびSerializableにかわる、新たなシリアライズのメカニズムです。
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
これはバグレポに上がっていた例です。
WrapperObject::serialize
メソッドに何もしないserialize/unserialize
がありますが、これが入っていると何故かunserialize
に失敗します。
新たなシリアライズシステムではこのような問題が起こりません。
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関数にオプションmax_depth
が追加されました。
$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
PEARがデフォルトでインストールされなくなります。
require_once("Auth/Auth.php"); // failed to open stream: No such file or directory
手動でインストールすれば当然ながら今後も使用可能です。
またコンパイルオプション--with-pear
を指定することでもインストールできますが、このオプションは非推奨で、今後削除される可能性があります。
Curl
PHPというより、同梱されるCurlのバージョンに依るものです。
$cfile = new CURLFile('https://www.google.com/images/srpr/logo1w.png','image/png','testpic');
libcurlのバージョンが7.56.0
以降であれば、CURLFileにURLを指定できます。
また定数CURLPIPE_HTTP1がE_DEPRECATEDになりました。
libcurlでdeprecateになったためです。
libcur7.62.0
以降は使えなくなります。
FILTER_VALIDATE_FLOAT
検証フィルタFILTER_VALIDATE_FLOATがオプションmin_range
/max_range
に対応し、FILTER_VALIDATE_INTと同じ挙動になりました。
filter_var(10.1, FILTER_VALIDATE_FLOAT, [
'options'=>[
'min_range' => 1,
'max_range' => 10,
]
]); // false
むしろ何故今まで対応していなかったのだろう。
IMG_FILTER_SCATTER
GDに画像フィルタ定数IMG_FILTER_SCATTERが追加されました。
$img = imagecreatefrompng('image.png');
imagefilter($img, IMG_FILTER_SCATTER , 3, 5);
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
点描のようなかんじに画像をぼかします。
第三、第四引数でぼかし度合いを調整できます。
imagefilter($img, IMG_FILTER_SCATTER, 3, 10); // 左
imagefilter($img, IMG_FILTER_SCATTER, 1, 100); // 右
正規表現フラグPREG_OFFSET_CAPTURE / PREG_UNMATCHED_AS_NULL
preg_replace_callbackおよびpreg_replace_callback_arrayが、正規表現フラグPREG_OFFSET_CAPTUREとPREG_UNMATCHED_AS_NULLを受け取るようになりました。
$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で埋めて送られてきます。
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"]
よくわからないので詳細はコメントを参照してください。
PDO DSN
PDOのDSNにユーザ名userとパスワードpasswordを書けるようになりました。
$dsn = 'mysql:dbname=test;host=127.0.0.1;user=testuser;password=testpass';
$pdo = new PDO($dsn);
元々Postgresだけ対応していたのが、MySQLなどその他のデータベースにも書けるようになったとのことです。
これは地味に便利では。
DSNとコンストラクタが両方指定された場合はコンストラクタが優先されます。
PDO ?のエスケープ
SQLの構文中において、?
を??
でエスケープできるようになりました。
$sql = "SELECT * FROM my_table WHERE my_col ?? 'my_key'";
SQL構文中で?
と書くとプレースホルダと解釈されてしまいますが、それを回避することができます。
これまでPostgresの?演算子を書くことができなかったため、その対策です。
文字列値としては、これまでもこれからも普通に書けます。
strip_tags
strip_tags関数の第二引数を配列で渡せるようになりました。
$str = '<a><b><i><u>テキスト</u></i></b></a>';
strip_tags($str, ['b', 'u']); // <b><u>テキスト</u></b>
むしろ今までできなかったのかよ。
なお、配列で渡す場合はタグの括弧は不要です。
array_merge
array_merge関数とarray_merge_recursive関数の第一引数を省略できるようになりました。
array_merge(); // []
空の配列が返ります。
おそらくスプレッド構文に空の配列を渡してしまったとき用。
$arr = [];
array_merge(...$arr); // PHP7.3まではE_WARNING
proc_open
proc_open関数の第一引数を配列で渡せるようになりました。
proc_open(['php', '-r', 'echo "Hello World\n";'], $descriptors, $pipes);
何がうれしいのかってOSコマンドインジェクションを考えなくて済むようになります。
また、第二引数がリダイレクタとnullに対応しました。
proc_open($cmd, [2 => ['redirect', 1]], $pipes); // 2>&1
proc_open($cmd, [2 => ['null']], $pipes); // 2>null
わりとexecってやっちゃうタイプなので、個人的にはあまり使わない関数です。
pcntl_unshare
pcntl_unshare関数が追加されました。
他プロセスと共有しているコンテキストを分離するとかなんとからしいのだけど、Windowsでは動かないのでよくわかりません。
SplPriorityQueue
SplPriorityQueue::setExtractFlagsに0を渡すと即座に例外を出すようになりました。
$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が追加されました。
echo MB_ONIGURUMA_VERSION; // 6.9.3
正規表現エンジン鬼車のバージョンがわかるようになります。
鬼車
鬼車がPHP本体にバンドルされなくなりました。
かわりにlibonigを導入しなければならないそうです。
手元のXAMPPにはそんなファイルがなかったのですが、mb正規表現は普通に動いていました。
どうして動いているのかはわかりません。
get_declared_classes
get_declared_classes関数が、まだインスタンス化されていない無名クラスを返さなくなりました。
$class1 = new class {};
var_dump(get_declared_classes()); // $class2は入ってない
$class2 = new class {};
PHP7.3までは一覧に$class1
も$class2
も出てきていました。
PHP7.4以降は$class1
しか出てきません。
imagecreatefromtga
imagecreatefromtga関数が追加されました。
$img = imagecreatefromtga('image.tga');
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
誰得にも程があるのではないか。
ファイル末尾の<?php
最後に改行がない
<?php
ファイル末尾に改行を入れずに<?php
とだけ書くと、これまでは<?php
という文字列と解釈されていました。
すなわち<?php
という文字列がHTMLに出力されていました。
PHP7.4以降はPHP開始タグと解釈されるようになります。
その後が何もないので、実質的には何もしません。
STREAM_OPTION_READ_BUFFER
includeやrequireをストリームで使用している場合、streamWrapper::stream_set_optionがSTREAM_OPTION_READ_BUFFER
オプション付きで呼ばれるようになりました。
カスタムストリームラッパーを自作している場合、それに対する実装が必要です。
定数PASSWORD_XXXの値変更
定数PASSWORD_XXXの定数値が変更になりました。
echo PASSWORD_ARGON2ID; // argon2id
例としてPASSWORD_DEFAULTは1からnullに、PASSWORD_ARGON2IDは3から"argon2id"になります。
正しい実装をしているかぎりは影響ありません。
fread / fwrite
fread / fwriteが失敗時にfalseを返すようになりました。
$fp = fopen('/path/to/dummy', 'a');
fread($fp, 100); // false
PHP7.3までは0や""が返ってきていました。
DateIntervalの曖昧な比較
DateIntervalの曖昧な比較ができなくなりました。
new DateInterval('P1D') == new DateInterval('P1D'); // Warning: Cannot compare DateInterval objects
E_WARININGが発生し、たとえ同じ値であろうとも常にfalseが返ってきます。
PHP7.3までは異なる値であろうが常にtrueとなっていました。
厳密な比較は常にfalseで、警告も発生しません。
$dti = new DateInterval('P1D');
$dti2 = $dti;
var_dump($dti == $dti2, $dti === $dti2); // true, true
同じオブジェクトであれば当然trueであり、E_WARININGも発生しません。
リフレクションのシリアライズ
リフレクションをserializeするとFatal errorが発生するようになりました。
$ref = new ReflectionClass(new stdClass());
serialize($ref); // Serialization of 'ReflectionClass' is not allowed
リフレクションのシリアライズはこれまでもサポートされておらず、壊れることがありました。
PHP7.4では明確に禁止されます。
get_object_vars
get_object_varsにArrayObjectを突っ込んでも、値が取れなくなりました。
$arr = new ArrayObject([1, 2, 3]);
get_object_vars($arr); // []
PHP7.3までは[1, 2, 3]
が返ってきました。
これによってReflectionObject::getProperties、Iterator等が影響を受けます。
(array)$arr
キャストは影響を受けず、値を取得することができます。
get_mangled_object_vars
get_mangled_object_vars関数が追加されました。
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がCountableをimplementsしました。
$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
実はずっと昔からCountable
でもないのにcountできていたので、単に現状を実態に合わせたというものです。
sapi_windows_set_ctrl_handler
sapi_windows_set_ctrl_handler関数が追加されました。
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
openssl_x509_verify関数が追加されました。
$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証明書のチェックができます。
こんなことわざわざPHPでするなよって気もしますが。
idn_to_utf8
idn_to_utf8およびidn_to_ascii関数の引数variantのデフォルト値がINTL_IDNA_VARIANT_UTS46になりました。
echo idn_to_utf8('xn--2-kq6aw43af1e4y9boczagup'); // 中島第2駐輪場
そういえば日本語ドメイン最近見ませんね。
token_get_all
token_get_all関数が、不正な文字列にトークンシンボルT_BAD_CHARACTERを返すようになりました。
$tokens = token_get_all('<?php ' . chr(0));
echo token_name($tokens[1][0]); // T_BAD_CHARACTER
これまでは解釈できない文字は単に無視されていたのですが、そのような文字を含めて正確にパースできるようになりました。
正しくないソースを正しく解析しても仕方ない気がしますが。
pkg-config
設定をpkg-config
に寄せていくために、多くのコンパイルオプションがディレクトリ指定できなくなります。
例としてCurlは、コンパイルオプション--with-curl=path/to/dir
とすることでディレクトリを指定することができました。
PHP7.4以降は--with-curl
にはオプションを設定できず、ディレクトリ指定することができません。
また-with-png-dir
のような、直接的にディレクトリを指定するオプションは削除されます。
まあ、最近は自力でインストールとかあまりしませんけどね。
演算子+-.の優先順位変更
演算子+-
と.
を並べて使うとE_DEPRECATEDが起こるようになりました。
PHP8において演算子.
の優先順位を下げるための布石です。
$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"になります。
三項演算子のネスト制限
解釈が一意に定まらない三項演算子のネストはPHP7.4でE_DEPRECATEDに、PHP8でエラーになります。
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あたりで他言語と同じ解釈に変更されると思われます。
波括弧による文字列アクセス
$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リリース時点では、今後削除される予定はありません。
角括弧による文字列以外へのアクセス
$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関数に、UTF-8以外のマルチバイト文字列を渡すとE_NOTICEが発生するようになります。
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が発生するようになります。
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関数に数値形式でない文字列を渡すとE_WARNINGが発生するようになりました。
echo bcadd("2", "3a"); // Warning: bcadd(): bcmath function argument is not well-formed
PHP7.3までは何も出ませんでした。
値そのものは、昔から0として扱われていました。
上記例でいうと、出力はPHP5時代からずっと"2"のままです。
解釈が通常のPHP関数と異なるので少々わかりにくいですね。
base_convert
base_convert関数に無効な文字が渡された場合、E_DEPRECATEDが発生するようになりました。
base_convert('012', 2, 10); // Deprecated: Invalid characters passed for attempted conversion, these have been ignored
PHP7.3までは何のエラーも出ませんでした。
いずれにせよ、途中に出てくる無効な値は単に無視されます。
base_convertのほか、hexdec、octdec、bindecにも同じ変更が入ります。
単に全ての関数が同じ内部関数_php_math_basetozvalを使っているからという理由ですが。
socket_addrinfo_lookup
socket_addrinfo_lookup関数において、引数AI_IDN_ALLOW_UNASSIGNEDとAI_IDN_USE_STD3_ASCII_RULESがE_DEPRECATEDになりました。
そもそもマニュアルが日本語化されてすらいないほど、この関数自体が使われていません。
Deprecate LDAP
ldap_control_paged_result_responseとldap_control_paged_resultがE_DERECATEDになりました。
かわりにldap_searchを使えということだそうです。
また、nsldap
とumich_ldap
のサポートが削除されました。
何のことだかさっぱりわかりません。
Deprecate is_real
is_real関数および(real)
キャストはPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。
is_real(1); // Deprecated: Function is_real() is deprecated
is_float(1); // OK
is_realではなくis_floatを使いましょう。
Deprecate Magic quotes
get_magic_quotes_gpcおよびget_magic_quotes_runtime関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。
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関数がオブジェクトを受け付けなくなりました。
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はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。
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はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。
ReflectionFunction::export('_'); // Deprecated: Function ReflectionFunction::export() is deprecated
echo new ReflectionFunction('_'); // OK
Reflector::export
は引数がvoidなのに、継承した各クラスは引数を受け取ります。
これはおかしいのでどうにかしなければならなかったのですが、単純にexport
が削除されることになりました。
mb_strrpos
mb_strrpos関数の第三引数に文字エンコーディングを渡せなくなります。
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関数の引数を逆にできなくなります。
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
を削除できなくなります。
(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関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。
hebrevc('a'); // Deprecated: Function hebrevc() is deprecated
テキストに直接HTMLタグを書き込むという不適切な仕様が入っているためです。
かわりに nl2br(hebrev($str))
を使いましょう。
Deprecate convert_cyr_string
convert_cyr_string関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。
convert_cyr_string('str', 'k', 'i'); // Deprecated: Function convert_cyr_string() is deprecated
非常に古い関数であるため、文字コードの指定方法が他の関数と一貫していません。
今後はiconvやmb_convert_encodingを使いましょう。
Deprecate money_format
money_format関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。
money_format('%i', 123456789); // Fatal error: Uncaught Error: Call to undefined function money_format()
money_format() は Windows では 定義されていません。
おふぅ。
このようにmoney_formatは環境によって使用できなかったり、ロケールによって結果が変わったりします。
今後はNumberFormatter::formatCurrency等を使いましょう。
Deprecate ezmlm_hash
ezmlm_hash関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。
ezmlm_hash('test@example.com'); // Deprecated: Function ezmlm_hash() is deprecated
そもそもこの関数を使っている人はいるのだろうか。
Deprecate restore_include_path
restore_include_path関数はPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。
restore_include_path(); // Deprecated: Function restore_include_path() is deprecated
set_xxx関数をを多重に積んだ場合、restore_error_handlerやrestore_exception_handler等は変更をひとつ前の状態に戻すのに対し、restore_include_pathだけはいきなり初期値に戻します。
動作が異なっており混乱の元であるため削除されます。
今後はini_restore('include_path')
を使いましょう。
Deprecate allow_url_include
allow_url_includeディレクティブはPHP7.4でE_DEPRECATEDになり、PHP8で削除されます。
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のような世界がいいかって言われたらそれも嫌ですけどね。
さすがにいきなり仕事でプロパティ型指定を書き始めたりはしませんが、一年後には大抵の場所で使えるようになっている、くらいに程々に普及するくらいには進んでほしいところです。
-
2019年9月リリースのCentOS8でようやくPHP7.2になった。 ↩