Bases de données de fichiers plats [fermé]

Quelles sont les meilleures pratiques pour créer des structures de base de données de fichiers plats en PHP?

Beaucoup des frameworks de fichiers plats PHP les plus matures que je vois tentent d'implémenter une syntaxe de requête de type SQL, qui est supérieure à mes besoins dans la plupart des cas (j'utiliserais simplement une base de données à ce stade).

Y a-t-il des astuces élégantes pour obtenir de bonnes performances et des fonctionnalités avec un petit surcoût de code?

104
demandé sur Rann Lifshitz 2008-08-01 18:19:52

12 réponses

Eh bien, quelle est la nature des bases de données plates. Sont-ils grands ou petits. Est-ce des tableaux simples avec des tableaux en eux? si c'est quelque chose de simple, dites userprofiles construits en tant que tels:

$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 pour charger l'enregistrement pour l'utilisateur

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

Mais encore une fois, cette implémentation varie en fonction de l'application et de la nature de la base de données dont vous avez besoin.

71
répondu w-ll 2012-10-14 12:26:34

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.

47
répondu yukondude 2008-08-08 23:00:54

À mon avis, utiliser une "base de données de fichiers plats" dans le sens que vous entendez (et la réponse que vous avez acceptée) n'est pas nécessairement la meilleure façon de faire les choses. Tout d'abord, l'utilisation de serialize() et unserialize() peut causer des maux de tête majeurs si quelqu'un Entre et modifie le fichier (ils peuvent, en fait, mettre du code arbritrary dans votre "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éé le mien les fichiers" propriétaires", et le projet a explosé au point où il a besoin d'une base de données, et je pense" vous savez, j'aurais aimé écrire ceci pour une base de données pour commencer " - parce que le refactoring du code prend beaucoup trop de temps et d'efforts.

De cela, j'ai appris que l'imperméabilisation future de mon application afin que quand elle grossisse, Je n'ai pas à passer des jours à refactorer est la façon d'aller de l'avant. Comment dois-je faire?

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

En fait, espescially avec la méthode de "réponse acceptée", il peut réduire considérablement l'utilisation de la mémoire de votre application (vous n'avez pas à charger tous les "enregistrements" en PHP)

20
répondu Mez 2012-10-31 13:23:52

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

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

15
répondu saint_groceon 2012-10-14 12:28:29

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

Un répertoire par nœud de contenu:

./content/YYYYMMDDHHMMSS/

Sous-répertoires de chaque nœud, y compris

/tags  
/authors  
/comments  

Ainsi que des fichiers texte simples dans le répertoire de noeud pour le contenu pré-et post-rendu et autres.

Cela permettrait un PHP simpleglob() appel (et probablement un renversement du tableau de résultats) pour interroger à peu près n'importe quoi dans la structure de contenu:

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

Retournerait des chemins incluant tous les articles étiquetés "Drôle".

12
répondu saint_groceon 2015-08-24 17:19:04

Voici le code que nous utilisons pour Lilina:

<?php
/**
 * Handler for persistent data files
 *
 * @author Ryan McCue <cubegames@gmail.com>
 * @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 en tant que fichier séparé, ce que nous avons trouvé assez efficace pour l'utilisation (Aucune donnée inutile n'est chargée et c'est plus rapide à enregistrer).

9
répondu Ryan McCue 2015-05-03 13:29:02

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

8
répondu Jason 2012-10-28 07:03:01

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 d'enregistrer 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;
}
7
répondu jpcrevoisier 2012-12-19 21:17:45

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 n'avez qu'un seul fichier, vous pouvez le corriger (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, répartie en fonction des |).

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 devez gérer le séparateur dans les données (par exemple si le nick est la guerre / ordz).

6
répondu ofaurax 2008-09-18 07:51:30

C'est inspirant comme une solution pratique:
https://github.com/mhgolkar/FlatFire
Il utilise plusieurs stratégies pour gérer les données...
[Copié à partir du fichier Readme]

Libre 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
6
répondu omran 2013-05-02 13:57:13

IMHO, vous avez deux options si vous voulez éviter quelque chose homebrewing:

  1. SQLite

    Si vous êtes familier avec PDO, vous pouvez installer un pilote PDO qui prend en charge SQLite. Je ne l'ai jamais utilisé, mais j'ai utilisé PDO une tonne avec MySQL. Je vais donner un coup de feu sur un projet en cours.

  2. XML

    Fait cela plusieurs fois pour des quantités relativement faibles de données. XMLReader {[18] } est une classe légère de style curseur en lecture directe. SimpleXML facilite la lecture d'un document XML dans un objet auquel vous pouvez accéder comme n'importe quelle autre instance de classe.

6
répondu siliconrockstar 2015-08-24 17:50:22

Il suffit de signaler un problème potentiel avec une base de données de fichiers plats 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 contient un "|" ou "\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:

Séparateur de colonne: #$% (Shift+345)

Séparateur de ligne: ^&* (Shift+678)

Fichier Texte: test data#$%blah blah#$%^&*new row#$%new row data 2

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

Ou quoi que ce soit le long de ces ligne. 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 (ie. moins de 20 lignes), mais deviennent d'énormes porcs de mémoire pour les grandes bases de données.

4
répondu Michael Burt 2013-01-04 00:39:53