Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【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 __serializeserialize_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 $year151     * @param int $month152     * @param int $day153     * @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 $year165     * @param int $week166     * @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 $hour179     * @param int $minute180     * @param int $second181     * @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化する例です。

関連コンテンツ