Bases de datos de archivos planos [cerrado]


¿Cuáles son las mejores prácticas para crear estructuras de bases de datos de archivos planos en PHP?

Muchos de los frameworks de archivos planos PHP más maduros que veo por ahí intentan implementar una sintaxis de consulta similar a SQL, que es exagerada para mis propósitos en la mayoría de los casos (solo usaría una base de datos en ese momento).

¿Hay algún truco elegante para obtener un buen rendimiento y características con una pequeña sobrecarga de código?

Author: Rann Lifshitz, 2008-08-01

12 answers

Bueno, cuál es la naturaleza de las bases de datos planas. Son grandes o pequeños. ¿Son arreglos simples con arreglos en ellos? si es algo simple decir userprofiles construido como tal:

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

Y para guardar o actualizar el registro db para ese usuario.

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

Y cargar el registro para el usuario

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

Pero de nuevo esta implementación variará según la aplicación y la naturaleza de la base de datos que necesita.

 71
Author: w-ll,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-14 12:26:34

Podrías considerar SQLite. Es casi tan simple como archivos planos, pero obtienes un motor SQL para consultar. también funciona bien con PHP.

 47
Author: yukondude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-08 23:00:54

En mi opinión, usar una "Base de datos de Archivos planos" en el sentido que quieres decir (y la respuesta que has aceptado) no es necesariamente la mejor manera de hacer las cosas. En primer lugar, usar serialize() y unserialize() puede causar GRANDES dolores de cabeza si alguien entra y edita el archivo (de hecho, puede poner código arbritrary en su "base de datos" para que se ejecute cada vez.)

Personalmente, yo diría - ¿por qué no mirar hacia el futuro? Ha habido tantas veces que he tenido problemas porque he estado creando mi propio archivos "propietarios", y el proyecto ha explotado a un punto en el que necesita una base de datos, y estoy pensando" ya sabes, me gustaría haber escrito esto para una base de datos para empezar " - porque la refactorización del código toma demasiado tiempo y esfuerzo.

De esto he aprendido que la preparación futura de mi aplicación para que cuando se hace más grande no tengo que ir y pasar días refactorizando es la manera de seguir adelante. ¿Cómo hago esto?

SQLite. Funciona como una base de datos, utiliza SQL, y es bastante fácil de cambiar a MySQL (especialmente si está utilizando clases abstractas para la manipulación de bases de datos como yo!)

De hecho, especialmente con el método de "respuesta aceptada", puede reducir drásticamente el uso de memoria de su aplicación (no tiene que cargar todos los" REGISTROS " en PHP)

 20
Author: Mez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-31 13:23:52

Eso es cierto. serialize() puede ser bastante útil para eso también.

Creo que el truco para llegar a un sistema viable es encontrar alguna manera de indexar los nodos de datos sin suicidarse con la complejidad.

 15
Author: saint_groceon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-14 12:28:29

Un marco que estoy considerando sería para una plataforma de blogs. Dado que casi cualquier vista posible de los datos que desee se ordenaría por fecha, estaba pensando en esta estructura:

Un directorio por nodo de contenido:

./content/YYYYMMDDHHMMSS/

Subdirectorios de cada nodo, incluyendo

/tags  
/authors  
/comments  

Así como archivos de texto simples en el directorio de nodos para contenido pre y post - renderizado y similares.

Esto permitiría un PHP simpleglob() llamada (y probablemente una reversión de la matriz de resultados) para consultar sobre casi cualquier cosa dentro de la estructura de contenido:

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

Devolvería rutas incluyendo todos los artículos etiquetados como "divertido".

 12
Author: saint_groceon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-24 17:19:04

Aquí está el código que usamos para 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);
    }
}

?>

Almacena cada entrada como un archivo separado, que encontramos que es lo suficientemente eficiente para su uso (no se cargan datos innecesarios y es más rápido de guardar).

 9
Author: Ryan McCue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-03 13:29:02

Si va a utilizar un archivo plano para conservar los datos, utilice XML para estructurar los datos. PHP tiene un analizador XML integrado .

 8
Author: Jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-28 07:03:01

He escrito dos funciones simples diseñadas para almacenar datos en un archivo. Usted puede juzgar por sí mismo si es útil en este caso. El punto es guardar una variable php (si es una matriz, una cadena o un objeto) en un archivo.

<?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
Author: jpcrevoisier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-19 21:17:45

Si desea un resultado legible por humanos, también puede usar este tipo de archivo:

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

De esta manera, solo tiene un archivo, puede depurarlo (y corregirlo manualmente) fácilmente, puede agregar campos más tarde (al final de cada línea) y el código PHP es simple (para cada línea, dividir de acuerdo a |).

Sin embargo, los inconvenientes son que debe analizar todo el archivo para buscar algo (si tiene millones de entradas, no está bien) y debe manejar el separador en los datos (por ejemplo, si el nick es WaR / ordz).

 6
Author: ofaurax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 07:51:30

Este es inspirador como una solución práctica:
https://github.com/mhgolkar/FlatFire
Utiliza múltiples estrategias para el manejo de datos...
[Copiado del archivo Readme]

Libre o Estructurado o Mixto

- 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
Author: omran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-02 13:57:13

En mi humilde opinión, tienes dos opciones si quieres evitar homebrewing algo:

  1. SQLite

    Si está familiarizado con PDO, puede instalar un controlador PDO que admita SQLite. Nunca lo usé, pero he usado PDO una tonelada con MySQL. Voy a darle una oportunidad a este proyecto actual.

  2. XML

    Hecho esto muchas veces para cantidades relativamente pequeñas de datos. XmlReader es una clase ligera, de lectura directa y estilo cursor. SimpleXML facilita la lectura de un documento XML en un objeto al que puede acceder como cualquier otra instancia de clase.

 6
Author: siliconrockstar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-24 17:50:22

Simplemente señalando un problema potencial con una base de datos de archivos planos con este tipo de sistema:

data|some text|more data

row 2 data|bla hbalh|more data

...etc

El problema es que los datos de la celda contienen un "|" o un "\n", entonces los datos se perderán. A veces sería más fácil dividir por combinaciones de letras que la mayoría de la gente no usaría.

Por ejemplo:

Divisor de columna: #$% (Shift+345)

Divisor de filas: ^&* (Shift+678)

Archivo de texto: test data#$%blah blah#$%^&*new row#$%new row data 2

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

O cualquier cosa a lo largo de estos alinear. Además, podría agregar que las bases de datos de archivos planos son buenas para sistemas con pequeñas cantidades de datos (es decir. menos de 20 filas), pero se convierten en enormes cerdos de memoria para bases de datos más grandes.

 4
Author: Michael Burt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-04 00:39:53