Bases de données de fichiers plats

Quelles sont les meilleures pratiques concernant la création de structures de base de données de fichiers à plat en PHP? Beaucoup des frameworks de fichiers plats PHP plus matures que je vois là-bas tentent d'implémenter une syntaxe de requête de type SQL, ce qui est excessif pour mes besoins dans la plupart des cas (j'utiliserais simplement une base de données à ce moment là).

Existe-t-il des astuces élégantes pour obtenir de bonnes performances et fonctionnalités avec un petit code que l'on voudrait en s'attaquant à ce problème en premier lieu?

0
ajouté édité
Vues: 13
Je voudrais ajouter qu'il existe un package ici pour Base de données de fichiers plats github.com/tmarois/Filebase Je sais que c'est une vieille question, mais ce paquet est la version la plus récente et la plus conservée, en plus de plein de fonctionnalités .
ajouté l'auteur timothymarois, source

12 Réponses

Un cadre que je considère serait pour une plate-forme de blogs. Comme à peu près n'importe quelle vue possible des données que vous voulez serait triée par date, je pensais à cette structure:

Un répertoire par noeud de contenu:

./content/YYYYMMDDHHMMSS/

Sous-répertoires de chaque noeud, y compris

/tags  
/authors  
/comments  

Ainsi que de simples fichiers texte dans le répertoire des nœuds pour le contenu pré-et post-rendu et similaires.

Cela permettrait un appel PHP glob() simple (et probablement une inversion de le tableau des résultats) pour interroger à peu près n'importe quoi dans la structure du contenu:

glob("content/*/tags/funny");  

Souhaitez retourner les chemins d'accès, y compris tous les articles marqués "drôle".

0
ajouté

C'est vrai. serialize() peut être très utile pour ça aussi.

Je pense que l'astuce pour arriver à un système viable est de trouver un moyen d'indexer les nœuds de données sans se tuer avec la complexité.

0
ajouté

Eh bien, quelle est la nature des bases de données à plat. Sont-ils grands ou petits. Est-ce des tableaux simples avec des tableaux en eux? si c'est quelque chose de simple, dites userprofiles construit comme tel:

$user = array("name" => "dubayou", 
              "age" => 20,
              "websites" => array("dubayou.com","willwharton.com","codecream.com"),
              "and_one" => "more");

et pour enregistrer ou mettre à jour l'enregistrement db pour cet utilisateur.

$dir = "../userdata/";  //make sure to put it bellow what the server can reach.
file_put_contents($dir.$user['name'],serialize($user));

et charger l' enregistrement pour l'utilisateur

function &get_user($name){
    return unserialize(file_get_contents("../userdata/".$name));
}

mais encore une fois cette mise en œuvre variera selon l'application et la nature de la base de données dont vous avez besoin.

0
ajouté

Vous pouvez considérer SQLite . C'est presque aussi simple que des fichiers plats, mais vous obtenez un moteur SQL pour l'interrogation. Il fonctionne bien avec PHP aussi.

0
ajouté
SQLite a été construit dans 5.0+ par défaut, mais discountinued (!) À partir de PHP 5.4+ sur !!! Au moment où j'écris ceci en juillet 2012, SQLite ne fonctionnera plus par défaut sur les systèmes les plus récents. Déclaration officielle here
ajouté l'auteur Sliq, source
Installation du pilote SQLite PDO est assez trivial si vous avez un accès au serveur. Sur Ubuntu / Debian exécutant Apache2, faites juste apt-get install service php5-sqlite apache2 restart
ajouté l'auteur siliconrockstar, source
En réaction au commentaire de @Sliq, déclarer que "SQLite was ... interrompued" est une sorte de vrai: l'extension nommée "SQLite" a été abandonnée et "SQLite3" est maintenant activé par défaut. php.net/manual/fr/sqlite.installation.php "Depuis PHP 5.0 cette extension était fournie avec PHP A partir de PHP 5.4, cette extension n'est disponible que via PECL. " php.net/manual/fr/sqlite3.installation.php "SQLite3 L'e
ajouté l'auteur Paul van Leeuwen, source

