Summary
Method Name | __sleep() |
Type | Magic Method |
Access Modifier | public |
Parameters | None |
Return Type | array |
Purpose/Usage | Returns an array of the property names of the class which should be included when an object of that class is serialized |
When Invoked | Automatically invoked by the serialize() method. |
Limitations | Can not serialize resource. |
Signature
public function __sleep(): array
NOTE
The __sleep() magic method must be declared as public.
If not declared as public, then PHP will ignore it, and serialize all properties(as default behavior).
Usage
The __sleep() method returns an array of names of the variables of an object, which should be kept when the object is serialized.
We use the serialize() method to generate a string that represents the object that is provided by the serialize() method.
The __sleep() magic method is related to the serialize() method, and is called before the serialization process automatically.
NOTE
The __sleep() method returns an array of strings.
The strings in the array are the names of the variables/properties of the class.
Without the __sleep() method, the serialized data of an object will keep all the attributes. The __sleep() is used to restrict any attribute to the serialized object.
NOTE
If there is no __sleep() method present in the class, then all the attributes will be included in the serialized value.
The serialize() method is called in our code; it uses(triggers) the __sleep() magic method if available in the object.
NOTE
__wakeup() magic method is related to sleep.
__sleep() is called when we serialize, and __wakeup() is called when we unserialize.
Properties with all access modifiers(public, protected, private) can be included in the returned array of the __sleep() method. This allows us to control exactly which properties are included in the serialized object.
WARNING
Resource types, like file handler, database connection, etc. can not be serialized.
If the property of resource type is included in the returned array of the __sleep() method, PHP will emit a warning and set the property to null.
Use Cases
Let’s define a class for analyzing the case for __sleep().
__sleep() in Action
Let’s take a look at the usage of the __sleep() magic method.
Step #1: Class Definition
First, define a class. This class definition does not have anything special. Just define it according to your requirement.
Here we have-
NOTE
There are some additional methods implemented in the class. You can ignore the details of the implementation for now.
As the implementation details is not important for understanding the __sleep() magic method usage.
Also, you can use any class for this. There is nothing special in the DatabaseHandler class.
<?php
class DatabaseHandler
{
public function __construct(
private string $dbHost,
private string $dbName,
private string $dbUser,
private string $dbPass
) {}
public function connect(): PDO
{
$dsn = "mysql:host={$this->dbHost};dbname={$this->dbName}";
try {
$pdo = new PDO($dsn, $this->dbUser, $this->dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
throw new Exception("Connection failed: " . $e->getMessage());
}
}
public function executeQuery(string $query, array $params = []): array
{
$pdo = $this->connect();
$stmt = $pdo->prepare($query);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
// Usage of DatabaseHandler
$dbHandler = new DatabaseHandler('localhost', 'test_db', 'root', 'bigboxsecret');
try {
$results = $dbHandler->executeQuery("SELECT * FROM users WHERE age > :age", ['age' => 18]);
} catch (Exception $e) {
// echo "Error: " . $e->getMessage();
}
PHPStep #2: Serialize and Unserialize
NOTE
By default, all the properties of the object are included in the serialized object.
$serializeDbHandler = serialize($dbHandler);
echo "Serialized DatabaseHandler: " . PHP_EOL;
var_dump($serializeDbHandler);
$unserializedDbHandler = unserialize($serializeDbHandler);
echo "Unserialized DatabaseHandler: " . PHP_EOL;
var_dump($unserializedDbHandler);
PHPOutput:
You can see all the object properties in the serialized value, and also in the unserialized object.
Serialized DatabaseHandler:
string(199) "
O:15:"DatabaseHandler":4:{
s:23:"DatabaseHandlerdbHost"; s:9:"localhost";
s:23:"DatabaseHandlerdbName"; s:7:"test_db";
s:23:"DatabaseHandlerdbUser"; s:4:"root";
s:23:"DatabaseHandlerdbPass"; s:12:"bigboxsecret";
}
"
Unserialized DatabaseHandler:
object(DatabaseHandler)#3 (4) {
["dbHost":"DatabaseHandler":private]=>
string(9) "localhost"
["dbName":"DatabaseHandler":private]=>
string(7) "test_db"
["dbUser":"DatabaseHandler":private]=>
string(4) "root"
["dbPass":"DatabaseHandler":private]=>
string(12) "bigboxsecret"
}
Step #3: Add __sleep() to Class
<?php
class DatabaseHandler
{
public function __construct(
private string $dbHost,
private string $dbName,
private string $dbUser,
private string $dbPass,
) {}
public function connect(): PDO
{
$dsn = "mysql:host={$this->dbHost};dbname={$this->dbName}";
try {
$pdo = new PDO($dsn, $this->dbUser, $this->dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
throw new Exception("Connection failed: " . $e->getMessage());
}
}
public function executeQuery(string $query, array $params = []): array
{
$pdo = $this->connect();
$stmt = $pdo->prepare($query);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function __sleep()
{
echo 'In __sleep()' . PHP_EOL;
return [
'dbHost',
'dbName',
'dbUser',
];
}
}
// Example usage
$dbHandler = new DatabaseHandler('localhost', 'test_db', 'root', '');
try {
$results = $dbHandler->executeQuery("SELECT * FROM users WHERE age > :age", ['age' => 18]);
} catch (Exception $e) {
// echo "Error: " . $e->getMessage();
}
$serializeDbHandler = serialize($dbHandler);
echo "Serialized DatabaseHandler: " . PHP_EOL;
var_dump($serializeDbHandler);
$unserializedDbHandler = unserialize($serializeDbHandler);
echo "Unserialized DatabaseHandler: " . PHP_EOL;
var_dump($unserializedDbHandler);
PHPOutput:
If you check the serialized output, “dbPass” is not present in that. When unserialized, you can see “dbPass”, but it’s value is “uninitialized”.
Because, we are ignoring “dbPass”, by not including it in the returned value of __sleep() method.
In __sleep()
Serialized DatabaseHandler:
string(161) "
O:15:"DatabaseHandler":3:{
s:23:"DatabaseHandlerdbHost";
s:9:"localhost";
s:23:"DatabaseHandlerdbName";
s:7:"test_db";
s:23:"DatabaseHandlerdbUser";
s:4:"root";
}
"
Unserialized DatabaseHandler:
object(DatabaseHandler)#3 (3) {
["dbHost":"DatabaseHandler":private]=>
string(9) "localhost"
["dbName":"DatabaseHandler":private]=>
string(7) "test_db"
["dbUser":"DatabaseHandler":private]=>
string(4) "root"
["dbPass":"DatabaseHandler":private]=>
uninitialized(string)
}
__sleep() with Different Access Modifier
Let’s see how the __sleep(), and serialization and unserialization processes affect/handle properties with different access – public, protected, private, and also the readonly properties.
NOTE
The serialized value does not hold any significant identifier for the readonly properties. So from the serialized value, we can not figure out if a value is readonly or not.
When the object is unserialized, then that information is retrieved from the class definition.
Take a look at the following example-
<?php
class UserProfile
{
public string $username;
protected string $email;
private string $passwordHash;
public readonly string $createdAt;
public function __construct($username, $email, $password, $createdAt)
{
$this->username = $username;
$this->email = $email;
$this->passwordHash = password_hash($password, PASSWORD_DEFAULT);
$this->createdAt = $createdAt;
}
public function __sleep()
{
return ['username', 'email', 'passwordHash', 'createdAt'];
}
}
// Example usage
$user = new UserProfile(
"bigboxcode",
"webhkp@bigboxcode.com",
"secretbox",
date('Y-m-d H:i:s')
);
$serialized = serialize($user);
echo "Serialized:" . PHP_EOL;
var_dump($serialized);
$unserialized = unserialize($serialized);
echo "Unserialized:" . PHP_EOL;
var_dump($unserialized);
PHPOutput:
In the serialized value, you can see key name and value. Take a close look at the key names-
Serialized:
string(244) "
O:11:"UserProfile":4:{
s:8:"username";
s:10:"bigboxcode";
s:8:"*email";
s:21:"webhkp@bigboxcode.com";
s:25:"UserProfilepasswordHash";
s:60:"$2y$12$oPpgB5Ku.JWICVsQDVUJ5ufZbeQnrX/9TXeaDMXMbLyeRsZs9hPV2";
s:9:"createdAt";
s:19:"2025-07-12 07:51:18";
}
"
Unserialized:
object(UserProfile)#2 (4) {
["username"]=>
string(10) "bigboxcode"
["email":protected]=>
string(21) "webhkp@bigboxcode.com"
["passwordHash":"UserProfile":private]=>
string(60) "$2y$12$oPpgB5Ku.JWICVsQDVUJ5ufZbeQnrX/9TXeaDMXMbLyeRsZs9hPV2"
["createdAt"]=>
string(19) "2025-07-12 07:51:18"
}
__sleep() Method in Parent-Child Relation
Now, let’s take a look how different property behaves for an object of a child class.
Case #1: Access Public Property of Parent
Public properties of the parent are accessible by default. Just add the name of the property to the list.
Here we have a public property “$identifier” in the class “ResourceHandler”, and “DatabaseHandler” is extending it.
Add the property name “identifier” in the returned array of __sleep() of the “DatabaseHandler”.
<?php
class ResourceHander
{
public string $identifier;
public function __construct(string $identifier)
{
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
$this->identifier = $identifier;
}
}
class DatabaseHandler extends ResourceHander
{
public function __construct(
private string $dbHost,
private string $dbName,
private string $dbUser,
private string $dbPass,
) {
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
parent::__construct(__CLASS__ . "_" . uniqid());
}
public function connect(): PDO
{
$dsn = "mysql:host={$this->dbHost};dbname={$this->dbName}";
try {
$pdo = new PDO($dsn, $this->dbUser, $this->dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
throw new Exception("Connection failed: " . $e->getMessage());
}
}
public function executeQuery(string $query, array $params = []): array
{
$pdo = $this->connect();
$stmt = $pdo->prepare($query);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function __sleep()
{
echo 'In __sleep()' . PHP_EOL;
return [
'identifier',
'dbHost',
'dbName',
'dbUser',
];
}
}
// Example usage
$dbHandler = new DatabaseHandler('localhost', 'test_db', 'root', '');
try {
$results = $dbHandler->executeQuery("SELECT * FROM users WHERE age > :age", ['age' => 18]);
} catch (Exception $e) {
// echo "Error: " . $e->getMessage();
}
$serializeDbHandler = serialize($dbHandler);
echo "Serialized DatabaseHandler: " . PHP_EOL;
var_dump($serializeDbHandler);
$unserializedDbHandler = unserialize($serializeDbHandler);
echo "Unserialized DatabaseHandler: " . PHP_EOL;
var_dump($unserializedDbHandler);
PHPOutput:
In __construct() inside DatabaseHandler
In __construct() inside ResourceHander
In __sleep()
Serialized DatabaseHandler:
string(216) "
O:15:"DatabaseHandler":4 {
s:10:"identifier";
s:29:"DatabaseHandler_686a3978f406d";
s:23:"DatabaseHandlerdbHost";
s:9:"localhost";
s:23:"DatabaseHandlerdbName";
s:7:"test_db";
s:23:"DatabaseHandlerdbUser";
s:4:"root";
}
"
Unserialized DatabaseHandler:
object(DatabaseHandler)#3 (4) {
["identifier"]=>
string(29) "DatabaseHandler_686a3978f406d"
["dbHost":"DatabaseHandler":private]=>
string(9) "localhost"
["dbName":"DatabaseHandler":private]=>
string(7) "test_db"
["dbUser":"DatabaseHandler":private]=>
string(4) "root"
["dbPass":"DatabaseHandler":private]=>
uninitialized(string)
}
Case #2: Access Protected Property of Parent
Protected properties can also be accessed directly. Just add the property name to the returned list of __sleep() method.
Here the “$metadata” property is protected. Now add “metdata” to the returned array of __sleep(), and we can see the ‘metadata’ in the serialized and unserialized values.
<?php
class ResourceHander
{
public string $identifier;
protected array $metadata = [];
public function __construct(string $identifier)
{
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
$this->identifier = $identifier;
}
public function setMetadata(string $key, mixed $value): void {
$this->metadata[$key] = $value;
}
public function getMetadata(string $key): mixed {
return $this->metadata[$key] ?? null;
}
}
class DatabaseHandler extends ResourceHander
{
public function __construct(
private string $dbHost,
private string $dbName,
private string $dbUser,
private string $dbPass,
) {
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
parent::__construct(__CLASS__ . "_" . uniqid());
}
public function connect(): PDO
{
$dsn = "mysql:host={$this->dbHost};dbname={$this->dbName}";
try {
$pdo = new PDO($dsn, $this->dbUser, $this->dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
throw new Exception("Connection failed: " . $e->getMessage());
}
}
public function executeQuery(string $query, array $params = []): array
{
$pdo = $this->connect();
$stmt = $pdo->prepare($query);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function __sleep()
{
echo 'In __sleep()' . PHP_EOL;
return [
'identifier',
'metadata',
'dbHost',
'dbName',
'dbUser',
];
}
}
// Example usage
$dbHandler = new DatabaseHandler('localhost', 'test_db', 'root', '');
$dbHandler->setMetadata('description', 'Main database connection for user data');
$dbHandler->setMetadata('created_by', 'webhkp');
$dbHandler->setMetadata('created_at', date('Y-m-d H:i:s'));
$dbHandler->setMetadata('tags', ['production', 'mysql', 'critical']);
$dbHandler->setMetadata('version', '1.0.3');
try {
$results = $dbHandler->executeQuery("SELECT * FROM users WHERE age > :age", ['age' => 18]);
} catch (Exception $e) {
// echo "Error: " . $e->getMessage();
}
$serializeDbHandler = serialize($dbHandler);
echo "Serialized DatabaseHandler: " . PHP_EOL;
var_dump($serializeDbHandler);
$unserializedDbHandler = unserialize($serializeDbHandler);
echo "Unserialized DatabaseHandler: " . PHP_EOL;
var_dump($unserializedDbHandler);
PHPOutput:
In __construct() inside DatabaseHandler
In __construct() inside ResourceHander
In __sleep()
Serialized DatabaseHandler:
string(482) "
O:15:"DatabaseHandler":5:{
s:10:"identifier";
s:29:"DatabaseHandler_6873ccf76f02c";
s:11:"*metadata";
a:5:{
s:11:"description";
s:38:"Main database connection for user data";
s:10:"created_by";s:6:"webhkp";
s:10:"created_at";
s:19:"2025-07-13 15:12:55";
s:4:"tags";
a:3:{
i:0;
s:10:"production";
i:1;s:5:"mysql";
i:2;
s:8:"critical";
}
s:7:"version";
s:5:"1.0.3";
}
s:23:"DatabaseHandlerdbHost";
s:9:"localhost";
s:23:"DatabaseHandlerdbName";
s:7:"test_db";
s:23:"DatabaseHandlerdbUser";
s:4:"root";
}
"
Unserialized DatabaseHandler:
object(DatabaseHandler)#3 (5) {
["identifier"]=>
string(29) "DatabaseHandler_6873ccf76f02c"
["metadata":protected]=>
array(5) {
["description"]=>
string(38) "Main database connection for user data"
["created_by"]=>
string(6) "webhkp"
["created_at"]=>
string(19) "2025-07-13 15:12:55"
["tags"]=>
array(3) {
[0]=>
string(10) "production"
[1]=>
string(5) "mysql"
[2]=>
string(8) "critical"
}
["version"]=>
string(5) "1.0.3"
}
["dbHost":"DatabaseHandler":private]=>
string(9) "localhost"
["dbName":"DatabaseHandler":private]=>
string(7) "test_db"
["dbUser":"DatabaseHandler":private]=>
string(4) "root"
["dbPass":"DatabaseHandler":private]=>
uninitialized(string)
}
Case #3: Can’t Access Private Property of Parent Directly
The private properties can not be added normally.
Here we have private property $apiToken.
<?php
class ResourceHander
{
public string $identifier;
protected array $metadata = [];
private string $apiToken;
public function __construct(string $identifier, string $token)
{
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
$this->identifier = $identifier;
$this->apiToken = $token;
}
public function setMetadata(string $key, mixed $value): void {
$this->metadata[$key] = $value;
}
public function getMetadata(string $key): mixed {
return $this->metadata[$key] ?? null;
}
}
class DatabaseHandler extends ResourceHander
{
public function __construct(
private string $dbHost,
private string $dbName,
private string $dbUser,
private string $dbPass,
) {
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
parent::__construct(__CLASS__ . "_" . uniqid(), bin2hex(random_bytes(16)));
}
public function connect(): PDO
{
$dsn = "mysql:host={$this->dbHost};dbname={$this->dbName}";
try {
$pdo = new PDO($dsn, $this->dbUser, $this->dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
throw new Exception("Connection failed: " . $e->getMessage());
}
}
public function executeQuery(string $query, array $params = []): array
{
$pdo = $this->connect();
$stmt = $pdo->prepare($query);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function __sleep()
{
echo 'In __sleep()' . PHP_EOL;
return [
'identifier',
'metadata',
'apiToken',
'dbHost',
'dbName',
'dbUser',
];
}
}
// Example usage
$dbHandler = new DatabaseHandler('localhost', 'test_db', 'root', '');
// $dbHandler->setMetadata('description', 'Main database connection for user data');
// $dbHandler->setMetadata('created_by', 'webhkp');
// $dbHandler->setMetadata('created_at', date('Y-m-d H:i:s'));
// $dbHandler->setMetadata('tags', ['production', 'mysql', 'critical']);
// $dbHandler->setMetadata('version', '1.0.3');
try {
$results = $dbHandler->executeQuery("SELECT * FROM users WHERE age > :age", ['age' => 18]);
} catch (Exception $e) {
// echo "Error: " . $e->getMessage();
}
$serializeDbHandler = serialize($dbHandler);
echo "Serialized DatabaseHandler: " . PHP_EOL;
var_dump($serializeDbHandler);
$unserializedDbHandler = unserialize($serializeDbHandler);
echo "Unserialized DatabaseHandler: " . PHP_EOL;
var_dump($unserializedDbHandler);
PHPOutput:
In __construct() inside DatabaseHandler
In __construct() inside ResourceHander
In __sleep()
PHP Warning: serialize(): "apiToken" returned as member variable from __sleep() but does not exist in db_serialize.php on line 93
Serialized DatabaseHandler:
string(241) "
O:15:"DatabaseHandler":5:{
s:10:"identifier";
s:29:"DatabaseHandler_6873d575ef1ad";
s:11:"*metadata";
a:0:{}
s:23:"DatabaseHandlerdbHost";
s:9:"localhost";
s:23:"DatabaseHandlerdbName";
s:7:"test_db";
s:23:"DatabaseHandlerdbUser";
s:4:"root";
}
"
Unserialized DatabaseHandler:
object(DatabaseHandler)#3 (5) {
["identifier"]=>
string(29) "DatabaseHandler_6873d575ef1ad"
["metadata":protected]=>
array(0) {
}
["apiToken":"ResourceHander":private]=>
uninitialized(string)
["dbHost":"DatabaseHandler":private]=>
string(9) "localhost"
["dbName":"DatabaseHandler":private]=>
string(7) "test_db"
["dbUser":"DatabaseHandler":private]=>
string(4) "root"
["dbPass":"DatabaseHandler":private]=>
uninitialized(string)
}
Case #4: Use Getter and Setter to Access Private Property of Parent
<?php
class ResourceHander
{
public string $identifier;
protected array $metadata = [];
private string $apiToken {
get {
return $this->apiToken;
}
set {
if (strlen($value) === 0) {
throw new ValueError("API token must be non-empty");
}
$this->apiToken = $value;
}
}
public function __construct(string $identifier)
{
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
$this->identifier = $identifier;
}
public function setMetadata(string $key, mixed $value): void {
$this->metadata[$key] = $value;
}
public function getMetadata(string $key): mixed {
return $this->metadata[$key] ?? null;
}
}
class DatabaseHandler extends ResourceHander
{
public function __construct(
private string $dbHost,
private string $dbName,
private string $dbUser,
private string $dbPass,
) {
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
parent::__construct(__CLASS__ . "_" . uniqid());
}
public function connect(): PDO
{
$dsn = "mysql:host={$this->dbHost};dbname={$this->dbName}";
try {
$pdo = new PDO($dsn, $this->dbUser, $this->dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
throw new Exception("Connection failed: " . $e->getMessage());
}
}
public function executeQuery(string $query, array $params = []): array
{
$pdo = $this->connect();
$stmt = $pdo->prepare($query);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function __sleep()
{
echo 'In __sleep()' . PHP_EOL;
return [
'identifier',
'metadata',
'apiToken',
'dbHost',
'dbName',
'dbUser',
];
}
}
// Example usage
$dbHandler = new DatabaseHandler('localhost', 'test_db', 'root', '');
// $dbHandler->setMetadata('description', 'Main database connection for user data');
// $dbHandler->setMetadata('created_by', 'webhkp');
// $dbHandler->setMetadata('created_at', date('Y-m-d H:i:s'));
// $dbHandler->setMetadata('tags', ['production', 'mysql', 'critical']);
// $dbHandler->setMetadata('version', '1.0.3');
$dbHandler->apiToken = bin2hex('1234567890');
try {
$results = $dbHandler->executeQuery("SELECT * FROM users WHERE age > :age", ['age' => 18]);
} catch (Exception $e) {
// echo "Error: " . $e->getMessage();
}
$serializeDbHandler = serialize($dbHandler);
echo "Serialized DatabaseHandler: " . PHP_EOL;
var_dump($serializeDbHandler);
$unserializedDbHandler = unserialize($serializeDbHandler);
echo "Unserialized DatabaseHandler: " . PHP_EOL;
var_dump($unserializedDbHandler);
PHPOutput:
In __construct() inside DatabaseHandler
In __construct() inside ResourceHander
In __sleep()
Serialized DatabaseHandler:
string(284) "
O:15:"DatabaseHandler":6:{
s:10:"identifier";
s:29:"DatabaseHandler_68742b8706114";
s:11:"*metadata";
a:0:{}
s:8:"apiToken";
s:20:"31323334353637383930";
s:23:"DatabaseHandlerdbHost";
s:9:"localhost";
s:23:"DatabaseHandlerdbName";
s:7:"test_db";
s:23:"DatabaseHandlerdbUser";
s:4:"root";
}
"
Unserialized DatabaseHandler:
object(DatabaseHandler)#3 (6) {
["identifier"]=>
string(29) "DatabaseHandler_68742b8706114"
["metadata":protected]=>
array(0) {
}
["apiToken":"ResourceHander":private]=>
string(20) "31323334353637383930"
["dbHost":"DatabaseHandler":private]=>
string(9) "localhost"
["dbName":"DatabaseHandler":private]=>
string(7) "test_db"
["dbUser":"DatabaseHandler":private]=>
string(4) "root"
["dbPass":"DatabaseHandler":private]=>
uninitialized(string)
}
Case #4: Use Mangled Name to Access Private Property of Parent
Name manginling is the process which is used by PHP, to internally encode private and protected property names while serializing the object.
Here is how the properties with different visibility are represented internally in a PHP class, during serialization-
Visibility | Internal Serialized Name | Note |
---|---|---|
Public | “property“ | |
Protected | “\0*\0property“ | Mangled name for protected property |
Private | “\0ClassName\0property‘ | Mangled name for private property |
NOTE
The \0 is an escape sequence(Null Byte). It is a non-printable control character.
<?php
class ResourceHandler
{
public string $identifier;
private string $apiToken;
public function __construct(string $identifier, string $token)
{
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
$this->identifier = $identifier;
$this->apiToken = $token;
}
}
class DatabaseHandler extends ResourceHandler
{
public function __construct(
private string $dbHost,
private string $dbName,
private string $dbUser,
private string $dbPass,
) {
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
parent::__construct(__CLASS__ . "_" . uniqid(), bin2hex(random_bytes(16)));
}
public function connect(): PDO
{
$dsn = "mysql:host={$this->dbHost};dbname={$this->dbName}";
try {
$pdo = new PDO($dsn, $this->dbUser, $this->dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
throw new Exception("Connection failed: " . $e->getMessage());
}
}
public function executeQuery(string $query, array $params = []): array
{
$pdo = $this->connect();
$stmt = $pdo->prepare($query);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function __sleep()
{
echo 'In __sleep()' . PHP_EOL;
return [
'identifier',
"\0ResourceHandler\0apiToken",
'dbHost',
'dbName',
'dbUser',
// 'unknownVar',
];
}
}
// Example usage
$dbHandler = new DatabaseHandler('localhost', 'test_db', 'root', 'passs987654321');
// $dbHandler->apiToken = bin2hex('1234567890');
try {
$results = $dbHandler->executeQuery("SELECT * FROM users WHERE age > :age", ['age' => 18]);
} catch (Exception $e) {
// echo "Error: " . $e->getMessage();
}
$serializeDbHandler = serialize($dbHandler);
echo "Serialized DatabaseHandler: " . PHP_EOL;
var_dump($serializeDbHandler);
$unserializedDbHandler = unserialize($serializeDbHandler);
echo "Unserialized DatabaseHandler: " . PHP_EOL;
var_dump($unserializedDbHandler);
PHPOutput:
In __construct() inside DatabaseHandler
In __construct() inside ResourceHandler
In __sleep()
Serialized DatabaseHandler:
string(289) "
O:15:"DatabaseHandler":5:{
s:10:"identifier";
s:29:"DatabaseHandler_686ab2f91541e";
s:25:"ResourceHandlerapiToken";
s:32:"723220f6f38cb6805e1d5b4f1574e983";
s:23:"DatabaseHandlerdbHost";
s:9:"localhost";
s:23:"DatabaseHandlerdbName";
s:7:"test_db";
s:23:"DatabaseHandlerdbUser";
s:4:"root";
}
"
Unserialized DatabaseHandler:
object(DatabaseHandler)#3 (5) {
["identifier"]=>
string(29) "DatabaseHandler_686ab2f91541e"
["apiToken":"ResourceHandler":private]=>
string(32) "723220f6f38cb6805e1d5b4f1574e983"
["dbHost":"DatabaseHandler":private]=>
string(9) "localhost"
["dbName":"DatabaseHandler":private]=>
string(7) "test_db"
["dbUser":"DatabaseHandler":private]=>
string(4) "root"
["dbPass":"DatabaseHandler":private]=>
uninitialized(string)
}
Edge Cases
Case #1:
WARNING
If the __sleep() method does not return anything, or returns null, any value other than an array, then PHP issues an E_WARNING-
PHP Warning: serialize(): DatabaseHandler::__sleep() should return an array only containing the names of instance-variables to serialize in db_serialize.php on line 58
<?php
class DatabaseHandler
{
public function __construct(
private string $dbHost,
private string $dbName,
private string $dbUser,
private string $dbPass,
) {
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
}
public function connect(): PDO
{
$dsn = "mysql:host={$this->dbHost};dbname={$this->dbName}";
try {
$pdo = new PDO($dsn, $this->dbUser, $this->dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
throw new Exception("Connection failed: " . $e->getMessage());
}
}
public function executeQuery(string $query, array $params = []): array
{
$pdo = $this->connect();
$stmt = $pdo->prepare($query);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function __sleep()
{
echo 'In __sleep()' . PHP_EOL;
// Same thing happens if there is no return statement
// or the returned value is not an array
return null;
}
}
// Example usage
$dbHandler = new DatabaseHandler('localhost', 'test_db', 'root', '');
try {
$results = $dbHandler->executeQuery("SELECT * FROM users WHERE age > :age", ['age' => 18]);
} catch (Exception $e) {
// echo "Error: " . $e->getMessage();
}
$serializeDbHandler = serialize($dbHandler);
echo "Serialized DatabaseHandler: " . PHP_EOL;
var_dump($serializeDbHandler);
$unserializedDbHandler = unserialize($serializeDbHandler);
echo "Unserialized DatabaseHandler: " . PHP_EOL;
var_dump($unserializedDbHandler);
PHPOutput:
In __construct() inside DatabaseHandler
In __sleep()
PHP Warning: serialize(): DatabaseHandler::__sleep() should return an array only containing the names of instance-variables to serialize in db_serialize.php on line 59
Serialized DatabaseHandler:
string(2) "N;"
Unserialized DatabaseHandler:
NULL
Case #2:
WARNING
If the returned array from the __sleep() method contains a variable name that does not exist, then an E_WARNING is issued by PHP –
PHP Warning: serialize(): "someUnknownVar" returned as member variable from __sleep() but does not exist in db_serialize.php on line 56
The object will still be serialized, but it will not contain the unknown variable provided by __sleep().
<?php
class DatabaseHandler
{
public function __construct(
private string $dbHost,
private string $dbName,
private string $dbUser,
private string $dbPass,
) {
echo 'In __construct() inside ' . __CLASS__ . PHP_EOL;
}
public function connect(): PDO
{
$dsn = "mysql:host={$this->dbHost};dbname={$this->dbName}";
try {
$pdo = new PDO($dsn, $this->dbUser, $this->dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
throw new Exception("Connection failed: " . $e->getMessage());
}
}
public function executeQuery(string $query, array $params = []): array
{
$pdo = $this->connect();
$stmt = $pdo->prepare($query);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function __sleep()
{
echo 'In __sleep()' . PHP_EOL;
return [
'dbHost',
'dbName',
'dbUser',
'unknownVar',
];
}
}
// Example usage
$dbHandler = new DatabaseHandler('localhost', 'test_db', 'root', '');
try {
$results = $dbHandler->executeQuery("SELECT * FROM users WHERE age > :age", ['age' => 18]);
} catch (Exception $e) {
// echo "Error: " . $e->getMessage();
}
$serializeDbHandler = serialize($dbHandler);
echo "Serialized DatabaseHandler: " . PHP_EOL;
var_dump($serializeDbHandler);
$unserializedDbHandler = unserialize($serializeDbHandler);
echo "Unserialized DatabaseHandler: " . PHP_EOL;
var_dump($unserializedDbHandler);
PHPOutput:
In __construct() inside DatabaseHandler
In __sleep()
PHP Warning: serialize(): "unknownVar" returned as member variable from __sleep() but does not exist in db_serialize.php on line 59
Serialized DatabaseHandler:
string(161) "
O:15:"DatabaseHandler":3:{
s:23:"DatabaseHandlerdbHost";
s:9:"localhost";
s:23:"DatabaseHandlerdbName";
s:7:"test_db";
s:23:"DatabaseHandlerdbUser";
s:4:"root";
}
"
Unserialized DatabaseHandler:
object(DatabaseHandler)#3 (3) {
["dbHost":"DatabaseHandler":private]=>
string(9) "localhost"
["dbName":"DatabaseHandler":private]=>
string(7) "test_db"
["dbUser":"DatabaseHandler":private]=>
string(4) "root"
["dbPass":"DatabaseHandler":private]=>
uninitialized(string)
}
Case #3: Resource Can Not Be Serialized
<?php
class FileHandler {
private string $filePath;
private $handle;
public function __construct(string $filePath) {
$this->filePath = $filePath;
$this->handle = fopen($filePath, 'w');
fwrite($this->handle, "File opened from file handler.\n");
}
public function writeData(string $message): void {
fwrite($this->handle, $message . "\n");
}
public function __sleep(): array {
echo "__sleep() called" . PHP_EOL;
return [
'filePath',
'handle'
];
}
}
// FileHandler Usage
$sampleFileHandler = new FileHandler('sample.txt');
$sampleFileHandler->writeData("Test message -" . time());
$serializeFileHandler = serialize($sampleFileHandler);
echo "Serialized serializeFileHandler: " . PHP_EOL;
var_dump($serializeFileHandler);
$unserializedFileHandler = unserialize($serializeFileHandler);
echo "Unserialized serializeFileHandler: " . PHP_EOL;
var_dump($unserializedFileHandler);
$unserializedFileHandler->writeData("Test message after unserialization -" . time());
PHPOutput:
__sleep() called
Serialized serializeFileHandler:
string(101) "
O:11:"FileHandler":2:{
s:21:"FileHandlerfilePath";
s:10:"sample.txt";
s:19:"FileHandlerhandle";
i:0;
}
"
Unserialized serializeFileHandler:
object(FileHandler)#2 (2) {
["filePath":"FileHandler":private]=>
string(10) "sample.txt"
["handle":"FileHandler":private]=>
int(0)
}
PHP Fatal error: Uncaught TypeError: fwrite(): Argument #1 ($stream) must be of type resource, int given in file_seialize.php:16
Stack trace:
#0 file_seialize.php(16): fwrite()
#1 file_seialize.php(47): FileHandler->writeData()
#2 {main}
thrown in file_seialize.php on line 16
The solution to this problem is to reinitiate the resource handler during the wakeup process-
<?php
class FileHandler {
private string $filePath;
private $handle;
public function __construct(string $filePath) {
$this->filePath = $filePath;
$this->handle = fopen($filePath, 'w');
fwrite($this->handle, "File opened from file handler.\n");
}
public function writeData(string $message): void {
fwrite($this->handle, $message . "\n");
}
public function __sleep(): array {
echo "__sleep() called" . PHP_EOL;
return [
'filePath',
'handle'
];
}
public function __wakeup(): void {
echo "__wakeup() called" . PHP_EOL;
$this->handle = fopen($this->filePath, 'a');
fwrite($this->handle, "Resource opened again in wakeup\n");
}
}
// FileHandler Usage
$sampleFileHandler = new FileHandler('sample.txt');
$sampleFileHandler->writeData("Test message -" . time());
$serializeFileHandler = serialize($sampleFileHandler);
echo "Serialized serializeFileHandler: " . PHP_EOL;
var_dump($serializeFileHandler);
$unserializedFileHandler = unserialize($serializeFileHandler);
echo "Unserialized serializeFileHandler: " . PHP_EOL;
var_dump($unserializedFileHandler);
$unserializedFileHandler->writeData("Test message after unserialization -" . time());
PHPOutput:
__sleep() called
Serialized serializeFileHandler:
string(101) "
O:11:"FileHandler":2:{
s:21:"FileHandlerfilePath";
s:10:"sample.txt";
s:19:"FileHandlerhandle";
i:0;
}
"
__wakeup() called
Unserialized serializeFileHandler:
object(FileHandler)#2 (2) {
["filePath":"FileHandler":private]=>
string(10) "sample.txt"
["handle":"FileHandler":private]=>
resource(6) of type (stream)
}