【PHP8.x】Random\Engine\Mt19937::__serialize()メソッドの使い方
__serializeメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
__serializeメソッドは、Random\Engine\Mt19937クラスに属し、この擬似乱数生成エンジンの現在の内部状態をシリアライズするために実行されるメソッドです。シリアライズとは、オブジェクトが持つ内部のデータ(状態)を、ファイルに保存したりネットワーク経由で送信したりできるような、永続化可能な形式に変換する処理を指します。Random\Engine\Mt19937は、高品質な擬似乱数を生成するために広く用いられているメルセンヌ・ツイスターアルゴリズムを実装したエンジンです。
この__serializeメソッドは、乱数生成器が現在どのような状態にあるか、つまり次にどの乱数が生成されるかといった情報を正確に抽出し、保存可能なデータとして整形します。これにより、ある時点での乱数エンジンの状態を一時停止して保存し、後でその状態から乱数生成を再開したり、別の環境で全く同じ乱数シーケンスを再現したりすることが可能になります。例えば、ゲームのセーブデータに乱数生成の状態を含め、ロード時に続きから乱数を使いたい場合などに非常に有用です。このメソッドは、内部的にオブジェクトの重要な状態を表すキーと値のペアからなる配列を返し、PHPのシリアライズ機構によってそのデータが保存されます。
構文(syntax)
1<?php 2 3namespace Random\Engine; 4 5class Mt19937 6{ 7 public function __serialize(): array 8 { 9 return []; 10 } 11}
引数(parameters)
引数なし
引数はありません
戻り値(return)
array
__serialize メソッドは、オブジェクトの状態をシリアライズ可能な形式で表す連想配列を返します。この配列には、オブジェクトの内部状態を保存するために必要な情報が含まれています。
サンプルコード
PHP serialize_precision で浮動小数点数精度を制御する
1<?php 2 3/** 4 * このクラスは、PHPのRandom\Engine\Mt19937エンジンと浮動小数点数をラップし、 5 * カスタムのシリアライズ・デシリアライズ動作を定義します。 6 * 7 * __serializeメソッドの戻り値の配列に浮動小数点数を含めることで、 8 * PHPのini設定 `serialize_precision` がどのように影響するかを 9 * 示します。 10 */ 11class MySerializableWrapper 12{ 13 private Random\Engine\Mt19937 $engine; 14 private float $myFloat; 15 16 /** 17 * コンストラクタ。乱数エンジンと浮動小数点数を初期化します。 18 * 19 * @param float $initialFloat 初期設定する浮動小数点数 20 */ 21 public function __construct(float $initialFloat) 22 { 23 // Random\Engine\Mt19937はPHP 8以降で利用可能なMersenne Twister乱数エンジンです。 24 $this->engine = new Random\Engine\Mt19937(); 25 $this->myFloat = $initialFloat; 26 } 27 28 /** 29 * オブジェクトがserialize()関数によってシリアライズされる際に呼び出されます。 30 * オブジェクトのどのプロパティをシリアライズするかを定義する配列を返します。 31 * 32 * ここで返される配列内の値がPHPのserialize()によって処理されます。 33 * 配列に含まれる浮動小数点数は、`serialize_precision` のini設定の影響を受けます。 34 * 35 * @return array シリアライズするプロパティとその値の連想配列 36 */ 37 public function __serialize(): array 38 { 39 return [ 40 // Random\Engine\Mt19937オブジェクトは、それ自身の__serializeメソッドを 41 // 持っているため、PHPは内部でそのメソッドを呼び出して状態をシリアライズします。 42 'engine_state' => $this->engine, 43 // この浮動小数点数は、MySerializableWrapperがシリアライズされる際に 44 // `serialize_precision` の設定によって精度が影響を受けます。 45 'my_float_value' => $this->myFloat, 46 ]; 47 } 48 49 /** 50 * オブジェクトがunserialize()関数によってデシリアライズされる際に呼び出されます。 51 * __serializeメソッドから返された配列と同じ形式の配列を受け取り、 52 * オブジェクトの状態を再構築します。 53 * 54 * @param array $data オブジェクトを再構築するためのデータ 55 */ 56 public function __unserialize(array $data): void 57 { 58 $this->engine = $data['engine_state']; 59 $this->myFloat = $data['my_float_value']; 60 } 61 62 /** 63 * 格納されている浮動小数点数を取得します。 64 * 65 * @return float 66 */ 67 public function getMyFloat(): float 68 { 69 return $this->myFloat; 70 } 71 72 /** 73 * 格納されているRandom\Engine\Mt19937インスタンスを取得します。 74 * 75 * @return Random\Engine\Mt19937 76 */ 77 public function getEngine(): Random\Engine\Mt19937 78 { 79 return $this->engine; 80 } 81} 82 83// ----------------------------------------------------------------------------- 84// サンプルコードの実行部分 85// ----------------------------------------------------------------------------- 86 87// PHP 8以降であることを確認 88if (version_compare(PHP_VERSION, '8.0.0', '<')) { 89 echo "このスクリプトはPHP 8.0以降が必要です。" . PHP_EOL; 90 exit(1); 91} 92 93// 非常に高い精度を持つ浮動小数点数を定義 94$originalFloat = 1.234567890123456789; // デフォルトのPHPシリアライズ精度(約17桁)を超える 95echo "元の浮動小数点数: " . sprintf('%.20F', $originalFloat) . PHP_EOL . PHP_EOL; 96 97// 1. `serialize_precision` をデフォルト値 (-1 は通常約17桁を意味します) でテスト 98echo "--- serialize_precision = " . ini_get('serialize_precision') . " (デフォルト) ---" . PHP_EOL; 99// `ini_set` は実行時のみ設定を変更し、スクリプト終了後に元に戻ります 100ini_set('serialize_precision', -1); 101 102$wrapperDefault = new MySerializableWrapper($originalFloat); 103$serializedDefault = serialize($wrapperDefault); 104echo "シリアライズされた文字列 (デフォルト精度): " . $serializedDefault . PHP_EOL; 105 106$unserializedDefault = unserialize($serializedDefault); 107echo "デシリアライズされた浮動小数点数 (デフォルト精度): " . sprintf('%.20F', $unserializedDefault->getMyFloat()) . PHP_EOL; 108echo "元の値と一致するか (デフォルト精度): " . ($originalFloat === $unserializedDefault->getMyFloat() ? "はい" : "いいえ") . PHP_EOL . PHP_EOL; 109 110// 2. `serialize_precision` を高い値 (20) でテスト 111echo "--- serialize_precision = 20 ---" . PHP_EOL; 112ini_set('serialize_precision', 20); 113 114$wrapperHighPrec = new MySerializableWrapper($originalFloat); 115$serializedHighPrec = serialize($wrapperHighPrec); 116echo "シリアライズされた文字列 (高精度): " . $serializedHighPrec . PHP_EOL; 117 118$unserializedHighPrec = unserialize($serializedHighPrec); 119echo "デシリアライズされた浮動小数点数 (高精度): " . sprintf('%.20F', $unserializedHighPrec->getMyFloat()) . PHP_EOL; 120echo "元の値と一致するか (高精度): " . ($originalFloat === $unserializedHighPrec->getMyFloat() ? "はい" : "いいえ") . PHP_EOL . PHP_EOL; 121 122// 3. `serialize_precision` を低い値 (5) でテスト 123echo "--- serialize_precision = 5 ---" . PHP_EOL; 124ini_set('serialize_precision', 5); 125 126$wrapperLowPrec = new MySerializableWrapper($originalFloat); 127$serializedLowPrec = serialize($wrapperLowPrec); 128echo "シリアライズされた文字列 (低精度): " . $serializedLowPrec . PHP_EOL; 129 130$unserializedLowPrec = unserialize($serializedLowPrec); 131echo "デシリアライズされた浮動小数点数 (低精度): " . sprintf('%.20F', $unserializedLowPrec->getMyFloat()) . PHP_EOL; 132echo "元の値と一致するか (低精度): " . ($originalFloat === $unserializedLowPrec->getMyFloat() ? "はい" : "いいえ") . PHP_EOL . PHP_EOL; 133 134// 注: 浮動小数点数の比較には === (厳密な比較) を使用していますが、 135// 浮動小数点数の特性上、誤差が生じることがあります。 136// 実際のアプリケーションでは、特定の許容範囲内での比較 (例: abs($a - $b) < EPSILON) 137// を使用することが推奨されます。
PHP 8で導入されたRandom\Engine\Mt19937::__serializeメソッドは、この乱数エンジンのインスタンスがserialize()関数によって文字列に変換される際に呼び出される特別なメソッドです。引数はなく、オブジェクトの現在の状態を表す連想配列を戻り値として返します。このメソッドが返す配列の内容が、最終的にシリアライズされた文字列に含まれることになります。
サンプルコードでは、MySerializableWrapperというカスタムクラスを定義し、その中でRandom\Engine\Mt19937インスタンスと浮動小数点数をラップしています。このクラスの__serializeメソッドでは、ラップされたエンジンと浮動小数点数を配列として返しており、これがシリアライズの対象となります。
特に注目すべき点は、PHPのini設定であるserialize_precisionが、シリアライズされる浮動小数点数の精度にどのように影響するかを示していることです。この設定値は、浮動小数点数を文字列として表現する際の有効桁数を決定します。serialize_precisionの値によって、シリアライズされた文字列に含まれる浮動小数点数の精度が変化し、その結果、デシリアライズ後に元の浮動小数点数と一致しない場合があることを、具体的な設定変更と出力で比較して解説しています。
__serializeメソッドは、オブジェクトの状態を安全に保存し、後で復元するために利用します。特に浮動小数点数を扱う場合、PHPのserialize_precision設定値によってシリアライズされる精度が変わるため、デシリアライズ後に元の値と一致しない可能性があります。異なる環境でオブジェクトをシリアライズ・デシリアライズする場合、serialize_precisionの設定差が浮動小数点数の誤差に繋がる点に留意してください。安全性を確保するため、浮動小数点数の比較には、厳密な比較ではなく、許容範囲を設けた比較方法を検討することが推奨されます。
PHP Randomクラスのserialize/unserialize
1<?php 2 3/** 4 * Random\Engine\Mt19937 クラスのカスタムシリアライズとデシリアライズをデモンストレーションします。 5 * 6 * この関数は、PHPの serialize() および unserialize() 関数が内部で 7 * Random\Engine\Mt19937::__serialize() メソッドをどのように利用して 8 * オブジェクトの状態を保存・復元するかを示します。 9 * 10 * このコードは PHP 8.2 以降で動作します。 11 */ 12function demonstrateRandomEngineSerialization(): void 13{ 14 echo "--- Random\\Engine\\Mt19937 のカスタムシリアライズとデシリアライズのデモンストレーション ---" . PHP_EOL . PHP_EOL; 15 16 // 1. オリジナル Random\Engine\Mt19937 インスタンスの作成 17 // 特定のシード値で初期化することで、再現可能な乱数列を生成します。 18 $seed = 54321; 19 $originalEngine = new Random\Engine\Mt19937($seed); 20 // Random\Randomizer は、Random\Engine を利用してより高レベルな乱数生成機能を提供します。 21 $originalRandomizer = new Random\Randomizer($originalEngine); 22 23 echo "■ オリジナルエンジンからの初期乱数生成:" . PHP_EOL; 24 for ($i = 0; $i < 3; $i++) { 25 echo "- " . $originalRandomizer->getInt(1, 100) . PHP_EOL; 26 } 27 echo PHP_EOL; 28 29 // 2. オブジェクトをシリアライズ 30 // PHPの serialize() 関数が呼び出されると、オブジェクトに __serialize() メソッドが定義されている場合、 31 // そのメソッドが内部で呼び出され、オブジェクトの状態が配列として取得されます。 32 // その配列が最終的にバイナリ文字列に変換され、$serializedData に格納されます。 33 $serializedData = serialize($originalEngine); 34 echo "■ シリアライズされたデータ(文字列形式):" . PHP_EOL; 35 echo "データ長: " . strlen($serializedData) . " バイト" . PHP_EOL; 36 // シリアライズされた生データは通常読みにくいため、ここでは内容を表示しません。 37 echo PHP_EOL; 38 39 // 3. シリアライズされたデータをデシリアライズ 40 // unserialize() 関数は、シリアライズされた文字列を基に新しいオブジェクトを生成します。 41 // オブジェクトに __unserialize() メソッドが定義されている場合、内部的にそれが呼び出され、 42 // シリアライズされた配列の内容を使ってオブジェクトの状態が復元されます。 43 /** @var Random\Engine\Mt19937 $deserializedEngine */ 44 $deserializedEngine = unserialize($serializedData); 45 $deserializedRandomizer = new Random\Randomizer($deserializedEngine); 46 47 echo "■ デシリアライズされたエンジンからの乱数生成:" . PHP_EOL; 48 echo "(シリアライズ時のオリジナルエンジンの状態の続きが生成されます)" . PHP_EOL; 49 for ($i = 0; $i < 3; $i++) { 50 echo "- " . $deserializedRandomizer->getInt(1, 100) . PHP_EOL; 51 } 52 echo PHP_EOL; 53 54 // 4. オリジナルエンジンの続きから乱数生成 55 echo "■ オリジナルエンジンの続きからの乱数生成:" . PHP_EOL; 56 // オリジナルエンジンも状態を保持しているので、前回の続きから乱数を生成します。 57 for ($i = 0; $i < 3; $i++) { 58 echo "- " . $originalRandomizer->getInt(1, 100) . PHP_EOL; 59 } 60 echo PHP_EOL; 61 62 echo "--- 結果のまとめ ---" . PHP_EOL; 63 echo "デシリアライズされたエンジンは、シリアライズ時のオリジナルエンジンの状態を正確に復元し、" . PHP_EOL; 64 echo "そこから同じ乱数列を生成し続けることが確認できます。" . PHP_EOL; 65 echo "これにより、オブジェクトの状態を保存し、後で全く同じ状態からその処理を再開することが可能になります。" . PHP_EOL; 66} 67 68// デモンストレーションを実行します。 69demonstrateRandomEngineSerialization();
PHPのRandom\Engine\Mt19937::__serializeメソッドは、乱数エンジンオブジェクトの現在の内部状態を保存するための特殊なメソッドです。このメソッドは、PHPのserialize()関数を用いてオブジェクトを文字列形式に変換する際に、内部的に自動で呼び出されます。引数はなく、オブジェクトの状態を正確に再現するために必要なデータを含む配列を戻り値として返します。
サンプルコードでは、まず乱数エンジンを作成し、いくつかの乱数を生成してその状態を進めます。その後、serialize()関数でこのエンジンを文字列化すると、__serializeが内部で働き、その時点のオブジェクトの状態情報が配列として取得され、シリアライズデータに組み込まれます。次にunserialize()関数で文字列から新しい乱数エンジンオブジェクトを復元します。この復元されたエンジンは、シリアライズ時の元のエンジンの状態を正確に引き継ぎます。結果として、デシリアライズ後に生成される乱数は、元のエンジンの続きと同じ乱数列を生成することが確認でき、これによりオブジェクトの状態を保存して後から全く同じ状態から処理を再開することが可能になります。
このサンプルコードはPHP 8.2以降のRandom名前空間の機能ですが、__serializeメソッドはPHP 8.0から導入されたカスタムシリアライズ機能です。serialize()時にはこのメソッドが返す配列が処理されます。オブジェクトの状態を正確に復元するには、通常__unserialize()メソッドとの連携が必要です。PHPバージョンやクラス定義の変更によって、シリアライズデータの互換性が失われる可能性があるため注意が必要です。また、不明なソースからのデシリアライズは、オブジェクトインジェクションなどのセキュリティ上のリスクから避けるべきです。