<?php
/**
 * Ce script contient la définition des classes 'filestorage' et 'base_storage'.
 *
 * @package framework_openmairie
 * @version SVN : $Id$
 */

/**
 *
 */
if (defined("PATH_OPENMAIRIE") !== true) {
    /**
     * @ignore
     */
    define("PATH_OPENMAIRIE", "");
}
require_once PATH_OPENMAIRIE."om_logger.class.php";

/**
 *
 */
define('OP_FAILURE', 'OP_FAILURE');

/**
 * Définition de la classe 'filestorage'.
 *
 * Cette classe est une classe d'abstraction de stockage de fichiers.
 * C'est cette classe qui est instanciée et utilisée par d'autres scripts pour
 * gérer la création, récupération, suppression de fichiers et ce peu importe
 * le stockage utilisé. Son objectif est d'instancier la classe de stockage
 * spécifique aussi appelée plugin de stockage correspondant au paramétrage
 * sélectionné. Cette classe de stockage spécifique hérite de la classe
 * 'base_storage' qui lui sert de modèle.
 */
class filestorage {

    /**
     * Cet attribut permet de stocker le type de stockage
     * cette valeur doit être remplie en fonction du paramétrage
     * présent dans le fichier dyn/filestorage.inc.php
     * @var null|string
     */
    var $type_storage = null;

    /**
     * Cet attribut permet de stocker le contenu du fichier de configuration
     * qui est présent dans le fichier dyn/filestorage.inc.php.
     * @var null|array
     */
    var $conf = null;

    /**
     * Cet attribut permet de stocker la ou les instances de système 
     * de stockage alternatif présent dans le fichier de configuration 
     * situé dans dyn/filestorage.inc.php.
     * @var null|array
     */
    var $alternate_storage = array();

    /**
     * Séparateur entre l'identifiant de l'alternate filestorage et celui du document
     * @var string
     */
    const ALTFS_SEP = '://';

    /**
     * Cette variable est l'objet renvoye par la connexion à la ressource de stockage
     * @var null|filestorage_base
     */
    var $storage;

    
    /**
     * Le constructeur instancie la classe de connecteur
     * envoyée par le paramétrage lors de l'instanciation et lui
     * transmettre le paramétrage en question.
     *
     * @param array $conf Tableau de configuration.
     */
    public function __construct($conf = array()) {
        // initialisation des attributs
        $this->type_storage = null;
        $this->storage = null;
        $this->conf = $conf;

        // recuperation du type de storage
        if (is_null($this->get_conf('storage')) !== true && $this->get_conf('storage') !== "") {
            $this->type_storage = $this->get_conf('storage');
        }

        // recuperation des donnees dependent du type de storage
        if (!is_null($this->type_storage)) {
            // Si l'url du fichier de la classe du storage a été renseigné
            if (is_null($this->get_conf('storage_path')) !== true && $this->get_conf('storage_path') !== "") {
                require_once $this->get_conf('storage_path');
                $class_name = $this->type_storage;
            } else {
                require_once PATH_OPENMAIRIE."om_filestorage_".
                $this->type_storage.".class.php";
                $class_name = 'filestorage_'.$this->type_storage;
            }
            $this->storage = new $class_name($this->get_conf());
        } else {
            // log le fait que le storage n'est pas bon
            logger::instance()->log(__CLASS__.' - '.__FUNCTION__.
                ' : Classe de sauvegarde de fichiers n\'etait pas cree',
                EXTRA_VERBOSE_MODE);
        }

        // Instanciation du filestorage pour enregistrement temporaire
        require_once PATH_OPENMAIRIE."om_filestorage_".
                    $this->get_conf(array("temporary", "storage")).".class.php";
        $class_name = 'filestorage_'.$this->get_conf(array("temporary", "storage"));
        $this->storage->temporary_storage = new $class_name($this->get_conf("temporary"));
    }


    /**
     * Le destructeur permet de détruire la ressource instanciée
     * dans le constructeur.
     */
    public function __destruct() {
        if (!is_null($this->storage)) {
            unset($this->storage);
        }
    }

