PHP: __sleep() Magic Method

Summary

Method Name__sleep()
TypeMagic Method
Access Modifierpublic
ParametersNone
Return Typearray
Purpose/UsageReturns an array of the property names of the class
which should be included when an object of that class is serialized
When InvokedAutomatically invoked by the serialize() method.
LimitationsCan 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

Control what attributes should and should not be serialized, when the object is passed to the serialize() method.
Optimize the serialization process, by providing limited/minimal and necessary data, which is very helpful for large objects.
Clean up the object before serialization, to remove/reset sensitive and/or temporary data.
Can be used to avoid circular references of object, during serialization.
Skip the data that is not supposed to be serialized, like calculated values or cached values.

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-

Class DatabaseHandler to handle database operations.
There are properties $dbHost, $dbName, $dbUser, $dbPass.

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();
}
PHP

Step #2: Serialize and Unserialize

Serialize an object of the class, and check the serialized value. You will see all the properties of the objects are present in the serialized value.
Unserialize the serialized value, and we get back all the property values of the object.

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);
PHP

Output:

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

Add a public method named __sleep(), the return type of this method is ‘array’.
The return value of the method must be an array of strings, and the elements of the array are the names of the properties of the class.
We can include or exclude any object property in the returned list. Here we are including ‘dbHost’, ‘dbName’, ‘dbUser’, and excluding ‘dbPass’.
<?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);
PHP

Output:

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.

Public property: saved as plain property name. For example: s:8:”username”;s:10:”bigboxcode”;
Protected property: saved with an asterisk(*) before the property name. For example: s:8:”*email”;s:21:”webhkp@bigboxcode.com”;
Private property: saved with the class name prefixed. For example: s:25:”UserProfilepasswordHash”;s:60:”$2y$12$oPpgB5Ku.JWICVsQDVUJ5ufZbeQnrX/9TXeaDMXMbLyeRsZs9hPV2″;
Read-only property: does not have any significant identifier, saved as a normal property.

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);
PHP

Output:

In the serialized value, you can see key name and value. Take a close look at the key names-

username” has no change, as it is public.
*email” has an asterisk with the kay name, as it is protected.
UserProfilepasswordHash” represents the “passwordHash” property, but has the class name prefixed, as it is a private property.
createdAt” has no change in name, as it is just a readonly property.
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);
PHP

Output:

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);
PHP

Output:

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);
PHP

Output:

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);
PHP

Output:

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-

VisibilityInternal Serialized NameNote
Publicproperty
Protected“\0*\0propertyMangled name for protected property
Private“\0ClassName\0propertyMangled 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);
PHP

Output:

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);
PHP

Output:

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);
PHP

Output:

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());
PHP

Output:

__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-

Define a __wakeup() method in the class, with signature public function __wakeup().
In the __wakeup() method reinitiate/reassign the file file hander, fro the $filePath.
<?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());
PHP

Output:

__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)
}

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.