Bancos de dados de arquivo simples [fechado]

120

Quais são as práticas recomendadas para criar estruturas de banco de dados de arquivo simples em PHP?

Muitas estruturas de arquivo simples de PHP mais amadurecidas por aí, nas quais tento implementar sintaxe de consulta semelhante a SQL, que é exagerada para meus propósitos na maioria dos casos. (Eu usaria apenas um banco de dados nesse ponto).

Existe algum truque elegante para obter bom desempenho e recursos com uma pequena sobrecarga de código?

saint_groceon
fonte
1
Eu gostaria de acrescentar que existe um pacote aqui para o Flat File Database github.com/tmarois/Filebase Eu sei que esta é uma questão antiga, mas este pacote é o mais recente construído e mantido, além de cheio de recursos que a maioria negligencia em incluir .
tmarois
Estou desenvolvendo um CMS e uso um banco de dados de texto de arquivo de texto simples. Demorou muitas horas para fazer e muitas horas para refratar, mas funciona perfeitamente. As consultas serão realizadas muito mais rápido com um banco de dados totalmente indexado e otimizado. No entanto, evito a necessidade de consultas armazenando metadados e com organização e estrutura cuidadosas. Quando preciso de dados, consigo sem um for loop(a menos que esteja usando todos os dados da pasta), portanto, o desempenho é muito mais rápido do que um banco de dados. Eu entraria em detalhes e daria uma resposta muito boa, mas infelizmente esta questão está encerrada.
Dan Bray

Respostas:

75

Bem, qual é a natureza dos bancos de dados planos. Eles são grandes ou pequenos. São arrays simples com arrays neles? se for algo simples, diga perfis de usuário construídos como tal:

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

e para salvar ou atualizar o registro db desse usuário.

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

e carregar o registro para o usuário

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

mas, novamente, essa implementação irá variar de acordo com a aplicação e a natureza do banco de dados de que você precisa.

w-ll
fonte
48

Você pode considerar o SQLite . É quase tão simples quanto arquivos simples, mas você obtém um mecanismo SQL para consultas. Também funciona bem com PHP .

yukondude
fonte
6
O SQLite foi compilado no 5.0+ por padrão, mas com desconto (!) Do PHP 5.4+ em diante !!! Enquanto escrevo isso em julho de 2012, o SQLite não funcionará mais em sistemas atualizados por padrão. Declaração oficial aqui
Sliq
A instalação do driver SQLite PDO é bastante trivial se você tiver acesso ao servidor. No Ubuntu / Debian executando Apache2, basta fazer apt-get install php5-sqlite service apache2 restart
siliconrockstar
4
Em reação ao comentário de @Sliq, declarar que "SQLite foi ... descontinuado" é meio que verdadeiro: a extensão chamada "SQLite" foi descontinuada e "SQLite3" agora está habilitado por padrão. php.net/manual/en/sqlite.installation.php "Desde o PHP 5.0 esta extensão foi empacotada com o PHP. Começando com o PHP 5.4, esta extensão está disponível apenas via PECL." php.net/manual/en/sqlite3.installation.php "A extensão SQLite3 é habilitada por padrão a partir do PHP 5.3.0." "Esta extensão foi brevemente uma extensão PECL, mas essa versão é recomendada apenas para uso experimental."
Paul van Leeuwen
Você não respondeu à pergunta
JG Estiot
20

Em minha opinião, usar um "Banco de Dados de Arquivo Simples" no sentido que você quer (e a resposta que você aceitou) não é necessariamente a melhor maneira de fazer as coisas. Em primeiro lugar, usar serialize()e unserialize()pode causar grandes dores de cabeça se alguém entrar e editar o arquivo (eles podem, de fato, colocar código arbitrário em seu "banco de dados" para ser executado a cada vez.)

Pessoalmente, eu diria - por que não olhar para o futuro? Muitas vezes eu tive problemas porque tenho criado meus próprios arquivos "proprietários" e o projeto explodiu a ponto de precisar de um banco de dados, e estou pensando "sabe, gostaria Para começar, escrevi isso para um banco de dados "- porque a refatoração do código exige muito tempo e esforço.

Com isso, aprendi que fazer uma revisão futura de meu aplicativo para que, quando ele ficar maior, eu não precise ficar dias refatorando, é o caminho a seguir. Como eu faço isso?

SQLite. Ele funciona como um banco de dados, usa SQL e é muito fácil de mudar para mySQL (especialmente se você estiver usando classes abstratas para manipulação de banco de dados como eu!)

Na verdade, especialmente com o método da "resposta aceita", ele pode reduzir drasticamente o uso de memória do seu aplicativo (você não precisa carregar todos os "RECORDS" no PHP)