    /**
     * Cette fonction retourne le répertoire qui stocke le fichier avec
     * les données brutes, et le fichier avec les métadonnées. Quand un
     * fichier qui sert comme lock est créé il est placé dans ce même
     * répertoire
     * @param string $uid L'identifiant du fichier
     * @return Le répertoire qui contient les fichiers. Si le chemin de
     * racine de sauvegarde des fichiers n'est pas set, on retourne null.
     */
    public function getPath($uid) {
        $storage = $this->storage;
        if (! empty($alternate_storage_key = $this->getAltFsKeyFromUIDPrefix($uid))) {
            $storage = $this->init_alternate_storage($alternate_storage_key);
            // Pour récupérer l'uid correct il faut enlever le préfixe
            $uid = $this->removePrefixFromUID($uid);
        }
        if (is_null($storage)) {
            return OP_FAILURE;
        }
        // appel à la resource de stockage pour stocker le fichier
        return $storage->getPath($uid);
    }

    /**
     * Cette fonction est un accesseur permettant de récupérer la configuration du filestorage ou une partie de celle-ci.
     * Il y a plusieurs étapes de vérifications :
     * Etape 1 :
     * Lorsque le paramètre est à null, on retourne la configuration complète
     * Etape 2 :
     * Si le paramètre est un string on vérifie que c'est une clé qui existe dans le tableau de conf et on 
     * retourne la valeur de cette clé.
     * Il est possible que cette fonction soit utilisée dans une fonction php comme array_keys, si l'entrée n'existe pas 
     * on doit retourner un tableau vide.
     * Sinon on retourne null
     * Etape 3 :
     * Si le paramètre est un tableau à deux entrées, cela veut dire que l'on cherche un résultat dans un tableau de tableau.
     * Si ce tableau de tableau existe alors on retourne la valeur associée, sinon on retourne null.
     *
     * @param string $uid L'identifiant du fichier
     * @return multitype le tableau de configuration complet ou la valeur d'une entrée du tableau de configuration 
     * ou la valeur d'un tableau de tableau ou null.
     */
    function get_conf($params = null) {
        if ($params === null) {
            return $this->conf;
        }
        if (is_string($params) === true
            && is_array($this->conf) === true) {
            //
            if (array_key_exists($params, $this->conf) === true) {
                return $this->conf[$params];
            } elseif ($params === "override_storage_matrix") {
                return array();
            }  elseif ($params === "alternate_storage") {
                return array();
            }
        }
        if (is_array($params) === true
            && is_array($this->conf) === true
            && count($params) === 2
            && array_key_exists($params[0], $this->conf) === true
            && is_array($this->conf[$params[0]]) === true
            && array_key_exists($params[1], $this->conf[$params[0]]) === true) {
            //
            return $this->conf[$params[0]][$params[1]];
        }
        return null;
    }