À mon humble avis, vous avez deux options si vous voulez éviter quelque chose homebrewing:

  1. SQLite

    If you're familiar with PDO, you can install a PDO driver that supports SQLite. Never used it, but I have used PDO a ton with MySQL. I'm going to give this a shot on a current project.

  2. XML

    Done this many times for relatively small amounts of data. XMLReader is a lightweight, read-forward, cursor-style class. SimpleXML makes it simple to read an xml document into an object that you can access just like any other class instance.

0
ajouté

Si vous voulez un résultat lisible par l'homme, vous pouvez également utiliser ce type de fichier:

ofaurax|27|male|something|
another|24|unknown||
...

De cette façon, vous avez un seul fichier, vous pouvez le déboguer (et corriger manuellement) facilement, vous pouvez ajouter des champs plus tard (à la fin de chaque ligne) et le code PHP est simple (pour chaque ligne, divisée selon |).

Cependant, les inconvénients sont que vous devez analyser le fichier entier pour rechercher quelque chose (si vous avez des millions d'entrées, ce n'est pas bien) et vous devriez gérer le séparateur dans les données (par exemple si le pseudo est WaR | ordz).

0
ajouté

Si vous souhaitez utiliser un fichier plat pour conserver les données, utilisez xml pour structurer les données. PHP a un analyseur xml intégré .

0
ajouté
Et suivez les règles xml de lisibilité humaine ou vous pourriez aussi bien utiliser la sérialisation ou json ou quelque chose.
ajouté l'auteur Ben, source

À mon avis, l'utilisation d'une «base de données de fichiers plats» dans le sens que vous voulez dire (et la réponse que vous avez acceptée) n'est pas forcément la meilleure façon de faire les choses. Tout d'abord, l'utilisation de serialize() et de unserialize() peut causer des maux de tête MAJEURS si quelqu'un entre et édite le fichier (ils peuvent, en fait, placer du code arbitraire dans "base de données" à exécuter à chaque fois.)

Personnellement, je dirais - pourquoi ne pas regarder vers l'avenir? Il y a eu tellement de fois que j'ai eu des problèmes parce que j'ai créé mes propres fichiers "propriétaires", et le projet a explosé au point où il a besoin d'une base de données, et je me dis "vous savez, je souhaite Je l'ai écrit pour une base de données pour commencer "- parce que la refactorisation du code prend beaucoup trop de temps et d'efforts.

De là, j'ai appris que mon application est à l'épreuve du futur, de sorte que, quand elle s'agrandit, je n'ai pas besoin d'y aller et de passer des jours à refactoring. Comment puis-je faire cela?

SQLite. Il fonctionne comme une base de données, utilise SQL, et est assez facile de passer à mySQL (en particulier si vous utilisez des classes abstraites pour la manipulation de base de données comme je le fais!)

En fait, surtout avec la méthode de la "réponse acceptée", cela peut réduire drastiquement l'utilisation de la mémoire de votre application (vous n'avez pas besoin de charger tous les "RECORDS" en PHP)

0
ajouté

Voici le code que nous utilisons pour Lilina:

<?php
/**
 * Handler for persistent data files
 *
 * @author Ryan McCue 
 * @package Lilina
 * @version 1.0
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 */

/**
 * Handler for persistent data files
 *
 * @package Lilina
 */
class DataHandler {
    /**
     * Directory to store data.
     *
     * @since 1.0
     *
     * @var string
     */
    protected $directory;

    /**
     * Constructor, duh.
     *
     * @since 1.0
     * @uses $directory Holds the data directory, which the constructor sets.
     *
     * @param string $directory 
     */
    public function __construct($directory = null) {
        if ($directory === null)
            $directory = get_data_dir();

        if (substr($directory, -1) != '/')
            $directory .= '/';

        $this->directory = (string) $directory;
    }

    /**
     * Prepares filename and content for saving
     *
     * @since 1.0
     * @uses $directory
     * @uses put()
     *
     * @param string $filename Filename to save to
     * @param string $content Content to save to cache
     */
    public function save($filename, $content) {
        $file = $this->directory . $filename;

        if(!$this->put($file, $content)) {
            trigger_error(get_class($this) . " error: Couldn't write to $file", E_USER_WARNING);
            return false;
        }

        return true;
    }

