php - Bases de données de fichiers plats

Translate

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 matures que je vois 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 stade).

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

This question and all comments follow the "Attribution Required."

Toutes les réponses

Translate

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, dire des profils d'utilisateurs construits comme tels:

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

et pour enregistrer ou mettre à jour leenregistrement dbpour 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 lerecordpour 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.

La source
Translate

Vous pourriez envisagerSQLite. C'est presque aussi simple que des fichiers plats, mais vous obtenez un moteur SQL pour les requêtes. Ilfonctionne bien avec PHPaussi.

La source
Mez
Translate

À 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, utiliserserialize()etunserialize()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 là, j'ai appris que la pérennité de mon application afin que, lorsqu'elle s'agrandisse, je n'ai pas à passer des jours à la refactoriser, c'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)

La source
Translate

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 PHPglob()appel (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".

La source
Translate

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).

La source
Translate

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

La source
Translate

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).

La source
Translate

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;
}
La source
Translate

Celui-ci est inspirant comme solution pratique:
https://github.com/mhgolkar/FlatFire
Il utilise plusieurs stratégies pour gérer les données ...
[Copié à partir du fichier Lisez-moi]

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
La source
Translate

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.XMLReaderest une classe légère de type curseur à lecture directe.SimpleXMLsimplifie la lecture d'un document XML dans un objet auquel vous pouvez accéder comme toute autre instance de classe.

La source
Translate

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.

La source