    /**
     * Cette fonction appelle la fonction de mémé nom de la
     * ressource de stockage pour sauvegarder un fichier
     * @param string $file_content Le contenu de fichier
     * @param mixed $metadata Les metadata du fichier à sauvegarder
     * @param string $mode origine des données (content/temporary/path)
     * @param string $file_object_type contient une chaîne de caractère avec le format suivant : table.champ
     * @return string Le résultat de création retourné par la ressource de
     * stockage. Si la classe de sauvegarde n'était pas instanciée, on
     * retourne OP_FAILURE.
     */
    public function create($file_content, $metadata, $mode = "from_content", $file_object_type = null) {
        $storage = $this->storage;
        $uid_prefixe = "";
        logger::instance()->log(__METHOD__.'() file_object_type: '.var_export($file_object_type, true), EXTRA_VERBOSE_MODE);
        logger::instance()->log(__METHOD__.'() override_storage_matrix: '.var_export($this->get_conf("override_storage_matrix"), true), EXTRA_VERBOSE_MODE);
        if ($file_object_type !== null
            && in_array($file_object_type, array_keys($this->get_conf("override_storage_matrix"))) === true) {
            //
            $alternate_storage_key = $this->get_conf(array("override_storage_matrix", $file_object_type));
            logger::instance()->log(__METHOD__.'() alternate_storage_key: '.var_export($alternate_storage_key, true), EXTRA_VERBOSE_MODE);
            $storage = $this->init_alternate_storage($alternate_storage_key);
            logger::instance()->log(__METHOD__.'() storage: '.var_export($storage, true), EXTRA_VERBOSE_MODE);
            $uid_prefixe = $alternate_storage_key.self::ALTFS_SEP;
            logger::instance()->log(__METHOD__.'() uid_prefixe: '.var_export($uid_prefixe, true), EXTRA_VERBOSE_MODE);
        }
        // on retourne une erreur si la ressource de stockage n'était pas trouvée
        if (is_null($storage)) {
            logger::instance()->log(__METHOD__.'() failure: empty storage', EXTRA_VERBOSE_MODE);
            return OP_FAILURE;
        }
        // appel à la resource de stockage pour stocker le fichier
        $ret = $storage->create($file_content, $metadata, $mode);
        if ($ret == OP_FAILURE) {
            return $ret;
        }
        $op = $uid_prefixe.$ret;
        logger::instance()->log(__METHOD__.'() op: '.var_export($op, true), EXTRA_VERBOSE_MODE);
        return $op;
    }

    /**
     * Cette fonction permet de créer une instance d'alternate storage
     * Si une instance existe déjà on la retourne
     * Sinon on retourne null
     * @param string $alternate_storage_key une chaîne de caractère correspondant à la clé de l'alternate storage 
     * @return une instance d'alternate storage ou null
     */
    function init_alternate_storage($alternate_storage_key) {

        if (isset($this->alternate_storage) === true
            && is_array($this->alternate_storage) === true
            && array_key_exists($alternate_storage_key, $this->alternate_storage) === true
            && $this->alternate_storage[$alternate_storage_key] !== null) {
            //
            return $this->alternate_storage[$alternate_storage_key];
        }
        // On récupère l'alternate storage
        $alternate_storage_conf = $this->get_conf(array("alternate_storage", $alternate_storage_key));
        logger::instance()->log(__METHOD__.'() alternate_storage_conf: '.var_export($alternate_storage_conf, true), EXTRA_VERBOSE_MODE);
        if (is_array($alternate_storage_conf) !== true
            || array_key_exists("storage", $alternate_storage_conf) !== true) {
            //
            logger::instance()->log(__METHOD__.'() failure: no alternate storage', EXTRA_VERBOSE_MODE);
            return null;
        }
        //
        if (array_key_exists("storage_path", $alternate_storage_conf) === true
            && file_exists($alternate_storage_conf["storage_path"]) === true) {
            //
            require_once $alternate_storage_conf["storage_path"];
            $class_name = $alternate_storage_conf["storage"];
        } elseif (file_exists(PATH_OPENMAIRIE."om_filestorage_".$alternate_storage_conf["storage"].".class.php") === true) {
            require_once PATH_OPENMAIRIE."om_filestorage_".$alternate_storage_conf["storage"].".class.php";
            $class_name = 'filestorage_'.$alternate_storage_conf["storage"];
        } else {
            logger::instance()->log(__METHOD__.'() failure: class file "'.$alternate_storage_conf["storage_path"].'" do not exists', EXTRA_VERBOSE_MODE);
            return null;
        }
        $this->alternate_storage[$alternate_storage_key] = new $class_name($alternate_storage_conf);
        $this->alternate_storage[$alternate_storage_key]->temporary_storage = $this->storage->temporary_storage;
        return $this->alternate_storage[$alternate_storage_key];
    }

