diff --git a/appendices/filters.xml b/appendices/filters.xml index 2cfdceb557..d724de7bfb 100644 --- a/appendices/filters.xml +++ b/appendices/filters.xml @@ -1,6 +1,6 @@ - + @@ -290,7 +290,7 @@ fclose($fp);
zlib.deflate と zlib.inflate - + zlib.deflate (圧縮) と zlib.inflate (展開) は、 RFC 1951 で述べられている圧縮方法を @@ -309,10 +309,123 @@ fclose($fp); 効率は落ちますがメモリの消費量を抑えられます。値を指定しなかった際の window の初期値は、現在 15 です。 + zlib.deflate フィルタは + DEFLATEZLIBGZIP + の 3 つの圧縮方法を実装しており、どれを使うかは + window パラメータの値で決まります。 + + window パラメータの下位 4 ビットは、内部で使われる「ヒストリバッファ」の + サイズを、2 を底とする対数で指定するもので、8 から 15 までの + 範囲を取ります。それ以外のビットの意味は、 + 以下のとおりです。 + + + + + DEFLATE (RFC 1951) + は、ヘッダもチェックサムも持たない生の圧縮アルゴリズムです。 + window パラメータに -9 から -15 の範囲の値を指定した場合に、この圧縮が行われます。 + この圧縮アルゴリズムは、zlib.deflate フィルタが生成する + すべてのフォーマットの基礎になっています。 + 文字列を直接扱う関数としては、gzdeflate と + gzinflate が対応します。 + + + + + + ZLIB (RFC 1950) + は、DEFLATE アルゴリズムを適用した上で、2 バイトのヘッダと、 + 圧縮前のデータの Adler32 チェックサムをビッグエンディアンのバイトオーダーで + 格納した 4 バイトのトレーラーを追加します: + + + 2 バイトのヘッダをビッグエンディアンの 16 ビット符号なし整数として読んだ値は、 + 31 の倍数でなければなりません。 + window パラメータに 8 から 15 の範囲の値を指定した場合に、 + このフォーマットが生成されます。 + 文字列を直接扱う関数としては、gzcompress と + gzuncompress が対応します。 + + + + + + GZIP (RFC 1952) + は、DEFLATE アルゴリズムを適用した上で、ヘッダと、 + 圧縮前のデータの CRC32 チェックサムおよびその長さを、 + どちらもリトルエンディアンのバイトオーダーで格納したトレーラーを追加します: + + + これが .gz ファイルのフォーマットです。 + GZIPHEADER には、順に + GZIP シグネチャ (\x1f\x8B)、 + 圧縮メソッド (\x08)、値がゼロのフラグバイト、 + 値がゼロの 4 バイトの更新時刻、圧縮レベルに応じた extra-flags バイト、 + そして現在のオペレーティングシステム + (\x00 = FAT ファイルシステム、 + \x03 = Unix など) が格納されます。 + window パラメータに 9+16=25 から 15+16=31 の範囲の値を指定した場合に、 + このフォーマットが生成されます。 + 圧縮前のデータの長さには 4GB という上限があることに注意してください。 + この上限を超えた場合、LENGTH の部分には実際の長さを + 2^32 で割った余りだけが格納されます。 + 文字列を直接扱う関数としては、gzencode と + gzdecode が対応します。また + gzopen 関数を使うと、.gz + ファイルの読み書きができます。 + + + + + zlib.inflate フィルタで指定できるのは + window パラメータだけで、それ以外のパラメータ + (memorylevel) は無視されます。 + $W をヒストリバッファのサイズの 2 を底とする対数とすると、展開時には + 2^$W バイトが確保されます。ZLIB フォーマットの場合、この値はヘッダに記録された + 値以上でなければならず、展開時にチェックされます。それ以外のフォーマットの場合は、 + データ中に実際に現れるマッチ距離をまかなえる大きさであれば十分です。 + 範囲は 9 ≤ $W ≤ 15 です。値がわからない場合は $W=15 にしておくのが安全です。 + + + + + DEFLATE (RFC 1951): + window=-$W を指定します。$W には、圧縮時に使った値以上を指定します。 + 値がわからない場合は window=-15 を指定してください。 + + + + + + ZLIB (RFC 1950): + window=$W を指定します。$W の値は ZLIB ヘッダから取得できます。 + 値がわからない場合は window=15 を指定してください。 + + + + + + GZIP (RFC 1952): + window=$W+16 を指定します。GZIP ヘッダは window サイズを保持していないので、 + 圧縮時に使った値がわからない場合は window=31 を指定してください。 + + + + + + ZLIB or GZIP: + ヘッダを自動で検出させるには window=$W+32 を指定します。こうすると + どちらのフォーマットも認識して展開できます。 + window=15+32=47 にしておくのが安全です。 + + + + memory は、作業用の一時メモリをどの程度割り当てるかを 指定します。1 (最小限) から 9 (最大限) の間で指定できます。この値は 圧縮の速度のみに影響し、圧縮後のデータのサイズには影響しません。 - + diff --git a/language/context/http.xml b/language/context/http.xml index f0c1a4914f..a42c91b42e 100644 --- a/language/context/http.xml +++ b/language/context/http.xml @@ -1,6 +1,6 @@ - + @@ -134,9 +134,15 @@ Location のリダイレクトをたどります。 0 で無効にできます。 - - デフォルトは 1 です。 - + + このオプションを設定しない場合、リダイレクトをたどるのは + 300301302、 + 303307308 + のレスポンスコードのときだけです。 + 1 を設定するのはこれと同等ではなく、 + Location ヘッダを含むレスポンスであれば、 + レスポンスコードが何であってもリダイレクトをたどるようになります。 + @@ -200,11 +206,52 @@ + + + uri_parser_class + string あるいは null + + + + URI のパースに使用するクラス。&null; の場合は、 + parse_url に基づく従来の挙動を使用します。 + 指定できる値は Uri\Rfc3986\Uri + (RFC 3986 に準拠したパーサー) と Uri\WhatWg\Url + (WHATWG URL Standard のパーサー) です。 + ユーザーランドの独自実装はサポートされていません。 + + + デフォルトは &null; です。 + + + + + &reftitle.changelog; + + + + + &Version; + &Description; + + + + + 8.5.0 + + uri_parser_class オプションが追加されました。 + + + + + + + &reftitle.examples; diff --git a/language/control-structures/for.xml b/language/control-structures/for.xml index 4888f7a28a..51d06128e3 100644 --- a/language/control-structures/for.xml +++ b/language/control-structures/for.xml @@ -1,6 +1,6 @@ - + for @@ -134,97 +134,58 @@ endfor; - 多くのユーザーにとって、次の例のように配列をループ処理することはよくあるでしょう。 + 式2式3 は、繰り返しのたびに評価されます。 + パフォーマンス上の問題を避けるため、ここには単純な式を使うことをお勧めします。 + たとえば、繰り返しの回数があらかじめわかっている場合は、 + 式2 で関数をコールするのではなく変数を使うほうがよいでしょう。 - + 'Kalle', 'salt' => 856412), - array('name' => 'Pierre', 'salt' => 215863) -); -for($i = 0; $i < count($people); ++$i) { - $people[$i]['salt'] = random_int(100000, 999999); +$people = ['Kalle', 'Pierre']; + +// 悪い例: 繰り返しのたびに関数をコールしている +for($i = 0; $i < getIterationCount($people); ++$i) { + $people[$i] .= ' is cool'; } -var_dump($people); -]]> - - &example.outputs.similar; - - - array(2) { - ["name"]=> - string(5) "Kalle" - ["salt"]=> - int(454478) - } - [1]=> - array(2) { - ["name"]=> - string(6) "Pierre" - ["salt"]=> - int(776978) - } + +// よい例: 繰り返しの回数を変数に格納している +for($i = 0, $peopleCount = getIterationCount($people); $i < $peopleCount; ++$i) { + $people[$i] .= ' is cool'; } + ]]> - + - このコードは実行速度が遅くなることでしょう。 - というのも、配列のサイズを毎回取得しているからです。 - サイズが変わることはありえないのだから、これは簡単に最適化することができます。 - 配列のサイズを変数に格納して使うようにすれば、 - 何度も count を呼ばずに済むのです。 + 上の例の最初のループでは、関数が常に同じ値を返すにもかかわらず、 + 繰り返しのたびにその関数をコールしています。 + 2番目のループのようにその値を変数に格納しておけば、関数のコールは一度で済みます。 + ただし、繰り返しの回数が固定されることには注意してください。 + ループの中で配列を変更した場合、格納した値は配列の新しいサイズを反映しなくなります。 - - - - 'Kalle', 'salt' => 856412), - array('name' => 'Pierre', 'salt' => 215863) -); -for($i = 0, $size = count($people); $i < $size; ++$i) { - $people[$i]['salt'] = random_int(100000, 999999); -} -var_dump($people); -]]> - - &example.outputs.similar; - - - array(2) { - ["name"]=> - string(5) "Kalle" - ["salt"]=> - int(454478) - } - [1]=> - array(2) { - ["name"]=> - string(6) "Pierre" - ["salt"]=> - int(776978) - } -} -]]> - - - + + + 配列のサイズは配列自身が保持しています。 + つまり、ビルトイン関数 count をコールしても要素を数え上げる必要はなく、 + パフォーマンス上の問題は発生しません。 + ただし、配列全体をたどる COUNT_RECURSIVE を指定した場合は当てはまりません。 + また、Countable を実装したオブジェクトに + count を使う場合は、そのコールのコストが高くなる可能性があるため注意が必要です。 + + + + + + 配列を反復処理する方法として、for ループは推奨されません。 + &foreach; ループはこの用途のために設計されており、たいていの場合はより便利です。 + + - + 演算子の優先順位 演算子の優先順位 @@ -261,6 +261,13 @@ yield from + + (n/a) + => + + 値とキーの yield + + (n/a) yield @@ -294,6 +301,26 @@ 論理演算子 + + (n/a) + + include + include_once + require + require_once + + + include, + include_once, + require&listendand; + require_once + + + + (n/a) + fn (...) => + アロー関数のアローの曖昧さの回避 + (n/a) throw diff --git a/language/predefined/serializable.xml b/language/predefined/serializable.xml index 933d40e790..c415b12966 100644 --- a/language/predefined/serializable.xml +++ b/language/predefined/serializable.xml @@ -1,6 +1,6 @@ - + Serializable インターフェイス @@ -35,6 +35,37 @@ を実装したクラスに対しては、推奨されない警告が発生します。 + + + 新しいコードでは、代わりに PHP 7.4.0 以降で利用可能な + __serialize() と + __unserialize() + マジックメソッドを使いましょう。 + クラスがこれらのマジックメソッドとこのインターフェイスの両方を宣言している場合、 + serialize は常に + __serialize() を使い、 + Serializable::serialize がコールされることはありません。 + 一方、アンシリアライズでどちらが使われるかは、データの形式によって決まります。 + PHP 7.4.0 以降が書き出したデータは + __unserialize() が読み込み、 + それより前のバージョンが書き出したデータは今でも + Serializable::unserialize が読み込みます。 + そのため、Serializable の実装は、 + このような古いデータを読み込む場合や、 + Serializable による型宣言を満たす場合には、 + 依然として有用です。 + 4つのメソッドすべてを宣言すれば、あらゆるケースに対応でき、 + 推奨されない警告も発生しません。 + + + + + マジックメソッドには専用のインターフェイスがありません。 + そのため、Serializable を実装せずに + マジックメソッドだけを宣言したクラスは、このインターフェイスのインスタンスにはなりません。 + マジックメソッドを検出するには method_exists を使います。 + +
@@ -54,45 +85,61 @@
&reftitle.examples; - 基本的な使用法 + PHP 7.1.0 から 7.3.0 までをサポートする + + シリアライズ後の形式はマジックメソッドが受け持ち、インターフェイスのメソッドは + そこに処理を委譲します。こうすることで、ひとつの表現で両方の仕組みをまかなえます。 + data = "My private data"; - } - public function serialize() { - return serialize($this->data); - } - public function unserialize($data) { - $this->data = unserialize($data); +class Task implements Serializable +{ + private $label; + + public function __construct($label) + { + $this->label = $label; } - public function getData() { - return $this->data; + + public function __serialize(): array + { + return ['label' => $this->label]; } -} -$obj = new obj; -$ser = serialize($obj); + public function __unserialize(array $data): void + { + $this->label = $data['label']; + } -var_dump($ser); + // PHP 7.4.0 以降はコールされません + public function serialize() + { + return serialize($this->__serialize()); + } -$newobj = unserialize($ser); + // PHP 7.4.0 以降も、それより前に書き出されたデータに対してはコールされます + public function unserialize($data) + { + $this->__unserialize(unserialize($data)); + } +} -var_dump($newobj->getData()); +var_dump(serialize(new Task('deploy'))); ?> ]]> - &example.outputs.similar; + &example.outputs; + + PHP 7.4.0 より前のバージョンでは、同じコードがインターフェイス経由でシリアライズを行い、 + string(47) "C:4:"Task":31:{a:1:{s:5:"label";s:6:"deploy";}}" + を出力します。 +