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

120

Quelles sont les meilleures pratiques concernant la création de structures de base de données de fichiers plats en PHP?

Beaucoup de frameworks de fichiers plats PHP plus mûrs là-bas que j'essaie d'implémenter une syntaxe de requête de type SQL qui est au-dessus de mes besoins dans la plupart des cas. (Je voudrais simplement utiliser une base de données à ce stade).

Existe-t-il des astuces élégantes pour obtenir de bonnes performances et fonctionnalités avec une petite surcharge de code?

saint_groceon
la source
1
Je voudrais ajouter qu'il existe un package ici pour la base de données de fichiers plats github.com/tmarois/Filebase Je sais que c'est une vieille question, mais ce package est la version la plus récente et maintenue, plus pleine de fonctionnalités que la plupart négligent d'inclure .
tmarois
Je développe un CMS et j'utilise une base de données texte de fichier texte plat. Il a fallu de nombreuses heures à fabriquer et de nombreuses heures à réfracter mais cela fonctionne parfaitement. Les requêtes seront exécutées beaucoup plus rapidement avec une base de données entièrement indexée et optimisée. Cependant, j'évite le besoin de requêtes en stockant des métadonnées et avec une organisation et une structure soignées. Lorsque j'ai besoin de données, je les reçois sans for loop(sauf si j'utilise toutes les données du dossier), donc elles fonctionnent beaucoup plus rapidement qu'une base de données. Je voudrais entrer dans les détails et donner une très bonne réponse mais malheureusement cette question est close.
Dan Bray

Réponses:

75

Eh bien, quelle est la nature des bases de données plates. Sont-ils grands ou petits. S'agit-il de tableaux simples contenant des tableaux? si c'est quelque chose de simple, dites userprofiles construits comme tels:

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

et pour sauvegarder ou mettre à jour l' enregistrement de base de données 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 variera selon l'application et la nature de la base de données dont vous avez besoin.

w-ll
la source
48

Vous pourriez envisager SQLite . C'est presque aussi simple que des fichiers plats, mais vous obtenez un moteur SQL pour les requêtes. Cela fonctionne aussi bien avec PHP .

Yukondude
la source
6
SQLite a été intégré à 5.0+ par défaut, mais escompté (!) À partir de PHP 5.4+ !!! Au moment où j'écris ceci en juillet 2012, SQLite ne fonctionnera plus par défaut sur les systèmes à jour.
Déclaration
L'installation du pilote SQLite PDO est assez simple si vous avez accès au serveur. Sur Ubuntu / Debian exécutant Apache2, faites simplement apt-get install php5-sqlite service apache2 restart
siliconrockstar
4
En réaction au commentaire de @Sliq, dire que "SQLite a été ... interrompu" est en quelque sorte vrai: l'extension nommée "SQLite" a été abandonnée et "SQLite3" est maintenant activée par défaut. php.net/manual/en/sqlite.installation.php "Depuis PHP 5.0, cette extension était fournie avec PHP. Depuis PHP 5.4, cette extension n'est disponible que via PECL." php.net/manual/en/sqlite3.installation.php "L'extension SQLite3 est activée par défaut à partir de PHP 5.3.0." "Cette extension était brièvement une extension PECL mais cette version n'est recommandée que pour une utilisation expérimentale."
Paul van Leeuwen
Vous n'avez pas répondu à la question
JG Estiot
20

À mon avis, utiliser une "base de données de fichiers plats" dans le sens où vous l'entendez (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, utiliser 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 arbitraire 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éé mes propres fichiers "propriétaires", et le projet a explosé à un point où il a besoin d'une base de données, et je pense "vous savez, je souhaite J'avais écrit ceci pour une base de données pour commencer "- parce que la refactorisation du code prend beaucoup trop de temps et d'efforts.

De cela, j'ai appris que la pérennité de mon application afin que, lorsqu'elle s'agrandisse, je n'ai pas à passer des jours à la refactorisation est la voie à suivre. Comment puis-je faire cela?

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

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

Mez
la source
C'est vrai. serialize()peut être très utile pour cela aussi. Je pense que le truc pour trouver un système viable est de trouver un moyen d'indexer les nœuds de données sans vous tuer avec la complexité.
saint_groceon
12

Un cadre que j'envisage serait celui d'une plateforme de blogs. Étant donné que presque toutes les vues possibles des données que vous souhaiteriez seraient triées par date, je pensais à cette structure:

Un répertoire par nœud de contenu:

./content/YYYYMMDDHHMMSS/

Sous-répertoires de chaque nœud comprenant

/tags  
/authors  
/comments  

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

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

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

Renvoyerait des chemins comprenant tous les articles étiquetés "drôle".

saint_groceon
la source
9

Voici le code que nous utilisons pour Lilina:

<?php
/**
 * Handler for persistent data files
 *
 * @author Ryan McCue <[email protected]>
 * @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é suffisamment efficace pour être utilisé (aucune donnée inutile n'est chargée et il est plus rapide à enregistrer).

Ryan McCue
la source
8

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

Jason
la source
Et suivez les règles xml de lisibilité humaine ou vous pouvez aussi bien utiliser la sérialisation ou json ou quelque chose.
Ben
Très mauvais conseil. XML ne doit jamais être utilisé. C'est une grosse aberration.
JG Estiot
@JGEstiot Voulez-vous expliquer davantage?
UncaughtTypeError
7

Si vous souhaitez 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 déboguer (et le 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é selon |).

Cependant, l'inconvénient est 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 que vous devez gérer le séparateur dans les données (par exemple si le pseudo est WaR | ordz).

ofaurax
la source
7

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 (s'il s'agit d'un tableau, d'une chaîne ou d'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;
}
jpcrevoisier
la source
J'ai trouvé cela intéressant et c'est la meilleure façon, car nous venons de vider le tableau formaté dans un fichier. Nous n'avons pas besoin de le reconstruire, il suffit de le lire. En outre, modifier les variables, c'est un peu facile. Je n'utiliserai jamais cela pour stocker des données volumineuses, mais j'ai trouvé pratique de stocker les modules du programme sans base de données. Je vous remercie.
m3nda
7

Celui-ci est une solution pratique inspirante:
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
Omran
la source
7

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

  1. SQLite

    Si vous connaissez PDO, vous pouvez installer un pilote PDO prenant en charge SQLite. Je ne l'ai jamais utilisé, mais j'ai utilisé PDO une tonne avec MySQL. Je vais vous donner une chance sur un projet en cours.

  2. XML

    Fait cela plusieurs fois pour des quantités de données relativement petites. XMLReader est une classe légère de type curseur à lecture directe. SimpleXML simplifie la lecture d'un document XML dans un objet auquel vous pouvez accéder comme n'importe quelle autre instance de classe.

siliconrockstar
la source
5

Soulignant simplement 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 contiennent un "|" ou un "\ n" alors 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 colonnes: #$% (Shift+345)

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

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

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

Ou quoi que ce soit de ce genre. Aussi, 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 (c'est-à-dire moins de 20 lignes), mais deviennent d'énormes porcs de mémoire pour les bases de données plus grandes.

Michael Burt
la source
Bons points. Pour aller plus loin, PHP peut sérialiser JSON très facilement. L'échappement de l'entrée est beaucoup plus simple, vous n'avez donc pas besoin d'utiliser des combinaisons de chaînes amusantes pour que le fichier soit plus lisible.
Cypher le