    /**
     * Retire le préfixe de l'identifiant du document (pour le cas de l'aternate filestorage)
     * 
     * @param  string  $uid  L'identifiant (préfixé ou non)
     *
     * @return string  L'identifiant sans préfixe
     */
    protected function removePrefixFromUID(string $uid) {
        logger::instance()->log(__METHOD__.'() uid (before): '.var_export($uid, true), EXTRA_VERBOSE_MODE);
        if(strpos($uid, self::ALTFS_SEP) !== false) {
            list($prefixe, $rest) = explode(self::ALTFS_SEP, $uid, 2);
            $uid = $rest;
        }
        logger::instance()->log(__METHOD__.'() uid (after): '.var_export($uid, true), EXTRA_VERBOSE_MODE);
        return $uid;
    }

    /**
     * Renvoie l'identifiant de l'alternate filestorage à partir d'un UID de document préfixé
     *
     * @param  string  $uid  L'identifiant (préfixé)
     *
     * @return string  L'identifiant de l'alternate filestorage, ou null si préfixe absent de l'UID
     */
    protected function getAltFsKeyFromUIDPrefix(string $uid) {
        logger::instance()->log(__METHOD__.'() uid: '.var_export($uid, true), EXTRA_VERBOSE_MODE);
        if(strpos($uid, self::ALTFS_SEP) !== false) {
            list($prefixe, $rest) = explode(self::ALTFS_SEP, $uid, 2);
            logger::instance()->log(__METHOD__.'() prefixe: '.var_export($prefixe, true), EXTRA_VERBOSE_MODE);
            return $prefixe;
        }
        logger::instance()->log(__METHOD__.'() prefixe: '.var_export(null, true), EXTRA_VERBOSE_MODE);
        return null;
    }

    /**
     * Cette fonction appelle la fonction de mémé nom de la
     * ressource de stockage pour modifier un fichier, ou des metadata
     * d'un fichier.
     * @param string $uid L'identifiant de fichier
     * @param string $file_content Le contenu de fichier
     * @param mixed $metadata Les metadata du fichier à sauvegarder
     * @param string $mode origine des données (content/temporary/path)
     * @return string Le résultat de modification retourné par la ressource de
     * stockage. Si la classe de sauvegarde n'était pas instanciée, on
     * retourne OP_FAILURE.
     */
    public function update($uid, $file_content, $metadata, $mode = "from_content") {
        $storage = $this->storage;
        $uid_prefixe = '';
        if (! empty($alternate_storage_key = $this->getAltFsKeyFromUIDPrefix($uid))) {
            $storage = $this->init_alternate_storage($alternate_storage_key);
            // Pour récupérer l'uid correct il faut enlever le préfixe
            $uid = $this->removePrefixFromUID($uid);
            $uid_prefixe = $alternate_storage_key.self::ALTFS_SEP;
        }

        if (is_null($storage)) {
            return OP_FAILURE;
        }
        $ret = $storage->lock($uid);
        if ($ret !== true) {
            return $ret;
        }

        $ret = $storage->update($uid, $file_content, $metadata, $mode);

        $storage->unlock($uid);

        if( $ret == OP_FAILURE) {
            return $ret;
        }
        // Si $ret ne contient pas OP_FAILURE, on a donc un uid
        return $uid_prefixe.$ret;    
    }


    /**
     * Cette fonction appelle la fonction de même nom de la ressource de
     * stockage pour modifier les métadonnées d'un fichier.
     *
     * @param string $uid      Identifiant du fichier dans le filestorage.
     * @param array  $metadata Liste des métadonnées à mettre à jour.
     *
     * @return mixed Identifiant du fichier ou OP_FAILURE.
     */
    public function update_metadata($uid, array $metadata) {
        $storage = $this->storage;
        $uid_prefixe = "";
        if (! empty($alternate_storage_key = $this->getAltFsKeyFromUIDPrefix($uid))) {
            $storage = $this->init_alternate_storage($alternate_storage_key);
            // Pour récupérer l'uid correct il faut enlever le préfixe
            $uid = $this->removePrefixFromUID($uid);
            $uid_prefixe = $alternate_storage_key.self::ALTFS_SEP;
        }

        if (is_null($storage)) {
            return OP_FAILURE;
        }
        $ret = $storage->lock($uid);
        if ($ret !== true) {
            return $ret;
        }

        $ret = $storage->update_metadata($uid, $metadata);

        $storage->unlock($uid);

        if( $ret == OP_FAILURE) {
            return OP_FAILURE;
        }
        // Si $ret ne contient pas la valeur OP_FAILURE, on a donc un uid
        return $uid_prefixe.$ret;
    }