    /**
     * Saves data to file
     *
     * @since 1.0
     * @uses $directory
     *
     * @param string $file Filename to save to
     * @param string $data Data to save into $file
     */
    protected function put($file, $data, $mode = false) {
        if(file_exists($file) && file_get_contents($file) === $data) {
            touch($file);
            return true;
        }

        if(!$fp = @fopen($file, 'wb')) {
            return false;
        }

        fwrite($fp, $data);
        fclose($fp);

        $this->chmod($file, $mode);
        return true;

    }

    /**
     * Change the file permissions
     *
     * @since 1.0
     *
     * @param string $file Absolute path to file
     * @param integer $mode Octal mode
     */
    protected function chmod($file, $mode = false){
        if(!$mode)
            $mode = 0644;
        return @chmod($file, $mode);
    }

    /**
     * Returns the content of the cached file if it is still valid
     *
     * @since 1.0
     * @uses $directory
     * @uses check() Check if cache file is still valid
     *
     * @param string $id Unique ID for content type, used to distinguish between different caches
     * @return null|string Content of the cached file if valid, otherwise null
     */
    public function load($filename) {
        return $this->get($this->directory . $filename);
    }

    /**
     * Returns the content of the file
     *
     * @since 1.0
     * @uses $directory
     * @uses check() Check if file is valid
     *
     * @param string $id Filename to load data from
     * @return bool|string Content of the file if valid, otherwise null
     */
    protected function get($filename) {
        if(!$this->check($filename))
            return null;

        return file_get_contents($filename);
    }

    /**
     * Check a file for validity
     *
     * Basically just a fancy alias for file_exists(), made primarily to be
     * overriden.
     *
     * @since 1.0
     * @uses $directory
     *
     * @param string $id Unique ID for content type, used to distinguish between different caches
     * @return bool False if the cache doesn't exist or is invalid, otherwise true
     */
    protected function check($filename){
        return file_exists($filename);
    }

    /**
     * Delete a file
     *
     * @param string $filename Unique ID
     */
    public function delete($filename) {
        return unlink($this->directory . $filename);
    }
}

?>

Il stocke chaque entrée dans un fichier séparé, ce que nous avons trouvé est assez efficace pour l'utilisation (aucune donnée inutile est chargée et il est plus rapide à enregistrer).

0
ajouté

