Exporter des propriétés privées
durant serialize()en contournant le bug #40412

Depuis que j'ai ajouté le support de ICanBoogie/DateTime au paquet ActiveRecord, je souhaitais que les propriétés de type datetime soient toujours des instances de ICanBoogie/DateTime. Parce que les dates venant de la base de données sont des chaines de caractères, je souhaitais que l'on puisse poser une chaine de caractère ou une instance de DateTime, mais qu'à la lecture on obtienne toujours une instance de ICanBoogie/DateTime, même pour les dates vides. Rien de bien compliqué puisque les instances d'ActiveRecord étendent Object et qu'il est donc très simple d'ajouter des getter et des setters.

Par exemple, pour la propriété created_at, il suffit d'ajouter le getter volatile_get_created_at et le setter volatile_set_created_at et de rendre la propriété privée. De cette façon le getter et le setter sont toujours appelés puisque la propriété n'est accessible que depuis la classe qui la définie. Il ne restait plus qu'à ajouter created_at au tableau renvoyé par __sleep pour que la propriété soit exportée lors de la sérialisation de l'objet. Tout allait pour le mieux jusqu'à ce ce que je créé une sous-classes et que j'essaie d'en sérialiser une instance, et là patatras :

Notice: serialize(): « created_at » returned as member variable from __sleep() but does not exist …

Damned, c'est tout pourrit !

Export problématique des propriété privées

Il semble que les propriétés privées d'une super classe ne peuvent pas être exportées. Le rapport de bug 40412, qui date déjà de 2007, en fait mention. Lorsque l'on cherche ce message d'erreur sur Google, il y a pas mal de retours, notamment Symphony2 et Doctrine. On conseille en général d'utiliser la visibilité protégée… mais cela ne fait pas du tout mon affaire parce que j'ai besoin de la visibilité privée pour que le getter et le setter correspondants soient appelés.

J'étais triste et quasiment prêt à saisir quelques commandes git pour revenir en arrière quand j'ai pensé à la méthode from() de ma classe Object. En effet, il y a quelques temps j'avais écris cette méthode pour me permettre de créer des instances de classes à la manière du mode FETCH_CLASS de PDO . Vous pouvez consulter mon article à ce sujet. Après tout, j'arrivais bien à créer des propriétés protégées ou privées en fabriquant moi-même la chaine de sérialisation, ce qui me permettait de créer n'importe qu'elle instance avec comme source un tableau associatif :

<?php

class A extends ICanBoogie\Object
{
    private $a;
}

$a = A::from(array('a' => "testing"));

C'est en consultant le code de la fonction et en revoyant le message « but does not exist » que j'ai retrouvé l'espoir.

Un nouvel espoir

Parce que le nom d'une propriété protégée est préfixée par "\x00*\x00" et que celui d'une propriété privée est préfixée par "\x00{$class}\x00", où {$class} est la classe définissant la propriété privée, peut-être que si je préfixe le nom de la propriété cela pourrait marcher… J'avais trouvé la solution !

Pour exporter une propriété privée, il convient de préfixer son nom par "\x00{$class}\x00".

La fonctionnalité de mes rêves

La fonctionnalité dont j'avais si chèrement rêvé était maintenant devenue réalité. J'étais heureux.

<?php

namespace Icybee\Modules\Sites;

use ICanBoogie\DateTime;

class Site extends \ICanBoogie\ActiveRecord
{
    // …

    public function __sleep()
    {
        return parent::__sleep() + array
        (
            'created_at' => "\x00" . __CLASS__ . "\x00created_at"
        );
    }

    // …

    private $created_at;

    /**
     * Returns the created time.
     *
     * @return \ICanBoogie\DateTime
     */
    protected function volatile_get_created_at()
    {
        $time = $this->created_at;

        if ($time instanceof DateTime)
        {
            return $time;
        }

        return $this->created_at = $time === null ? DateTime::none() : new DateTime($time'utc');
    }

    /**
     * Sets the created time.
     *
     * @param \DateTime|string $value
     */
    protected function volatile_set_created_at($value)
    {
        $this->created_at = $value;
    }

    // …
}

Laisser un commentaire

Pas de commentaire