【PHP8.x】__serializeメソッドの使い方

__serializeメソッドの使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

__serializeメソッドは、DateTimeオブジェクトをシリアライズ(直列化)する際に呼び出されるメソッドです。このメソッドは、PHPがオブジェクトを文字列として保存したり、ネットワークを通じて転送したりする際に、オブジェクトの状態をどのように表現するかを定義するために使用されます。

具体的には、__serializeメソッドは、DateTimeオブジェクトの内部状態を表現する配列を返す必要があります。この配列は、オブジェクトをunserialize(直列化復元)する際に__unserializeメソッドに渡され、オブジェクトを元の状態に復元するために使用されます。

DateTimeオブジェクトの場合、通常はタイムスタンプ、タイムゾーン情報、および関連するプロパティが配列に格納されます。このメソッドを実装することで、DateTimeオブジェクトのシリアライズ処理をカスタマイズし、オブジェクトの状態を正確に保存および復元することが可能です。

例えば、DateTimeImmutableオブジェクトをシリアライズする場合など、DateTimeオブジェクトの特性に合わせて、シリアライズの方法を調整する必要がある場合に特に有用です。デフォルトのシリアライズ処理では対応できない複雑な状態を扱う場合にも、このメソッドを使用することで、柔軟なシリアライズ処理を実現できます。

このメソッドは、PHPのシリアライズメカニズムの一部として機能し、オブジェクトの永続化やデータ交換を効率的に行うための重要な役割を果たします。システムエンジニアは、このメソッドを理解し、適切に使用することで、DateTimeオブジェクトを含む複雑なデータ構造を安全かつ効率的に処理できるようになります。

構文(syntax)

1public DateTime::__serialize(): array

引数(parameters)

引数なし

引数はありません

戻り値(return)

array

__serialize メソッドは、DateTime オブジェクトをシリアライズ(文字列化)するために内部的に使用される配列を返します。この配列には、オブジェクトの状態を復元するために必要な情報が含まれています。

サンプルコード

PHP DateTime __serializeとserialize_precisionの挙動

1<?php
2
3/**
4 * PHP 8のDateTime::__serializeメソッド (概念) と
5 * php.ini設定 serialize_precision の関連性を示すサンプルコード。
6 *
7 * 注意: PHPの組み込みDateTimeクラスは、ユーザーが直接オーバーライド可能な
8 *      __serializeマジックメソッドを持っていません。
9 *      このCustomDateTimeクラスは、DateTimeを継承し、ユーザーが__serializeを
10 *      実装した場合の挙動と、serialize_precisionの影響をデモンストレーションする
11 *      ためのものです。組み込みDateTimeのシリアライズは内部的なメカニズムで行われます。
12 */
13class CustomDateTime extends DateTime
14{
15    /**
16     * オブジェクトがserialize()関数によってシリアライズされる際に呼び出されます。
17     * このメソッドは、オブジェクトのプロパティを表現する連想配列を返します。
18     * PHP 8の組み込みDateTimeオブジェクトのシリアライズは、内部的に同様のデータ構造を
19     * 生成しますが、この例ではカスタムのプロパティも追加しています。
20     *
21     * @return array シリアライズされるオブジェクトの状態を表す配列。
22     */
23    public function __serialize(): array
24    {
25        // DateTimeオブジェクトの重要な状態(タイムスタンプとタイムゾーン)を
26        // 配列として返します。
27        // ここに、serialize_precisionの設定が影響を与える浮動小数点数を含めます。
28        return [
29            'timestamp' => $this->getTimestamp(),
30            'timezone' => $this->getTimezone()->getName(),
31            // serialize_precision の影響を示すための浮動小数点数。
32            // これはDateTimeオブジェクトの本来のプロパティではありませんが、
33            // __serializeが返す配列に含めることで、その影響を確認できます。
34            'example_float_data' => 123.4567890123456789,
35        ];
36    }
37
38    /**
39     * オブジェクトがunserialize()関数によってアンシリアライズされる際に呼び出されます。
40     * __serialize()が返した配列を受け取り、オブジェクトの状態を復元します。
41     *
42     * @param array $data __serialize()が返したデータ配列。
43     */
44    public function __unserialize(array $data): void
45    {
46        // タイムゾーンを設定し、その後にタイムスタンプを設定してDateTimeオブジェクトの状態を復元します。
47        $this->setTimezone(new DateTimeZone($data['timezone']));
48        $this->setTimestamp($data['timestamp']);
49        // 'example_float_data' は、このCustomDateTimeオブジェクトの本来のプロパティではないため、
50        // アンシリアライズされたオブジェクトに動的にプロパティとして追加します。
51        $this->example_float_data = $data['example_float_data'];
52    }
53}
54
55// -------- serialize_precision の設定とシリアライズの結果 --------
56
57// serialize_precision をデフォルト値(-1、すべての有効桁数)に設定
58// 浮動小数点数は可能な限り精度を保ってシリアライズされます。
59ini_set('serialize_precision', -1);
60echo "--- serialize_precision = -1 (デフォルト、全桁数保持) ---\n";
61
62$originalDateTime = new CustomDateTime('2023-10-27 15:30:00 Asia/Tokyo');
63echo "オリジナルの浮動小数点数: " . sprintf('%.17F', $originalDateTime->example_float_data) . "\n";
64
65$serializedStringDefault = serialize($originalDateTime);
66echo "シリアライズされた文字列:\n" . $serializedStringDefault . "\n\n";
67
68$unserializedDateTimeDefault = unserialize($serializedStringDefault);
69echo "アンシリアライズ後の浮動小数点数: " . sprintf('%.17F', $unserializedDateTimeDefault->example_float_data) . "\n\n";
70
71// serialize_precision を特定の桁数に設定(例: 5桁)
72// 浮動小数点数は指定された精度で丸められてシリアライズされます。
73ini_set('serialize_precision', 5);
74echo "--- serialize_precision = 5 (小数点以下5桁まで) ---\n";
75
76// 再度オブジェクトを作成し、シリアライズします。
77// 同じオブジェクトを再シリアライズしても、__serializeが再度呼ばれ、新しいini設定が適用されます。
78$originalDateTime2 = new CustomDateTime('2023-10-27 15:30:00 Asia/Tokyo');
79echo "オリジナルの浮動小数点数: " . sprintf('%.17F', $originalDateTime2->example_float_data) . "\n";
80
81$serializedStringPrecision = serialize($originalDateTime2);
82echo "シリアライズされた文字列:\n" . $serializedStringPrecision . "\n\n";
83
84$unserializedDateTimePrecision = unserialize($serializedStringPrecision);
85echo "アンシリアライズ後の浮動小数点数: " . sprintf('%.17F', $unserializedDateTimePrecision->example_float_data) . "\n";
86
87// 浮動小数点数の比較
88if (abs($unserializedDateTimeDefault->example_float_data - $unserializedDateTimePrecision->example_float_data) > PHP_FLOAT_EPSILON) {
89    echo "注意: serialize_precision の設定により、浮動小数点数の値が変化しました。\n";
90}