J'ai écrit deux fonctions simples conçues pour stocker des données dans un fichier. Vous pouvez juger par vous-même si c'est utile dans ce cas. Le but est de sauvegarder une variable PHP (si c'est un tableau une chaîne ou un objet) dans un fichier.

<?php
function varname(&$var) {
    $oldvalue=$var;
    $var='AAAAB3NzaC1yc2EAAAABIwAAAQEAqytmUAQKMOj24lAjqKJC2Gyqhbhb+DmB9eDDb8+QcFI+QOySUpYDn884rgKB6EAtoFyOZVMA6HlNj0VxMKAGE+sLTJ40rLTcieGRCeHJ/TI37e66OrjxgB+7tngKdvoG5EF9hnoGc4eTMpVUDdpAK3ykqR1FIclgk0whV7cEn/6K4697zgwwb5R2yva/zuTX+xKRqcZvyaF3Ur0Q8T+gvrAX8ktmpE18MjnA5JuGuZFZGFzQbvzCVdN52nu8i003GEFmzp0Ny57pWClKkAy3Q5P5AR2BCUwk8V0iEX3iu7J+b9pv4LRZBQkDujaAtSiAaeG2cjfzL9xIgWPf+J05IQ==';
    foreach($GLOBALS as $var_name => $value) {
        if ($value === 'AAAAB3NzaC1yc2EAAAABIwAAAQEAqytmUAQKMOj24lAjqKJC2Gyqhbhb+DmB9eDDb8+QcFI+QOySUpYDn884rgKB6EAtoFyOZVMA6HlNj0VxMKAGE+sLTJ40rLTcieGRCeHJ/TI37e66OrjxgB+7tngKdvoG5EF9hnoGc4eTMpVUDdpAK3ykqR1FIclgk0whV7cEn/6K4697zgwwb5R2yva/zuTX+xKRqcZvyaF3Ur0Q8T+gvrAX8ktmpE18MjnA5JuGuZFZGFzQbvzCVdN52nu8i003GEFmzp0Ny57pWClKkAy3Q5P5AR2BCUwk8V0iEX3iu7J+b9pv4LRZBQkDujaAtSiAaeG2cjfzL9xIgWPf+J05IQ==')
        {
            $var=$oldvalue;
            return $var_name;
        }
    }
    $var=$oldvalue;
    return false;
}

function putphp(&$var, $file=false)
    {
    $varname=varname($var);
    if(!$file)
    {
        $file=$varname.'.php';
    }
    $pathinfo=pathinfo($file);
    if(file_exists($file))
    {
        if(is_dir($file))
        {
            $file=$pathinfo['dirname'].'/'.$pathinfo['basename'].'/'.$varname.'.php';
        }
    }
    file_put_contents($file,'<?php'."\n\$".$varname.'='.var_export($var, true).";\n");
    return true;
}
0
ajouté
J'ai trouvé cela intéressant et c'est la meilleure façon, parce que nous venons de vider le tableau formaté dans un fichier. Nous n'avons pas besoin de le reconstruire, il suffit de le lire. Aussi, modifier les variables c'est un peu facile. Je n'utiliserai jamais cela pour stocker de grandes données, mais j'ai trouvé pratique de stocker les modules du programme sans base de données. Je vous remercie.
ajouté l'auteur erm3nda, source
C'est une question vieille de quatre ans avec une réponse acceptée et beaucoup de réponses supplémentaires. Envisager de se concentrer sur des questions plus récentes, à moins que la réponse acceptée ici soit clairement erronée ou inadéquate.
ajouté l'auteur mcknz, source

Juste en soulignant un problème potentiel avec une base de données de fichier plat avec ce type de système:

data|some text|more data

row 2 data|bla hbalh|more data

...etc

Le problème est que les données de la cellule contiennent un "|" ou un "\ n", les données seront perdues. Parfois, il serait plus facile de diviser par des combinaisons de lettres que la plupart des gens n'utiliseraient pas.

Par exemple:

Column splitter: #$% (Shift+345)

Row splitter: ^&* (Shift+678)

Text file: test data#$%blah blah#$%^&*new row#$%new row data 2

Then use: explode("#$%", $data); use foreach, the explode again to separate columns

Ou quoi que ce soit le long de ces lignes. En outre, je pourrais ajouter que les bases de données de fichiers plats sont bonnes pour les systèmes avec de petites quantités de données (moins de 20 lignes), mais deviennent d'énormes porcs de mémoire pour les bases de données plus volumineuses.

0
ajouté
Bons points. Prenant ceci un peu plus loin, PHP peut sérialiser JSON très facilement. L'échappement de l'entrée est beaucoup plus simple, donc vous n'avez pas besoin d'utiliser de drôles de combinaisons de chaînes pour que le fichier soit plus lisible.
ajouté l'auteur Cypher, source

This one is inspiring as a practical solution:
https://github.com/mhgolkar/FlatFire
It uses multiple strategies to handling data...
[Copied from Readme File]

Gratuit ou structuré ou mixte

- STRUCTURED
Regular (table, row, column) format.
[DATABASE]
/   \
TX  TableY
    \_____________________________
    |ROW_0 Colum_0 Colum_1 Colum_2|
    |ROW_1 Colum_0 Colum_1 Colum_2|
    |_____________________________|
- FREE
More creative data storing. You can store data in any structure you want for each (free) element, its similar to storing an array with a unique "Id".
[DATABASE]
/   \
EX  ElementY (ID)
    \________________
    |Field_0 Value_0 |
    |Field_1 Value_1 |
    |Field_2 Value_2 |
    |________________|
recall [ID]: get_free("ElementY") --> array([Field_0]=>Value_0,[Field_1]=>Value_1...
- MIXD (Mixed)
Mixed databases can store both free elements and tables.If you add a table to a free db or a free element to a structured db, flat fire will automatically convert FREE or SRCT to MIXD database.
[DATABASE]
/   \
EX  TY
0
ajouté