【PHP8.x】DateTimeInterface::__serialize()メソッドの使い方
__serializeメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
__serializeメソッドは、PHP 8以降でオブジェクトをシリアライズする際に実行されるマジックメソッドです。このメソッドは、オブジェクトの内部状態を保存可能な形式に変換するために呼び出されます。シリアライズとは、オブジェクトの状態をデータとして保存したり、ネットワーク経由で送信したりできるよう、一連のバイト列や配列などに変換する処理のことです。
DateTimeInterfaceに定義されている__serializeメソッドは、このインターフェースを実装するDateTimeやDateTimeImmutableなどの日付・時刻オブジェクトがシリアライズされる際に、そのオブジェクトの持つ日付、時刻、タイムゾーンといった重要な情報を正確に取得し、配列として返す役割を担います。このメソッドが返す配列データは、後でオブジェクトを元の状態に復元する際(アンシリアライズ時)に__unserializeメソッドによって利用されます。
これにより、DateTimeオブジェクトが持つ複雑な内部状態(例:タイムゾーン情報を含む特定の日時)が失われることなく、データベースへの保存やキャッシュ、プロセス間通信など、様々な用途で安全に永続化・復元できるようになります。PHP 8では、従来の__sleepメソッドに代わり、より直感的で堅牢なシリアライズメカニズムを提供するために導入されました。
構文(syntax)
1<?php 2 3class MyDateRelatedClass implements DateTimeInterface 4{ 5 public function __serialize(): array 6 { 7 // ここにシリアライズしたいデータを配列で記述します。 8 // 例: return ['date' => $this->format('Y-m-d H:i:s.u'), 'timezone' => $this->getTimezone()->getName()]; 9 return []; 10 } 11 12 // NOTE: DateTimeInterface を完全に実装するには、 13 // 他の抽象メソッドも実装する必要がありますが、 14 // この例では __serialize メソッドの構文のみを示します。 15 public function format(string $format): string { return ''; } 16 public function getTimezone(): \DateTimeZone { return new \DateTimeZone('UTC'); } 17 public function getOffset(): int { return 0; } 18 public function getTimestamp(): int { return 0; } 19 public function diff(\DateTimeInterface $targetObject, bool $absolute = false): \DateInterval { return new \DateInterval('PT0S'); } 20 public function __toString(): string { return ''; } 21}
引数(parameters)
引数なし
引数はありません
戻り値(return)
array
DateTimeInterface::__serialize() メソッドは、DateTimeInterface オブジェクトの内部状態を表す連想配列を返します。この配列は、オブジェクトをシリアライズ(文字列化)する際に使用されます。
サンプルコード
PHP __serialize と serialize_precision による浮動小数点数シリアライズ
1<?php 2 3/** 4 * DateTimeInterface を実装し、__serialize メソッドを提供するカスタムクラス。 5 * このクラスは内部に DateTimeImmutable オブジェクトを持ち、日付/時刻の処理を委譲します。 6 * serialize_precision の影響を示すために浮動小数点数プロパティをシリアライズします。 7 */ 8class MyDateTimeWrapper implements DateTimeInterface 9{ 10 private DateTimeImmutable $internalDateTime; 11 private float $customPrecisionValue; 12 13 /** 14 * コンストラクタ 15 * 16 * @param string $time 日時文字列 17 * @param DateTimeZone|null $timezone タイムゾーンオブジェクト (nullの場合は現在のデフォルトタイムゾーン) 18 * @param float $precisionValue シリアライズ精度をテストするための浮動小数点数 19 */ 20 public function __construct( 21 string $time = "now", 22 ?DateTimeZone $timezone = null, 23 float $precisionValue = 0.12345678901234567 24 ) { 25 $this->internalDateTime = new DateTimeImmutable($time, $timezone); 26 $this->customPrecisionValue = $precisionValue; 27 } 28 29 /** 30 * DateTimeInterface のメソッド: 指定されたフォーマットで日時を文字列として返します。 31 * 32 * @param string $format フォーマット文字列 33 * @return string フォーマットされた日時文字列 34 */ 35 public function format(string $format): string 36 { 37 return $this->internalDateTime->format($format); 38 } 39 40 /** 41 * DateTimeInterface のメソッド: UTCからのオフセット秒数を返します。 42 * 43 * @return int オフセット秒数 44 */ 45 public function getOffset(): int 46 { 47 return $this->internalDateTime->getOffset(); 48 } 49 50 /** 51 * DateTimeInterface のメソッド: UNIXタイムスタンプを返します。 52 * 53 * @return int UNIXタイムスタンプ 54 */ 55 public function getTimestamp(): int 56 { 57 return $this->internalDateTime->getTimestamp(); 58 } 59 60 /** 61 * DateTimeInterface のメソッド: 現在のタイムゾーンオブジェクトを返します。 62 * 63 * @return DateTimeZone|false タイムゾーンオブジェクト、またはエラー時にfalse 64 */ 65 public function getTimezone(): DateTimeZone|false 66 { 67 return $this->internalDateTime->getTimezone(); 68 } 69 70 /** 71 * PHP 8.1 以降のオブジェクトシリアライズ用のマジックメソッド。 72 * オブジェクトをシリアライズする際に、どのデータを保存するかを配列で返します。 73 * このメソッドが返す配列内の浮動小数点数は `serialize_precision` の設定に影響されます。 74 * 75 * @return array シリアライズするデータの連想配列 76 */ 77 public function __serialize(): array 78 { 79 return [ 80 'datetime_iso' => $this->internalDateTime->format(DateTimeInterface::ATOM), 81 'timezone_name' => $this->internalDateTime->getTimezone()->getName(), 82 'customPrecisionValue' => $this->customPrecisionValue, 83 ]; 84 } 85 86 /** 87 * PHP 8.1 以降のオブジェクトアンシリアライズ用のマジックメソッド。 88 * `__serialize` メソッドが返した配列を受け取り、オブジェクトの状態を復元します。 89 * 90 * @param array $data 復元するデータの連想配列 91 */ 92 public function __unserialize(array $data): void 93 { 94 $this->internalDateTime = new DateTimeImmutable($data['datetime_iso'], new DateTimeZone($data['timezone_name'])); 95 $this->customPrecisionValue = $data['customPrecisionValue']; 96 } 97 98 /** 99 * カスタムプロパティ (浮動小数点数) のゲッター。 100 * 101 * @return float 浮動小数点数値 102 */ 103 public function getCustomPrecisionValue(): float 104 { 105 return $this->customPrecisionValue; 106 } 107 108 // DateTimeInterface のその他のメソッドは、内部の DateTimeImmutable オブジェクトに処理を委譲します。 109 // 以下は、DateTimeInterface を完全に実装するために必要なメソッド群です。 110 111 /** 112 * DateTimeInterface のメソッド: 日時を変更します。 113 * 114 * @param string $modifier 変更文字列 115 * @return static 変更後のオブジェクト 116 */ 117 public function modify(string $modifier): static 118 { 119 $this->internalDateTime = $this->internalDateTime->modify($modifier); 120 return $this; 121 } 122 123 /** 124 * DateTimeInterface のメソッド: 日時を加算します。 125 * 126 * @param DateInterval $interval 加算する期間 127 * @return static 加算後のオブジェクト 128 */ 129 public function add(DateInterval $interval): static 130 { 131 $this->internalDateTime = $this->internalDateTime->add($interval); 132 return $this; 133 } 134 135 /** 136 * DateTimeInterface のメソッド: 日時を減算します。 137 * 138 * @param DateInterval $interval 減算する期間 139 * @return static 減算後のオブジェクト 140 */ 141 public function sub(DateInterval $interval): static 142 { 143 $this->internalDateTime = $this->internalDateTime->sub($interval); 144 return $this; 145 } 146 147 /** 148 * DateTimeInterface のメソッド: 日付を設定します。 149 * 150 * @param int $year 年 151 * @param int $month 月 152 * @param int $day 日 153 * @return static 設定後のオブジェクト 154 */ 155 public function setDate(int $year, int $month, int $day): static 156 { 157 $this->internalDateTime = $this->internalDateTime->setDate($year, $month, $day); 158 return $this; 159 } 160 161 /** 162 * DateTimeInterface のメソッド: ISO日付を設定します。 163 * 164 * @param int $year 年 165 * @param int $week 週 166 * @param int $dayOfWeek 曜日 (1-7, 月曜日が1) 167 * @return static 設定後のオブジェクト 168 */ 169 public function setISODate(int $year, int $week, int $dayOfWeek = 1): static 170 { 171 $this->internalDateTime = $this->internalDateTime->setISODate($year, $week, $dayOfWeek); 172 return $this; 173 } 174 175 /** 176 * DateTimeInterface のメソッド: 時刻を設定します。 177 * 178 * @param int $hour 時 179 * @param int $minute 分 180 * @param int $second 秒 181 * @param int $microsecond マイクロ秒 182 * @return static 設定後のオブジェクト 183 */ 184 public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): static 185 { 186 $this->internalDateTime = $this->internalDateTime->setTime($hour, $minute, $second, $microsecond); 187 return $this; 188 } 189 190 /** 191 * DateTimeInterface のメソッド: UNIXタイムスタンプを設定します。 192 * 193 * @param int $timestamp UNIXタイムスタンプ 194 * @return static 設定後のオブジェクト 195 */ 196 public function setTimestamp(int $timestamp): static 197 { 198 $this->internalDateTime = $this->internalDateTime->setTimestamp($timestamp); 199 return $this; 200 } 201 202 /** 203 * DateTimeInterface のメソッド: タイムゾーンを設定します。 204 * 205 * @param DateTimeZone $timezone タイムゾーンオブジェクト 206 * @return static 設定後のオブジェクト 207 */ 208 public function setTimezone(DateTimeZone $timezone): static 209 { 210 $this->internalDateTime = $this->internalDateTime->setTimezone($timezone); 211 return $this; 212 } 213 214 /** 215 * DateTimeInterface のメソッド: 2つのDateTimeオブジェクトの差を計算します。 216 * 217 * @param DateTimeInterface $targetObject 比較対象のDateTimeオブジェクト 218 * @param bool $absolute 絶対値で返すか (trueの場合、常に正の値) 219 * @return DateInterval 期間オブジェクト 220 */ 221 public function diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval 222 { 223 return $this->internalDateTime->diff($targetObject, $absolute); 224 } 225} 226 227// --- サンプルコード開始 --- 228 229// MyDateTimeWrapper クラスのインスタンスを作成 230$initialPrecisionValue = 0.12345678901234567; // テスト用の浮動小数点数 231$myDtWrapper = new MyDateTimeWrapper("2023-10-27 10:30:45", new DateTimeZone("Asia/Tokyo"), $initialPrecisionValue); 232 233echo "--- シリアライズ前の情報 ---\n"; 234echo "元のDateTime: " . $myDtWrapper->format('Y-m-d H:i:s P') . "\n"; 235// sprintf('%.17F', ...) は浮動小数点数を17桁の精度で表示するための関数です。 236// これはPHPが内部で浮動小数点数をどのように扱うかを示すためであり、`serialize_precision` の動作とは直接関係ありません。 237echo "元の浮動小数点数: " . sprintf('%.17F', $myDtWrapper->getCustomPrecisionValue()) . "\n\n"; 238 239// 現在の serialize_precision の設定を保存し、スクリプト終了時に元の設定に戻します。 240$originalSerializePrecision = ini_get('serialize_precision'); 241 242echo "--- serialize_precision = -1 (デフォルト、内部精度で可能な限り正確にシリアライズ) ---\n"; 243// ini_set() を使用して、浮動小数点数のシリアライズ精度を変更します。 244// -1 はPHPが内部的に可能な限り正確な値をシリアライズすることを意味します。 245ini_set('serialize_precision', -1); 246$serializedDataDefault = serialize($myDtWrapper); 247echo "シリアライズ結果:\n" . $serializedDataDefault . "\n"; 248$unserializedDtDefault = unserialize($serializedDataDefault); 249echo "復元された浮動小数点数: " . sprintf('%.17F', $unserializedDtDefault->getCustomPrecisionValue()) . "\n\n"; 250 251echo "--- serialize_precision = 5 (小数点以下5桁まででシリアライズ) ---\n"; 252ini_set('serialize_precision', 5); 253$serializedDataPrecise5 = serialize($myDtWrapper); 254echo "シリアライズ結果:\n" . $serializedDataPrecise5 . "\n"; 255$unserializedDtPrecise5 = unserialize($serializedDataPrecise5); 256echo "復元された浮動小数点数: " . sprintf('%.17F', $unserializedDtPrecise5->getCustomPrecisionValue()) . "\n\n"; 257 258echo "--- serialize_precision = 2 (小数点以下2桁まででシリアライズ) ---\n"; 259ini_set('serialize_precision', 2); 260$serializedDataPrecise2 = serialize($myDtWrapper); 261echo "シリアライズ結果:\n" . $serializedDataPrecise2 . "\n"; 262$unserializedDtPrecise2 = unserialize($serializedDataPrecise2); 263echo "復元された浮動小数点数: " . sprintf('%.17F', $unserializedDtPrecise2->getCustomPrecisionValue()) . "\n\n"; 264 265// スクリプトの実行が完了したら、`serialize_precision` の値を元の設定に戻します。 266ini_set('serialize_precision', $originalSerializePrecision);
PHP 8の__serializeメソッドは、オブジェクトをserialize()関数で文字列に変換する際に、どのようなデータを保存するかをプログラマが独自に定義できる特殊なメソッドです。このメソッドは引数を取らず、シリアライズしたいデータを連想配列として返す必要があります。DateTimeInterfaceを実装するクラスでこのメソッドを使用すると、日付や時刻の情報だけでなく、そのオブジェクトが持つ他のカスタムデータも一緒にシリアライズ・アンシリアライズの対象にできます。
特に、__serializeメソッドが返す配列内に浮動小数点数(小数点を持つ数値)が含まれる場合、PHPの設定であるserialize_precisionがその精度に大きく影響します。この設定は、浮動小数点数がシリアライズされる際に小数点以下何桁まで保存されるかを制御します。例えば、-1に設定するとPHPは可能な限り正確な値を保存しようとしますが、正の整数を指定するとその桁数で丸められ、精度が失われる可能性があります。
サンプルコードでは、MyDateTimeWrapperというクラスがDateTimeInterfaceを実装し、日付情報とカスタムの浮動小数点数を持っています。__serializeメソッドはこれらの情報を配列として返しています。コードの実行部分では、serialize_precisionの設定を様々に変更しながらオブジェクトをシリアライズ・アンシリアライズし、カスタムの浮動小数点数がどのように変化するかを具体的に示しています。これにより、serialize_precisionが浮動小数点数の保存精度に与える影響を視覚的に理解することができます。
このサンプルコードは、オブジェクトを文字列に変換する__serializeメソッドと、浮動小数点数のシリアライズ精度を制御するserialize_precision設定の動作を示しています。__serializeメソッドは、PHP 8.1以降でオブジェクトをserialize()する際にどのデータを保存するかをカスタム定義するために使用します。特に注意すべきはserialize_precisionの設定です。これを小さい値にすると、浮動小数点数が丸められてしまい、データが復元された際に元の値と異なることがあります。金融計算など厳密な精度が求められる場面では、この設定がデータ損失につながる可能性があるため非常に重要です。通常は-1を設定し、PHPに可能な限り正確な値を扱わせることを推奨します。また、ini_setで設定を変更した場合は、必ず元の値に戻すよう心がけてください。
PHP DateTime __serialize 配列を JSON 変換する
1<?php 2 3/** 4 * DateTimeInterface::__serialize はPHP 8.1以降で導入されたマジックメソッドです。 5 * オブジェクトが serialize() 関数によってシリアライズされる際にPHP内部で呼び出され、 6 * そのオブジェクトの内部状態を表す配列を返します。 7 * 8 * このサンプルコードでは、DateTimeオブジェクトのシリアライズ時に__serializeが返すであろう配列を模倣し、 9 * その配列をJSON形式に変換する方法を示します。 10 * __serializeメソッド自体はPHPの内部処理のために存在し、通常、開発者が直接呼び出すものではない点に注意してください。 11 */ 12 13// 現在の日時を持つ DateTime オブジェクトを作成します。 14// 例として、特定の日時とタイムゾーンを設定しています。 15$dateTime = new DateTime('2023-10-27 10:30:00', new DateTimeZone('Asia/Tokyo')); 16 17// DateTime::__serialize が返すであろう配列の構造を模倣します。 18// 実際のPHP内部実装では、DateTimeオブジェクトの内部状態を再構築するために必要な 19// 複雑な情報を含む配列が返されます。 20// ここでは、日付の文字列表現、タイムゾーンの種類、タイムゾーン名といった、 21// 代表的な情報を抽出して配列を作成し、__serializeの「戻り値が配列である」という概念を示します。 22// timezone_type '3' は、PHP内部でタイムゾーンが名前で指定されていることを示す定数です。 23$serializedRepresentation = [ 24 'date' => $dateTime->format('Y-m-d H:i:s.u'), // 日付と時刻をマイクロ秒まで含む文字列 25 'timezone_type' => 3, // PHP内部でタイムゾーンが名前で指定されていることを示すタイプ 26 'timezone' => $dateTime->getTimezone()->getName(), // タイムゾーン名 27]; 28 29// 模倣した配列をJSON形式に変換します。 30// JSON_PRETTY_PRINT: JSONを整形して出力し、読みやすくします。 31// JSON_UNESCAPED_UNICODE: マルチバイト文字(日本語など)をエスケープせずに表示します。 32$jsonOutput = json_encode($serializedRepresentation, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); 33 34// 生成されたJSONを出力します。 35echo "DateTimeオブジェクトのシリアライズ表現(__serializeが返す配列を模倣)をJSONに変換:\n"; 36echo $jsonOutput . "\n\n"; 37 38// 参考情報: 39// DateTimeオブジェクトを直接 json_encode() した場合は、 40// そのオブジェクトのISO 8601形式の文字列がJSONに変換されます。 41// これは__serializeとは異なるメカニズムで処理されます。 42$jsonDirectOutput = json_encode($dateTime, JSON_PRETTY_PRINT); 43echo "DateTimeオブジェクトを直接JSONに変換(ISO 8601形式):\n"; 44echo $jsonDirectOutput . "\n"; 45
PHP 8.1以降で導入されたDateTimeInterface::__serializeは、マジックメソッドと呼ばれる特別なメソッドです。これは、DateTimeオブジェクトがserialize()関数によってシリアライズされる際に、PHPの内部で自動的に呼び出されます。引数はなく、オブジェクトの現在の内部状態を示す配列を戻り値として返します。通常、開発者が直接呼び出すことはなく、オブジェクトの永続化などの内部処理に利用されます。
サンプルコードでは、この__serializeが返すであろう配列の構造を模倣し、DateTimeオブジェクトの日付やタイムゾーン情報を配列に格納しています。その配列をjson_encode()関数でJSON形式に変換し、JSON_PRETTY_PRINTオプションで整形して出力しています。この例から、オブジェクトの内部状態が配列としてどのように表現されるかを学ぶことができます。なお、DateTimeオブジェクトを直接json_encode()した場合は、__serializeとは異なる仕組みでISO 8601形式の日時文字列がJSONとして出力されるため、この違いも理解しておくと良いでしょう。
DateTimeInterface::__serializeメソッドはPHP 8.1以降で導入された、オブジェクトのシリアライズ時にPHP内部で自動的に呼び出されるマジックメソッドです。開発者が直接呼び出すものではなく、サンプルコードは本来このメソッドが返す配列の構造を模倣しています。そのため、実際のPHP内部で利用される配列とは完全に同じではない点にご注意ください。また、DateTimeオブジェクトを直接json_encode()関数でJSON化すると、この__serializeの仕組みとは異なり、ISO 8601形式の文字列として変換されますので、利用目的に合わせて使い分けてください。このサンプルは、serialize()関数が出力するバイナリデータをJSONに変換するものではなく、オブジェクトの状態を表す配列をJSON化する例です。