Mez
fonte
Isso é verdade. serialize()pode ser muito útil para isso também. Acho que o truque para criar um sistema viável é encontrar uma maneira de indexar os nós de dados sem se matar de complexidade.
saint_groceon 01 de
12

Uma estrutura que estou considerando seria para uma plataforma de blog. Como praticamente qualquer visualização possível de dados que você deseja seria classificada por data, eu estava pensando sobre esta estrutura:

Um diretório por nó de conteúdo:

./content/YYYYMMDDHHMMSS/

Subdiretórios de cada nó, incluindo

/tags  
/authors  
/comments  

Bem como arquivos de texto simples no diretório do nó para conteúdo pré e pós-renderizado e similares.

Isso permitiria uma glob()chamada simples de PHP (e provavelmente uma reversão da matriz de resultado) para consultar praticamente qualquer coisa dentro da estrutura do conteúdo:

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

Retornaria caminhos incluindo todos os artigos marcados como "engraçados".

saint_groceon
fonte
9

Este é o 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);
    }
}

?>

Ele armazena cada entrada como um arquivo separado, que descobrimos ser eficiente o suficiente para uso (nenhum dado desnecessário é carregado e é mais rápido para salvar).

Ryan McCue
fonte
8

Se você for usar um arquivo simples para persistir os dados, use XML para estruturar os dados. PHP possui um analisador XML embutido .

Jason
fonte
E siga as regras de xml de legibilidade humana ou você também pode usar serialização ou json ou algo assim.
Ben
Conselho muito ruim. XML nunca deve ser usado. É uma grande aberração.
JG Estiot
@JGEstiot Care para explicar melhor?
UncaughtTypeError
7

Se você quiser um resultado legível por humanos, também pode usar este tipo de arquivo:

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

Desta forma, você tem apenas um arquivo, pode depurá-lo (e consertar manualmente) facilmente, pode adicionar campos posteriormente (no final de cada linha) e o código PHP é simples (para cada linha, dividido de acordo com |).

No entanto, a desvantagem é que você deve analisar o arquivo inteiro para pesquisar algo (se você tiver milhões de entradas, não está tudo bem) e deve lidar com o separador de dados (por exemplo, se o nick for WaR | ordz).

Ofaurax
fonte
7

Eu escrevi duas funções simples projetadas para armazenar dados em um arquivo. Você pode julgar por si mesmo se isso é útil neste caso. O objetivo é salvar uma variável php (se for um array, uma string ou um objeto) em um arquivo.

<?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
fonte
Achei isso interessante e essa é a maneira MELHOR, porque simplesmente despejamos o array formatado em um arquivo. Não precisamos construí-lo novamente, apenas leia. Além disso, editar variáveis ​​é um pouco fácil. Eu nunca vou usar isso para armazenar grandes dados, mas achei prático armazenar módulos do programa sem banco de dados. Obrigado.
m3nda
7

Este é inspirador como uma solução prática:
https://github.com/mhgolkar/FlatFire
Ele usa várias estratégias para lidar com dados ...
[Copiado do arquivo Leiame]

Livre ou estruturado ou misto

- 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
fonte
7

IMHO, você tem duas opções se quiser evitar algo caseiro:

  1. SQLite

    Se estiver familiarizado com o PDO, você pode instalar um driver PDO que suporte SQLite. Nunca usei, mas tenho usado uma tonelada de PDO com MySQL. Vou dar uma chance a um projeto atual.

  2. XML

    Feito isso muitas vezes para quantidades relativamente pequenas de dados. XMLReader é uma classe leve, com estilo de cursor para leitura. SimpleXML torna simples ler um documento XML em um objeto que você pode acessar como qualquer outra instância de classe.

siliconrockstar
fonte
5

Apenas apontando um problema potencial com um banco de dados de arquivo simples com este tipo de sistema:

data|some text|more data

row 2 data|bla hbalh|more data

... etc

O problema é que os dados da célula contêm um "|" ou um "\ n" então os dados serão perdidos. Às vezes, seria mais fácil dividir por combinações de letras que a maioria das pessoas não usaria.

Por exemplo:

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

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

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

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

Ou qualquer coisa nesse sentido. Além disso, devo acrescentar que os bancos de dados de arquivo simples são bons para sistemas com pequenas quantidades de dados (ou seja, menos de 20 linhas), mas tornam-se grandes consumidores de memória para bancos de dados maiores.

Michael Burt
fonte
Bons pontos. Indo um passo adiante, o PHP pode serializar JSON com muita facilidade. A entrada de escape é muito mais simples, então você não precisa usar combinações engraçadas de strings para que o arquivo seja mais legível.
Cypher