    /**
     * Cette fonction appelle la fonction de même nom de la
     * ressource de stockage pour supprimer un fichier
     * @param string $uid L'identifiant de fichier
     * @return string Le résultat de suppression retourné par la ressource de
     * stockage. Si la classe de sauvegarde n'était pas instanciée, on
     * retourne OP_FAILURE.
     */
    public function delete($uid) {
        $storage = $this->storage;
        $uid_prefixe = "";
        if (! empty($alternate_storage_key = $this->getAltFsKeyFromUIDPrefix($uid))) {
            $storage = $this->init_alternate_storage($alternate_storage_key);
            // Pour récupérer l'uid correct il faut enlever le préfixe
            $uid = $this->removePrefixFromUID($uid);
            $uid_prefixe = $alternate_storage_key.self::ALTFS_SEP;
        }

        if (is_null($storage)) {
            return OP_FAILURE;
        }
        $ret = $storage->lock($uid);
        if ($ret !== true) {
            return $ret;
        }

        $ret = $storage->delete($uid);
        // il est correct de faire un unlock, meme si, techniquement,
        // les fichiers ne doivent pas exister
        $storage->unlock($uid);

        if ($ret == OP_FAILURE) {
            return OP_FAILURE;
        }
        // Si $ret ne contient pas la valeur OP_FAILURE, on a donc un uid.
        return $uid_prefixe.$ret;
    }


    /**
     * Cette fonction appelle la fonction de même nom de la
     * ressource de stockage pour retourner les données d'un fichier,
     * ce qui inclure le fichier lui même, et le metadata.
     * @param string $uid L'identifiant de fichier
     * @return string Le résultat de extraction retourné par la ressource de
     * stockage. Si la classe de sauvegarde n'était pas instanciée, on
     * retourne OP_FAILURE.
     */
    public function get($uid) {
        $storage = $this->storage;
        if (! empty($alternate_storage_key = $this->getAltFsKeyFromUIDPrefix($uid))) {
            logger::instance()->log(__METHOD__.'() alternate_storage_key: '.var_export($alternate_storage_key, true), EXTRA_VERBOSE_MODE);
            $storage = $this->init_alternate_storage($alternate_storage_key);
            logger::instance()->log(__METHOD__.'() storage: '.(is_object($storage) ? get_class($storage) : var_export($storage, true)), EXTRA_VERBOSE_MODE);
            // Pour récupérer l'uid correct il faut enlever le préfixe
            $uid = $this->removePrefixFromUID($uid);
            logger::instance()->log(__METHOD__.'() uid: '.var_export($uid, true), EXTRA_VERBOSE_MODE);
        }

        if (is_null($storage)) {
            logger::instance()->log(__METHOD__.'() failure: empty storage', EXTRA_VERBOSE_MODE);
            return OP_FAILURE;
        }

        $op = $storage->get($uid);
        if (is_array($op)) {
            logger::instance()->log(__METHOD__.'() op (keys): '.var_export(array_keys($op), true), EXTRA_VERBOSE_MODE);
        }
        else {
            logger::instance()->log(__METHOD__.'() op: '.var_export($op, true), EXTRA_VERBOSE_MODE);
        }
        return $op;
    }

