itsgoingd / clockwork

Clockwork - php dev tools in your browser - server-side component
https://underground.works/clockwork
MIT License
5.69k stars 321 forks source link

Serialization Failure in Clockwork Due to Incomplete Object Representations #695

Closed hamedghaderi closed 7 months ago

hamedghaderi commented 8 months ago

We have encountered an issue with Clockwork where serialization fails under certain conditions. This problem appears to stem from incomplete object representations during operations that require serialization.

UlrichEckhardt commented 8 months ago

Looking at the PR, it seems to be a harmless change. Thinking about maintainability though, I'm wondering how you came across that error to begin with. What even is __PHP_Incomplete_Class? The info I found was almost none. Can you perhaps describe how to reproduce this?

hamedghaderi commented 8 months ago

@UlrichEckhardt,

__PHP_Incomplete_Class: this is a special PHP object that is used when an attempt is made to unserialize an object whose class definition is not available or not known at the time of unserialization. Essentially, it acts as a placeholder for the original object.

When PHP serializes objects, it saves the class name along with the object's properties. Upon unserialization, PHP attempts to recreate the object based on this information. If the class definition (i.e., the PHP code that defines the class) is not present or cannot be autoloaded, PHP cannot fully reconstruct the object. Instead, it returns an instance of __PHP_Incomplete_Class, which contains all the properties that were serialized but lacks the methods and class-specific behavior since PHP does not know what the original class was supposed to be.

The occurrence of the __PHP_Incomplete_Class error in our application stems from our multi-step model construction process. In this process, we incrementally validate and assemble different segments of a PHP class, with each validation step contributing to the formation of the model. To facilitate user progress through these steps, we temporarily store the partially completed model in the session.

This approach allows us to preserve and reuse the already validated segments of the model, ensuring that the user does not need to re-enter data upon encountering a validation error. It is only upon reaching the final step that we compile the fully qualified PHP class and persist it to the database.

hamedghaderi commented 8 months ago

@UlrichEckhardt To add clarity to our approach, let's consider a simplified example similar to our implementation. In our application, to persist user progress and validated data between these steps, we employ session storage:

function saveModelToSession($model) {
    $sessionData = $this->session->get('model_data');
    $modelClass = get_class($model);

    if (!isset($sessionData[$modelClass])) {
        $sessionData[$modelClass] = [];
    }

    $sessionData[$modelClass][$model->id] = $model;
   $this->session->set('model_data', $sessionData);
}
itsgoingd commented 8 months ago

Hey, thanks for investigating this and making a PR.

To reproduce this you can simply call unserialize() with a non-existent serialized class:

unserialize('O:3:"Foo":0:{}'); // __PHP_Incomplete_Class(Foo)
itsgoingd commented 7 months ago

Released Clockwork 5.2.1, including this fix.