DateTime::__serializeは、PHP 8で導入されたマジックメソッドの一つで、DateTimeオブジェクトがserialize()関数によって文字列に変換される際に呼び出されます。このメソッドはオブジェクトの現在の状態を正確に表現する連想配列を返す役割を持ち、引数は取りません。戻り値はarray型で、オブジェクトの再構築に必要なすべての情報を含みます。

組み込みのDateTimeクラスは、ユーザーが直接このメソッドをオーバーライドすることはできませんが、内部的には同様のメカニズムを用いてシリアライズ処理を行っています。提供されたサンプルコードは、DateTimeを継承したクラスで__serializeを実装することで、その挙動とphp.ini設定のserialize_precisionがシリアライズに与える影響をデモンストレーションしています。

serialize_precisionは、シリアライズされるデータに含まれる浮動小数点数の精度を制御する設定です。この設定値を変更すると、__serializeメソッドが返す配列内の浮動小数点数が、指定された精度で丸められて文字列化されます。例えば、デフォルトの-1(全桁数保持)から特定の桁数に設定することで、データのサイズを減らしたり、意図しない精度の丸めが行われる可能性もあるため、浮動小数点数を扱う際にはこの設定に注意が必要です。これにより、オブジェクトのデータを効率的かつ正確に保存・転送できるようになります。

PHPの組み込みDateTimeクラスは__serializeメソッドを直接オーバーライドできません。このサンプルコードは、その概念とphp.ini設定serialize_precisionの影響を理解していただくためのデモンストレーションとして、CustomDateTimeクラスで実装しています。オブジェクトをシリアライズする際は、__serialize__unserializeメソッドを必ずペアで実装し、元のオブジェクトの状態を正確に復元できるようにデータ構造を設計することが重要です。特に、serialize_precisionは浮動小数点数のシリアライズ精度に影響を与え、設定値によってはシリアライズ後に浮動小数点数の値が丸められ、アンシリアライズ後に元の値と異なる場合があります。異なるシステム間でオブジェクトをやり取りする場合や、厳密な数値精度が求められる場面では、この設定を十分に考慮し、データの整合性を慎重に確認するようにしてください。

PHP 8: __serializeでDateTimeを扱う