    /**
     * Cette fonction appelle la fonction de même nom de la
     * ressource de stockage pour sauvegarder un fichier temporaire
     * @param string $data Le contenu de fichier
     * @param mixed $metadata Les metadata du fichier à sauvegarder
     * @param string $mode origine des données (content/temporary/path)
     * @return string Le résultat de création retourné par la ressource de
     * stockage. Si la classe de sauvegarde n'était pas instanciée, on
     * retourne OP_FAILURE.
     */
    public function create_temporary($data, $metadata, $mode = "from_content") {
        // on retourne une erreur si la ressource de stockage n'était pas trouvée
        if (is_null($this->storage)) {
            return OP_FAILURE;
        }
        // appel à la resource de stockage pour stocker le fichier
        return $this->storage->create_temporary($data, $metadata, $mode);
    }

    /**
     * Cette fonction appelle la fonction de même nom de la
     * ressource de stockage pour supprimer un fichier temporaire
     * @param string $uid L'identifiant de fichier
     * @return string Le résultat de suppression retourné par la ressource de
     * stockage. Si la classe de sauvegarde n'était pas instanciée, on
     * retourne OP_FAILURE.
     */
    public function delete_temporary($uid) {
        //
        if (is_null($this->storage)) {
            return OP_FAILURE;
        }
        $ret = $this->storage->lock($uid);
        if ($ret === true) {
            //
            $ret = $this->storage->delete_temporary($uid);
            // il est correct de faire un unlock, meme si, techniquement,
            // les fichiers ne doivent pas exister
            $this->storage->unlock($uid);
        }
        return $ret;
    }

    /**
     * Cette fonction appelle la fonction de même nom de la
     * ressource de stockage pour retourner les données d'un fichier temporaire,
     * ce qui inclure le fichier lui même, et le metadata.
     * @param string $uid L'identifiant de fichier
     * @return string Le résultat de extraction retourné par la ressource de
     * stockage. Si la classe de sauvegarde n'était pas instanciée, on
     * retourne OP_FAILURE.
     */
    public function get_temporary($uid) {
        //
        if (is_null($this->storage)) {
            return OP_FAILURE;
        }
        return $this->storage->get_temporary($uid);
    }

    /**
     * Cette fonction retourne le nom de fichier temporaire qui est stocké
     * sous l'uid passé en paramètre.
     * @param string $uid L'identifiant de fichier
     * @return Le nom de fichier, si le fichier est trouvé, ou
     * OP_FAILURE si la classe de sauvegarde n'était pas instanciée
     */
    public function getFilename_temporary($uid) {
        if (is_null($this->storage)) {
            return OP_FAILURE;
        }
        return $this->storage->getFilename_temporary($uid);
    }


    /**
     * Cette fonction retourne le répertoire qui stocke le fichier avec
     * les données brutes, et le fichier avec les métadonnées. Quand un
     * fichier qui sert comme lock est créé il est placé dans ce même
     * répertoire
     * @param string $uid L'identifiant du fichier
     * @return Le répertoire qui contient les fichiers. Si le chemin de
     * racine de sauvegarde des fichiers n'est pas set, on retourne null.
     */
    public function getPath_temporary($uid) {
        if (is_null($this->storage)) {
            return OP_FAILURE;
        }
        // appel à la resource de stockage pour stocker le fichier
        return $this->storage->getPath_temporary($uid);
    }


    /**
     * Cette fonction retourne le nom de fichier qui est stocké
     * sous l'uid passé en paramètre.
     * @param string $uid L'identifiant de fichier
     * @return Le nom de fichier, si le fichier est trouvé, ou
     * OP_FAILURE si la classe de sauvegarde n'était pas instanciée
     */
    public function getFilename($uid) {
        $storage = $this->storage;
        if (! empty($alternate_storage_key = $this->getAltFsKeyFromUIDPrefix($uid))) {
            $storage = $this->init_alternate_storage($alternate_storage_key);
            // Pour récupérer l'uid correct il faut enlever le préfixe
            $uid = $this->removePrefixFromUID($uid);
        }
        if (is_null($storage)) {
            return OP_FAILURE;
        }
        return $storage->getFilename($uid);
    }


