Archivo de 'Uncategorized'

Singleton serializado en sesión de PHP

Llevo un par de días liado programando desde cero una pequeña tienda online (Sin pasarelas ni florituras). Entre los hitos que tenía para hoy, el más importante es crear el carrito de la compra. He buscado por el todo poderoso, pero todas las soluciones que veía me parecían ineficientes o feas. No quería almacenar el carrito de forma “permanente” en la base de datos, de modo que lo lógico me parecía usar la memoria de las sesiones. Evidentemente el carrito debía ser un singleton, pero ¿cómo hacer que se serialice automáticamente en memoria?

Echemos un primer vistazo a un singleton en PHP:

class Example
{
    // Hold an instance of the class
    private static $instance;

    // A private constructor; prevents direct creation of object
    private function __construct()
    {
        echo 'I am constructed';
    }

    // The singleton method
    public static function singleton()
    {
        if (!isset(self::$instance)) {
            $c = __CLASS__;
            self::$instance = new $c;
        }

        return self::$instance;
    }

    // Example method
    public function bark()
    {
        echo 'Woof!';
    }

    // Prevent users to clone the instance
    public function __clone()
    {
        trigger_error('Clone is not allowed.', E_USER_ERROR);
    }

}

Sencillo, verdad? :) Pero este es el esqueleto de un singleton básico. Se declara como privado el constructor, de forma que si necesitas una instancia del objeto debas hacerlo necesariamente por el método estático Example::singleton(). Este método construye una nueva instancia, o bien devuelve la que ya había (Almacenada en el atributo estático $instance). Sin embargo la clase que yo necesito debe cargarse por defecto de memoria, y almacenar el objeto de nuevo cuando éste se destruya. La solución aquí:

class ShoppingCart
{

    private $_items;
    private static $_instance;
    private static $_namespace = "spp";

    private function __construct()
    {
        $this->_items = Array();
    }

    public function __destruct()
    {
        $shopping = new Zend_Session_Namespace(self::$_namespace);
        $shopping->object = serialize(self::$_instance);
    }

    public function __clone() {
        throw new Exception("Cannot clone singleton!");
    }

    public static function singleton()
    {
        $shopping = new Zend_Session_Namespace(self::$_namespace);

        if (!isset(self::$_instance)) {
            if(isset($shopping->object)) {
                self::$_instance = unserialize($shopping->object);
            } else {
                $c = __CLASS__;
                self::$_instance = new $c;
            }
        }

        return self::$_instance;
    }

    public function addItem($id, $quantity)
    {
        // Código que añade un nuevo item
    }

    public function removeItem($id)
    {
        // Código que elimina un item
    }

    // Resto de funciones del carrito de la compra
}

Básicamente funciona igual, excepto que antes de crear un nuevo objeto, comprueba si hay alguno en el espacio de nombres de memoria asignado en el atributo estático $_namespace. Igualmente, cuando el objeto se destruye automáticamente por el recolector de basura, se serializa para guardarse la nueva versión en memoria.

Es importante darse cuenta de esto último: El método __destruct() no funcionará si ya hemos finalizado la sesión del usuario mediante session_write_close(). Por esa razón, en algunos casos tendremos que llamar explícitamente al destructor antes de que se cierre la sesión. Resulta que el método para redirigir a otra página Zend_Controller_Action::_redirect($url) cierra la sesión. Me he vuelto loco hasta que he averiguado la razón. Se corrige simplemente llamando a ShoppingCart::__destructor() antes de hacer la redirección.

Pero es que yo no te critico a ti

Pero es que yo no te critico a ti“, estoy cansado de que en debates públicos la gente argumente eso. Como si la no-crítica fuera signo de tener más razón. Muchas veces en un debate se enfrentan dos posturas:

  1. El individuo ‘A’ dice: Lo que yo hago aparentemente es correcto. En cambio tu postura es perjudicial.
  2. El individuo ‘B’ argumenta: Tú haz lo que te parezca bien a ti y yo lo que me parezca bien a mi.

Veamos algunos ejemplos…

Asunto Defensa
Creacionismo Yo respeto tu teoría, respeta tú la mia y deja que se enseñe en las aulas también el creacionismo.
Maltrato animal Yo respeto tus gustos, respeta tú los mios y deja que continúe la “fiesta nacional”.
Consumismo Yo respeto tu forma de vida, respeta tú la mia y deja que consuma lo que me de la gana.
Sexismo en las aulas Yo respeto tus métodos de enseñanza, respeta tú los mios y deja que mi escuela siga separando en aulas de chicos y de chicas.

Analizando el problema, vemos que en todos estos casos hay un individuo que se siente atacado e intenta justificar sus actos alegando libertad de elección. Pero es que, si tu libertad de decisión perjudica a la sociedad en conjunto, o va en favor de los gustos/ideas/intereses de sólo unos pocos, entonces muy posiblemente no sean aceptables (Aunque sean legales).

Por ejemplo, los defensores del creacionismo dicen que no se puede demostrar que su teoría es falsa. Nos ha jodido, tampoco se puede demostrar que Terelu exista (Quizá es un implante en nuestra psique), ni que sólo somos personajes de un enorme juego de rol. La demostración corre a cargo del que defiende la teoría, del mismo modo que los defensores de la evolución lo justificamos con el método científico. Llegados a este punto en el que queda claro quién actúa en favor de la sociedad, comienza el lamento y lloriqueo de los defensores de lo indefendible.

Ellos lo saben. Los argumentos que van contra sus ideas les dejan desnudos, y en un último intento por defender lo indefendible argumentan ese triste “Pero es que yo a ti no te critico“. Pues claro que no me criticas, como que no puedes.

Y es que, aunque lo irracional forme parte del ser humano, no debe formar parte de la sociedad. Por eso no tenemos porqué aceptar vuestras ideas.

Disclaimer: Como dijo Álex hace poco, Esto ha sido un vómito de ideas sin masticar demasiado y probablemente alguien me rebata algo y piense “joder, pues tienes razón.