1<?php
2
3/**
4 * DateTimeオブジェクトをプロパティとして持つカスタムクラス。
5 * PHP 8で導入された__serializeと__unserializeマジックメソッドを実装し、
6 * オブジェクトがserialize()関数やunserialize()関数で処理される際の
7 * データのカスタマイズ方法を示します。
8 */
9class MyCustomData
10{
11    public string $name;
12    public DateTime $createdAt;
13    public int $id;
14
15    /**
16     * コンストラクタ。
17     */
18    public function __construct(string $name, DateTime $createdAt, int $id)
19    {
20        $this->name = $name;
21        $this->createdAt = $createdAt;
22        $this->id = $id;
23    }
24
25    /**
26     * オブジェクトがserialize()関数でシリアライズされる際に呼び出されます。
27     * シリアライズされるべきプロパティを連想配列で返します。
28     * DateTimeオブジェクトは直接ではなく、ISO 8601形式の文字列に変換して保存します。
29     * これにより、タイムゾーン情報なども保持しつつ、異なる環境間での互換性を高めます。
30     *
31     * @return array<string, mixed>
32     */
33    public function __serialize(): array
34    {
35        return [
36            'name' => $this->name,
37            'createdAt' => $this->createdAt->format(DateTime::ATOM),
38            'id' => $this->id,
39        ];
40    }
41
42    /**
43     * オブジェクトがunserialize()関数でデシリアライズされる際に呼び出されます。
44     * __serialize()によってシリアライズされたデータ配列を受け取り、
45     * オブジェクトのプロパティを再構築します。
46     *
47     * @param array<string, mixed> $data
48     */
49    public function __unserialize(array $data): void
50    {
51        $this->name = $data['name'];
52        // 文字列からDateTimeオブジェクトを再構築します。
53        // DateTime::ATOM形式はDateTimeコンストラクタで直接解析可能です。
54        $this->createdAt = new DateTime($data['createdAt']);
55        $this->id = $data['id'];
56    }
57
58    /**
59     * オブジェクトのデバッグ用文字列表現を提供します。
60     */
61    public function __toString(): string
62    {
63        return sprintf(
64            "ID: %d, Name: %s, Created At: %s (%s)",
65            $this->id,
66            $this->name,
67            $this->createdAt->format('Y-m-d H:i:s'),
68            $this->createdAt->getTimezone()->getName()
69        );
70    }
71}
72
73// 元のオブジェクトを作成(具体的な日時とタイムゾーンを指定)
74$originalData = new MyCustomData("Sample Item", new DateTime('2023-10-27 10:30:00', new DateTimeZone('Europe/Berlin')), 123);
75echo "元のオブジェクト: " . $originalData . "\n\n";
76
77// オブジェクトをシリアライズ。このとき__serialize()マジックメソッドが呼び出されます。
78$serializedString = serialize($originalData);
79echo "シリアライズされたデータ: " . $serializedString . "\n\n";
80
81// シリアライズされたデータをデシリアライズ。このとき__unserialize()マジックメソッドが呼び出されます。
82$unserializedData = unserialize($serializedString);
83echo "デシリアライズされたオブジェクト: " . $unserializedData . "\n\n";
84
85// 検証: 元のオブジェクトとデシリアライズされたオブジェクトの内容が一致するか確認
86echo "検証結果:\n";
87echo "  Nameの一致: " . ($originalData->name === $unserializedData->name ? "OK" : "NG") . "\n";
88echo "  CreatedAtの一致 (ISO形式): " . ($originalData->createdAt->format(DateTime::ATOM) === $unserializedData->createdAt->format(DateTime::ATOM) ? "OK" : "NG") . "\n";
89echo "  IDの一致: " . ($originalData->id === $unserializedData->id ? "OK" : "NG") . "\n";
90
91?>

PHP 8では、オブジェクトをファイルやデータベースなどに保存するために文字列に変換するserialize()関数と、その文字列を元のオブジェクトに復元するunserialize()関数が利用されます。クラス内に__serialize()メソッドを実装すると、オブジェクトがserialize()される際に、どのプロパティをどのように保存するかをカスタマイズできます。

__serialize()メソッドは引数を取らず、オブジェクト内で保存したいプロパティを連想配列として返します。サンプルコードでは、DateTimeオブジェクトを直接保存せず、DateTime::ATOM形式の文字列に変換しています。これにより、日付やタイムゾーン情報を正確に保持し、異なる環境間での互換性を高めます。

一方、unserialize()関数でオブジェクトが復元される際には、__unserialize()メソッドが呼び出されます。このメソッドは、__serialize()が返したデータ配列を引数として受け取り、それを使ってオブジェクトのプロパティを再構築します。例えば、文字列からDateTimeオブジェクトを正確に復元します。このように__serialize()__unserialize()を組み合わせることで、複雑なオブジェクトのデータも安全かつ正確に保存・復元できるのです。

このサンプルコードで示される__serialize__unserializeマジックメソッドは、PHP 8で導入されたオブジェクトのシリアライズ・デシリアライズを詳細に制御する機能です。DateTimeオブジェクトのように内部状態が複雑なプロパティは、そのままシリアライズするのではなく、ISO 8601形式(DateTime::ATOM)などの普遍的な文字列に変換して保存すると、異なるシステムやPHPバージョン間でもタイムゾーン情報を含め安全かつ正確に復元できます。__unserializeメソッドでデータを受け取る際には、データの欠損や不正な値がないか常に検証する習慣が重要です。また、PHPのunserialize()関数は、信頼できない外部からの入力に対して利用するとPHPオブジェクトインジェクションなどのセキュリティリスクを伴うため、安易な使用を避け、厳重な入力検証やより安全なデータ形式の検討が必要です。

【PHP8.x】__serializeメソッドの使い方 | いっしー@Webエンジニア