2018年12月6日に、PHPの最新版であるPHP 7.3がリリースされました!新しくて便利な機能が登場し、関数の一分が推奨されなくなり、バグ修正が数多く行われ、パフォーマンスが向上します。PHP 7.3はKinstaのすべてのお客様を対象にMyKinstaダッシュボードで利用可能になっております。🤘
最新情報)現在、PHP 8.1(正式リリース)がすべてのプランでサポートされており、PHP 7.3のサポートは終了しています。KinstaがサポートするPHPバージョンは、8.1, 8.2 , 8.3です。
本記事では、僕らが個人的に最も重要だと判断した機能と変更点の概要をご説明します。 機能とバグ修正の完全なリストについては、PHP 7.3のアップグレードノートとPHP 7.3のコメント募集をご確認ください。
PHP 7.3の重要な変更点について
本記事ではPHP 7.3の次の変更点について述べます:
- フレキシブルなHeredoc構文とNowdoc構文
- 関数呼び出しで末尾カンマが使えるようになる
- JSON_THROW_ON_ERROR
- list() でリファレンス渡しが使えるようになる
- is_countable関数
- array_key_first()、array_key_last()
- Argon2パスワードハッシュの強化
- 推奨されなくなるもの
フレキシブルなHeredoc構文とNowdoc構文
PHP 7.3の最も重要な改善点の1つであるてめ、非常に興味深いです。したがって、PHP 7.3のheredoc/nowdocの変更点について述べる前に、これらの便利なコア機能の概要を簡単にご説明します。nowdocとheredocにすでにご自信のある方は、PHP 7.3の変更点にスキップしてください。
heredocとnowdoc構文の概要
heredoc構文は、大量のテキストを二重引用符の心配のない埋め込み方法です。heredocは<<<
の後にIDがあり、終わりに同じIDとセミコロンがあります。次に例を示します。
print <<<EOT
Heredoc text behaves just like a double-quoted string, without the double quotes.
EOT;
nowdocはheredocと似たように機能しますが、次の異なる点があります:
- 識別子をシングルクォートで囲んでいるようにします(
<<<'EOT'
)。 - nowdoc内のパースはありません。
nowdocの例を次に示します:
print <<<'EOT'
Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
EOT;
heredocとnowdocの終端 IDの規則は同じです:
- 終端 ID は、その行の最初のカラムから始める必要があります。
- 使用するラベルは、PHP の他のラベルと同様の命名規則に従う必要があります。つまり、英数字およびアンダースコアのみを含み、 数字でない文字またはアンダースコアで始まる必要があります。
PHPマニュアルの警告ですが、
非常に重要なことですが、終端 ID がある行には、セミコロン (;) 以外の他の文字が含まれていてはならないことに注意しましょう。これは、特に ID はインデントしてはならないということ、セミコロンの前に空白やタブを付けてはいけないことを意味します。終端 ID の前の最初の文字は、使用するオペレーティングシステムで定義された 改行である必要があることにも注意を要します。これは、例えば、Macintoshでは \r となります。 最後の区切り文字の後にもまた、改行を入れる必要があります。
PHP 7.2 の無効な構文:
class foo {
public $bar = <<<EOT
bar
EOT;
}
// Identifier must not be indented
PHP 7.2 の有効な構文:
class foo {
public $bar = <<<EOT
bar
EOT;
}
簡単にいうとPHP 7.2だと:
- 終端 IDはインデントのものにします。
- 終端 IDのある行にはスペースやタブなどの文字を含めることはできません。
- 終端 IDの前の最初の文字は改行にします。
- 終端 IDの後に改行します。
heredoc構文とnowdoc構文はかなり厳しいですが、PHP 7.3では以下の点が改善されるため、少し楽になります。
1. 終端 IDのインデント、行頭スペースの削除が可能になる
PHP 7.3では終端 IDのインデントができるようになり、次のコードも遠慮なく書けます:
class foo {
public $bar = <<<EOT
bar
EOT;
}
終端 IDのインデントは、各行から削除されるスペース(またはタブ)の数を設定します。終端IDのインデントは、本体のその他の行のインデントよりも長いものにしてはいけませんので、ご注意ください。
以下のコードをご参照ください:
class foo {
public $bar = <<<EOT
bar
EOT;
}
上記のコードでは、次のエラーが発生します:
Parse error: Invalid body indentation level (expecting an indentation at least ...) in %s on line %d
タブとスペースを削除することにより、heredoc/nowdocのインデントをコードのその他の行と同様なレベルのインデントにし、本体の各行の無駄なスペースがなくなります。
インデントにはタブもスペースも使用できますが、混ぜて使用することはできません。つまり、終端IDと本体の各行では同じ識別子を使用する必要があります。インデントにタブとスペースを混ぜるとパースエラーが発生します。
2. 終端IDの改行要件が削除さる
現在、終端IDの次に改行し、heredoc/nowdocを終了します。PHP 7.3ではこれが改善され、heredoc/nowdocを改行せずに終了できるようになります。以下はRFCの例です:
PHP 7.2の無効な構文:
$values = [<<<END
a
b
c
END
, 'd e f'];
PHP 7.3の有効な構文:
$values = [<<<END
a
b
c
END, 'd e f'];
とにかく、ID名を十分に注意を払って決めましょう。heredoc/nowdocの本文で使用した言葉と一致するID名を設定すると、エラーが発生することがあります。(詳細については、RFCとGitHubをご参照ください。)
両方の提案は3分の2以上の票を得て通過しました。
PHP 7.3 RFC
参照文献:
関数呼び出しで末尾カンマが使えるようになる
末尾のカンマは、要素、パラメータ、またはプロパティのリストに追加されるカンマのことです。コンマがないためにエラーが発生しないように、新しい値が頻繁に追加されるときにカンマは非常に便利です。PHPでは末尾のカンマは配列で使用でき、PHP 7.2以降では名前空間のグループ指定における最後のカンマも使用可能です。
PHP 7.3以降、関数呼び出しにも使えるようになります。末尾カンマが非常に便利であるときのVariadic functionsの例を確認しましょう:
foo(
$bar,
$baz,
);
compact()
を使った配列を作成するときに、sprintf()
でフォーマットされた文字列を返すときにまたは、配列をマージするときに末尾カンマが便利です:
$newArray = array_merge(
$arrayOne,
$arrayTwo,
['foo', 'bar'],
);
末尾カンマはデバッグにも便利です:
var_dump(
$foo,
$bar,
$baz,
);
unset()
とisset()
と一緒に使用すればパワーフルです:
unset(
$foo,
$bar,
$baz,
);
isset(
$foo,
$bar,
$baz,
);
末尾のカンマは、メソッド呼び出しとエンクロージャでも使用できます。
注:今回の変更の対象は関数呼び出しのみで、関数宣言構文は変更されません。さらに、シングルカンマ、複数の末尾のカンマ、先頭のカンマは許可されません。
追加の例については、RFCをご参照ください。本RFCは30うちの10の票を得て通過しました。
PHP 7.3 RFC
JSON_THROW_ON_ERROR
PHP 7.3の新しい機能の中で最も楽しみにされている機能の1つが、JSONエラーの新しい処理方法です。コア機能ではなく、json_decode()とjson_encode()のエラー動作を変更するJSON拡張機能のアップデートです。
現在、json_decode()
は失敗時にnull
を返しますが、null
も有効な結果になります。処理に失敗したかどうかがわかりにくいです。
処理に失敗したかを知りたい度に
json_last_error()
またはjson_last_error_msg()
で調べなければならないという状態です。これは、機械可読形式と人間が判読可能な形式でグローバルエラー状態を返します。 – PHP RFC
json_encode()
は失敗時に FALSE
を返します。明らかにエラーの値であるため、分かりやすいです。しかし、どちらでも、エラーが発生したときにプログラムを終了することまたは警告を出すことはありません。
なお、PHP 7.3向けの提案は以下通りです:
このRFCでは、代わりに
json_decode()
とjson_encode()
にJSON_THROW_ON_ERROR
というに新しいオプションフラグ値を追加することを提案しています。このフラグが発生すると、これらの関数のエラー動作が変更されます。グローバルエラー状態は変更されませんが、エラーが発生した場合は、json_last_error()
とjson_last_error_msg()
の設定メッセージとコードと供にJsonException
が出ます。
JSONエラーの簡単な例を示します:
try {
json_decode("{", false, 512, JSON_THROW_ON_ERROR);
}
catch (\JsonException $exception) {
echo $exception->getMessage(); // echoes "Syntax error"
}
エラーが出されることにはメリットが多いです。詳細については、RFCをご参照ください。
注:json_decode()
の無効な深度パラメータは警告を消し、NULL
を返します。この機能はJSON_THROW_ON_ERROR
の影響を受けません。同様に、パラメータ解析エラーはJSON_THROW_ON_ERROR
の影響を受けず、引き続き警告を生成します。
この提案は23対0票を得て通過しました。
PHP 7.3 RFC
参照文献:
list() でリファレンス渡しが使えるようになる
リファレンス渡しとは?
例えば下記の行があります:
$b = &$a;
ここで$b
は$a
の値になりますが、その値は$a
から$b
にコピーされません。PHPでは、リファレンスを使用して値を割り当てることができます。つまり、2つの変数が同じデータを指し、変数が変わると元のデータも影響を受けます。以下はPHPマニュアルの例です:
<?php
$a = 3;
$b = &$a; // $b is a reference to $a
print "$a\n"; // prints 3
print "$b\n"; // prints 3
$a
の値を変更しましょう:
$a = 4; // change $a
print "$a\n"; // prints 4
print "$b\n"; // prints 4 as well, since $b is a reference to $a, which has been changed
list()とは?PHP 7.3での変更点は?
list() 言語構造は、一連の変数に値を代入するために使われますが、list()
を使って変数にリファレンスの値を代入することはできません。
PHP 7.3ではこれが改善され、次の例に示すように、list()
構造を使用してでもリファレンスの変数を割り当てることができるようになります。
$array = [1, 2];
list($a, &$b) = $array;
下記と同じです:
$array = [1, 2];
$a = $array[0];
$b =& $array[1];
この提案のメリットは、現在は許可されていない複数の変数をリファレンスで割り当てることができることです。詳細については、RFCをご参照ください。この提案は、17対7票を得て通過しました。
PHP 7.3 RFC
参照文献
- PHPマニュアル – list()
- PHPマニュアル – リファレンスについて
- 代入演算子 – 参照による代入
is_countable関数
PHP 7.3のもう一つの便利な機能は、is_countable()
関数です。PHP 7.2まで、カウントできないものをcount() しようとするとエラーが発生します。このため、警告を回避するには、次のコードを追加します:
if (is_array($foo) || $foo instanceof Countable) {
// $foo is countable
}
今回のRFCは、変数が配列であるまたはカウントできる場合はtrue
を返し、そうでなければfalse
を返すis_countable()関数を提案しています。したがって、上記のコードを次のように変更できます:
if (is_countable($foo)) {
// $foo is countable
}
この提案は、25対0票を得て通過しました。
PHP 7.3 RFC
参照文献
array_key_first()、array_key_last()
現在、reset()、end() 及びkey()関数を使用して配列の最初と最後のキーを取り出せます。残念ながら、これらの関数を使用すると、内部状態を変更せずに配列の最初または最後のインデックスを収集する方法はありません。他のオプションは、コードの可読性とパフォーマンスを低下させます。
今回の提案は、PHPコアに2つの新しい関数を追加することにより、上記を改善します:
array_key_first()
array_key_last()
PHP 7.3以降は、array_key_first()
とarray_key_last()
で、配列の内部ポインタに影響を与えずに、該当の配列の最初と最後のキーを取り出せるようになります。これらの新しい関数を使用することにより、あまり複雑でないコードを書くことができ、場合によってはエラーを避けることさえできます。詳細については、RFCをご参照ください。
array_key_first()
と array_key_last()
は18対14票を得て承認されました。
注:元のRFCでは、array_value_first()
と array_value_last()
という2つの追加の関数も提案されましたが、別当の投票で投票され承認されておらず、PHPコアに含まれません。
PHP 7.3 RFC
参照文献:
Argon2パスワードハッシュの強化
Argon2はBcryptアルゴリズムの代替品としてPHP 7.2で導入されたハッシングアルゴリズムです。PHP 7.2では、password_*
関数で使用できるPASSWORD_ARGON2I
定数が導入されました:
password_hash('password', PASSWORD_ARGON2I);
最初の導入以来、Argon2の新しいバージョンが追加されている為、本稿執筆の時点では、Argon2には3つのバージョンがあります:
- Argon2dはGPUクラッキング攻撃に対する抵抗力を最大限に引き出します。高速であり、データ依存のメモリアクセスを使用します。
- Argon2iは、パスワードハッシュに適切な方法であるデータに依存しないメモリアクセスを使用します。トレードオフ攻撃を予防するにはメモリ上のパスが多く、上記より遅いです。
- Argon2idは、メモリ上の最初のパスのためにArgon2iのアプローチ、その後のパスのためにArgon2dのアプローチを使用する、組み合わせのハイブリッドバージョンです。
特定の理由があり、それ以外のバージョンが必要である場合を除き、インターネット上ではArgon2idが推奨されています。
新しいRFCでは、新しいPASSWORD_ARGON2ID
定数を使用してpassword_* 関数内でArgon2idを使用することが提案されています。
password_hash('password', PASSWORD_ARGON2ID);
実装はArgon2iの実装と同じで、同じコスト要因を受け入れます:
- ハッシュ中に消費されるKiBの数を定義するメモリコスト(デフォルト値は1<<10、または1024 KiB、または1 MiBです)
- ハッシュアルゴリズムの反復回数を指定する時間コスト(デフォルトは2回)
- ハッシュ処理中に使用される並列スレッドの数を指定する並列度(デフォルトは2件)
次のコードをご参照ください。
$options = ['memory_cost' => 1<<11, 'time_cost' => 4, 'threads' => 2];
password_hash('password', PASSWORD_ARGON2ID, $options);
詳細については、RFCをご参照ください。
PHP 7.3 RFC
参照文献:
- Argon2 (Wikipedia)
- Argon2: パスワードハッシングなどに便利なメモリハード機能 (PDF)
推奨されなくなるもの
以下はPHP 7.3で推奨されなくなり、PHP 8.0で削除される予定との機能・関数のリストです。
Deprecate and Remove image2wbmp()
image2wbmp()関数は、該当画像のWBMPバージョンを出力または保存するものです。この関数には、イメージリソース、ファイル名(保存されたファイルへのパス)、およびフォアグラウンドカラーの3つパラメータがあります。PHP 5.0以降、 imagewbmp()と同じ機能を果たします。したがって、今回のRFCでは非推奨かつ削除が提案されています。
PHP 7.3以降、image2wbmp()
を呼び出すと、非推奨警告が発行されます。削除後になると、呼び出しで致命的なエラーが発生します。
PHP 7.3 RFC
ケース・インセンシティブ定数が推奨されなくなり、削除される
PHPでは現在、ケース・センシティブ(大文字と小文字を区別した)定数もケース・インセンシティブ(大文字と小文字を区別していない)定数もサポートされています。一方、ケース・インセンシティブ定数はサポートされていても、機能異常の原因になる場合があり、使用しにくいものです。
今回の提案は、以下の前提で提案されます:
- クラス定数は、必ずケース・センシティブでなければならない
const
で宣言されたグローバル定数は、必ずケース・センシティブでなければならないdefine()
で定義された定数は、原則ケース・センシティブでなければならない
または、「PHP言語リファレンス」では下記が明確にされています:
デフォルトで定数では大文字小文字を区別します。慣習的に、 定数は常に大文字で表記されます。
そこで、今回のRFCは次の変更点を提案しています:
- 3番目のパラメータを
true
に設定してdefine()
を呼び出すことが推進されなくなる – PHP 7.3 - 宣言とは異なるケーシングを使用するケース・インセンシティブ定数が推進されなくなる(
true
、false
またはnull
を除き) – PHP 7.3 - ケース・インセンシティブ定数の宣言を削除する – PHP 8.0
- 特別なケースの定数である
true
、false
またはnull
を専用キーワードに変換する – PHP 8.0
PHP 7.3 RFC
ケース・インセンシティブ定数が推奨されなくなり、削除される.
その他のPHP 7.3で推進されなくなるもの
以下は、PHP 7.3で推奨されなくなる機能の簡単な一覧です。なお、個人的には重要だと判断したものしか述べませんので、ご注意ください。完全なリストについては、PHP 7.3で推奨されなくなる機能をご参照ください。
記録されていないマルチバイト文字列(mbstring)関数のエイリアス:mb_
という接頭辞を使用している同等の関数の重複である、記録されていないマルチバイト文字列関数のエイリアスがいくつかあります。たとえば、mbereg
はmb_ereg
のエイリアスです。
これらの関数はすべて推進されなくなり、コンパイル中に検出されると非推奨通知が発生します。
整数needle付き文字列の検索関数:これらの関数は、文字列のneedleで機能します。needleが文字列でない場合は、 それを整数に変換し、その番号に対応する文字として扱います。(詳細については、PHPマニュアルをご参照ください。) 以下はRFCの例です:
$str = "There are 10 apples";
var_dump(strpos($str, "10")); // int(10)
var_dump(strpos($str, 10)); // bool(false)
上記は難しくて、ユーザーデータにより型が変更される可能性があるため予期しない異常が発生することがあります。したがって今回のRFCは、次の関数に関してneedleが文字列でない場合、非推奨警告を提案しています:
strpos
strrpos
stripos
strripos
strstr
strchr
strrchr
stristr
PHP 8.0では、非推進警告を削除し、needleを自動的に文字列に変換する必要があります。
fgetss()
関数とstring.strip_tags
ストリームフィルタ:fgetss()
とstring.strip_tags
は、読み込み時に文字列からタグを取り除きます。関数もフィルタもの両方でstrip_tags()関数を公開します。マシンがストリーミング状態でないといけない為、strip_tags()
の実装が難しくなります。さらに、RFCではもう一つのデメリットが明確にされています:
一方、これらの機能はあまり役に立ちません。
strip_tags()
自体には機能の限界とバグがあり、あまり便利なものではありません。その上にストリーミングアプリケーションのネイティブサポートを提供する必要はありません。
したがって今回のRFCでは、fgetss()
、gzgetss()
とSplFileObject::fgetss()
が推進されなくなります。
PHP 7.3のWordPressユーザーへの影響は?
WordPressの正式分析ページによると、本稿執筆の時点では、WordPressユーザーの32.9%しかPHP 7にアップグレードしていません。わずか4%がPHP 7.2を使用しています。ご覧のとおり、ユーザーの38%以上の大多数はまだPHP 5.6を使用しています。さらに面白いことに、ユーザーの28.5%以上がサポートのないPHPバージョンを使用しています。2016年12月に、WordPress.orgは推進されるバージョンをPHP 5.6~PHP 7以上にすることを広告しました。
PHP 7のパフォーマンス
PHP 7の方がはるかに高速であることが証明されているため、上記のデータはパフォーマンスの観点からも分かりにくいです。 次に分析データを示します。
- 公式のPHPベンチマークでは、PHP 7をPHP 5.6と比較するとレイテンシがほぼ半分で、1秒あたり2倍のリクエストが実行されることが分かります。
- Christian VighもPHPのパフォーマンス比較を公開し、PHP 5.2がPHP 7よりも400%遅いことが判明しました。
当社も、独自のPHPパフォーマンスベンチマークを実行しました。上記のベンチマークと同様に、WooCommerce 5.0とPHP 7.3の組み合わせではPHP 5.6と比較して1秒あたりのトランザクション(リクエスト)が3倍多く実行されることがわかりました。
- 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 7.3と組み合わせたWordPress 4.9.8がWordPress 5.0よりわずかに速いことも興味深いです。
- WordPress 4.9.8 PHP 5.6のベンチマーク結果:97.59リクエスト/秒
- WordPress 4.9.8 PHP 7.0のベンチマーク結果:221.42リクエスト/秒
- WordPress 4.9.8 PHP 7.1のベンチマーク結果: 233.78リクエスト/秒
- WordPress 4.9.8 PHP 7.2のベンチマーク結果:250.36リクエスト/秒
- WordPress 4.9.8 PHP 7.3のベンチマーク結果:276.31リクエスト/秒🏆
第三者プラグインまたはテーマをすべて試験して正常に機能しているかを確認する理由で更新していない方も多いかもしれませんが、単純にまだできていないという方も多いでしょう。
ご利用のPHPバージョンを確認する
ご利用のPHPのバージョンが不明な方は、PingdomまたはGoogle ChromeのDevtoolsのようなツールを使って、簡単に確認できます。うことです。 HTTPリクエストの最初のヘッダーにはバージョンが表示されるはずです。
上記は、ホスティング会社がX-Powered-By
ヘッダーの値を変更しない限りできます。変更している場合、PHPバージョンが表示されないことがあります。その場合は、WordPress管理ダッシュボードのフッターに基本的なサーバー情報を表示する、Version Infoなどの無料のプラグインをインストールすることもできます。
または、FTP経由でファイルをアップロードすることも、ホスティング会社に依頼することできます。
PHP 7.3に更新する
PHP 7.3はリリースされている為、すぐに試験し始めた方がよいでしょう。WordPressウェブサイトをローカルで試験することも、コマンドラインからさまざまなバージョンのPHPを試験できるDockerのような環境でスクリプトを確認することもできます。
また、本番環境に一番近い為、ステージング環境をご利用することもできます。MyKinstaダッシュボードで数回クリックだけでステージング環境を作成できます。
本番サイトで使用する前に十分にテストしてください。ステージングサイト用のPHPバージョンを「ツール」の「PHPエンジン」でワンクリックで選択でき、サードパーティ製プラグインとテーマとの互換性を保証するための試験が開始できます。
PHPの最新版が正常に機能しているかを確認した上で、本番サイトをPHP 7.3に変更するか、変更も加えた場合にはステージングサイトを本番サイトに公開します。
まとめ
PHPの今まで最高のバージョンである最新版がリリースされました。フレキシブルなheredocとnowdoc、関数呼び出しで末尾カンマ、またはlist()
でリファレンス渡しが使えるようにります。本記事では、個人的に重要だと判断した改善点の概要を紹介しましたが、皆様の好きなものは何ですか。今回改善される機能をどのように活用する予定ですか。ご意見をお寄せください。PHPは未だ現役です。
PHP 7.3に関する提案の完全なリストについては、PHP 7.3のコメント募集ページとGitHubのPHP 7.3のアップグレードノートをご参照ください。
コメントを残す