    /**
     * Cette fonction retourne le mime type de fichier qui est stocké
     * sous l'uid passé en paramètre.
     * @param string $uid L'identifiant de fichier
     * @return Le mime type de fichier, si le fichier est trouvé, ou
     * OP_FAILURE si la classe de sauvegarde n'était pas instanciée
     */
    public function getMimetype($uid) {
        $storage = $this->storage;
        if (! empty($alternate_storage_key = $this->getAltFsKeyFromUIDPrefix($uid))) {
            $storage = $this->init_alternate_storage($alternate_storage_key);
            // Pour récupérer l'uid correct il faut enlever le préfixe
            $uid = $this->removePrefixFromUID($uid);
        }
        if (is_null($storage)) {
            return OP_FAILURE;
        }
        return $storage->getMimetype($uid);
    }


    /**
     * Cette fonction retourne la taille de fichier qui est stocké
     * sous l'uid passé en paramètre.
     * @param string $uid L'identifiant de fichier
     * @return La taille de fichier, si le fichier est trouvé, ou
     * OP_FAILURE si la classe de sauvegarde n'était pas instanciée
     */
    public function getSize($uid) {
        $storage = $this->storage;
        if (! empty($alternate_storage_key = $this->getAltFsKeyFromUIDPrefix($uid))) {
            $storage = $this->init_alternate_storage($alternate_storage_key);
            // Pour récupérer l'uid correct il faut enlever le préfixe
            $uid = $this->removePrefixFromUID($uid);
        }
        if (is_null($storage)) {
            return OP_FAILURE;
        }
        return $storage->getSize($uid);
    }


    /**
     * Cette fonction retourne le résultat d'appel à la fonction qui
     * retourne les métadonnées du fichier stocké sous l'uid passé
     * en paramètre.
     * @param string $uid L'identifiant de fichier
     * @return Le résultat d'appel à la fonction de la classe utilisée
     * pour le stockage des fichiers. On retourne OP_FAILURE si la classe
     * de sauvegarde n'était pas instanciée
     */
    public function getInfo($uid) {
        $storage = $this->storage;
        if (! empty($alternate_storage_key = $this->getAltFsKeyFromUIDPrefix($uid))) {
            $storage = $this->init_alternate_storage($alternate_storage_key);
            // Pour récupérer l'uid correct il faut enlever le préfixe
            $uid = $this->removePrefixFromUID($uid);
        }
        if (is_null($storage)) {
            return OP_FAILURE;
        }
        return $storage->getInfo($uid);
    }
}



/**
 * Définition de la classe 'filestorage_base'.
 */
class filestorage_base {

    /**
     * Cette variable est l'objet renvoye par la connexion à la ressource de stockage temporaire
     * @var null|filestorage_base
     */
    var $temporary_storage;

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de permettre la sauvegarde d'un fichier contenant les données,
     * ainsi que la sauvegarde de fichier contenant les métadonnées du fichier
     * précédemment cité.
     * @param string $file_content Le contenu de fichier
     * @param mixed $metadata Les métadonnées du fichier à sauvegarder
     * @param string $mode origine des données (content/temporary/path)
     * @return null
     */
    protected function create($file_content, $metadata, $mode = "from_content") {
        return null;
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de permettre la suppression d'un fichier contenant les données,
     * ainsi que de supprimer le fichier contenant les métadonnées du fichier
     * précédemment cité.
     * @param string $uid L'identifiant du fichier
     * @return L'identifiant du fichier
     */
    protected function delete($uid) {
        return $uid;
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de permettre la modification d'un fichier contenant les données,
     * ainsi que la modification du fichier contenant les métadonnées du fichier
     * précédemment cité.
     * @param string $uid L'identifiant du fichier
     * @param string $file_content Le contenu de fichier
     * @param mixed $metadata Les métadonnées du fichier à sauvegarder
     * @param string $mode origine des données (content/temporary/path)
     * @return string $uid L'identifiant du fichier
     */
    protected function update($uid, $file_content, $metadata, $mode = "from_content") {
        return $uid;
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de permettre la modification des métadonnées d'un fichier.
     *
     * @param string $uid      Identifiant du fichier dans le filestorage.
     * @param array  $metadata Liste des métadonnées à mettre à jour.
     *
     * @return string Identifiant du fichier.
     */
    protected function update_metadata($uid, array $metadata) {
        return $uid;
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de permettre l'extraction d'un fichier contenant les données,
     * ainsi que l'extraction de contenu du fichier contenant les métadonnées
     * du fichier précédemment cité.
     * @param string $uid L'identifiant du fichier
     */
    protected function get($uid) {
        return null;
    }

    /**
     * Créer un fichier temporaire sur le filesystem
     * @param  [string] $data contenu du fichier
     * @param  [mixed] $metadata     [tableau de méta données]
     * @param string $mode origine des données (content/temporary/path)
     * @return [string] uid
     */
    public function create_temporary($data, $metadata, $mode) {
        return $this->temporary_storage->create($data, $metadata, $mode);
    }

    /**
     * Créer un fichier temporaire sur le filesystem
     * @param  [string] $uid uid du fichier
     * @return uid
     */
    public function delete_temporary($uid) {
        return $this->temporary_storage->delete($uid);
    }

    /**
     * Son but est de permettre l'extraction d'un fichier contenant les données,
     * ainsi que l'extraction de contenu du fichier contenant les métadonnées
     * @param  [string] $uid uid du fichier
     * @return null
     */
    public function get_temporary($uid) {
        return $this->temporary_storage->get($uid);
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de retourner le nom du fichier temporaire identifié par l'uid passe en
     * paramétré.
     * @param string $uid L'identifiant du fichier
     * @return null
     */
    public function getFilename_temporary($uid) {
        return $this->temporary_storage->getFilename($uid);
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de retourner le path du fichier temporaire identifié par l'uid passe en
     * paramétré.
     * @param string $uid L'identifiant du fichier
     * @return null
     */
    public function getPath_temporary($uid) {
        return $this->temporary_storage->getPath($uid);
    }

    /**
     * Permet de recupérer le contenu et les métadonnées d'un fichier en fonction
     * du mode passé en paramètre qui définira le type de la donnée.
     * @param  string $data handle du fichier
     * @param string $mode origine des données (content/temporary/path)
     * @return mixed       Contenu du fichier
     */
    protected function getContent($data, $mode) {
        if ($mode == "from_temporary") {
            return $this->temporary_storage->get($data);
        } elseif ($mode == "from_path") {
            return array('file_content' => file_get_contents($data), 'metadata' => "");
        }
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de permettre le lock d'un fichier.
     * @param string $uid L'identifiant du fichier
     * @return false
     */
    protected function lock($uid) {
        //
        return false;
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de lâcher le lock sur un fichier.
     * @param string $uid L'identifiant du fichier
     */
    protected function unlock($uid) {
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de retourner le nom du fichier identifié par l'uid passe en
     * paramétré.
     * @param string $uid L'identifiant du fichier
     * @return null
     */
    protected function getFilename($uid) {
        return null;
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de retourner le mime type du fichier identifié par l'uid passe en
     * paramétré.
     * @param string $uid L'identifiant du fichier
     * @return null
     */
    protected function getMimetype($uid) {
        return null;
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de retourner le path du fichier identifié par l'uid passe en
     * paramétre.
     * @param string $uid L'identifiant du fichier
     * @return null
     */
    protected function getPath($uid) {
        return null;
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de retourner la taille du fichier identifié par l'uid passe en
     * paramétré.
     * @param string $uid L'identifiant du fichier
     * @return null
     */
    protected function getSize($uid) {
        return null;
    }

    /**
     * Cette fonction doit être implémente par des classes dérivées. Son but
     * est de retourner les metadonnees du fichier identifié par l'uid passe en
     * paramétré.
     * @param string $uid L'identifiant du fichier
     * @return null
     */
    protected function getInfo($uid) {
        return null;
    }
}
