Базы данных в плоских файлах [закрыто]

120

Каковы лучшие практики создания структур баз данных с плоскими файлами в PHP?

Существует множество более зрелых фреймворков для плоских файлов PHP, которые я пытаюсь реализовать с синтаксисом запросов, подобным SQL, который в большинстве случаев является лучшим для моих целей. (Я бы просто использовал базу данных на этом этапе).

Есть ли какие-нибудь изящные приемы, позволяющие получить хорошую производительность и функции с небольшими затратами на код?

saint_groceon
источник
1
Я хотел бы добавить, что здесь есть пакет для базы данных плоских файлов github.com/tmarois/Filebase Я знаю, что это старый вопрос, но этот пакет является самой последней сборкой и поддерживается, а также полон функций, которые редко включают ,
tmarois
Я разрабатываю CMS и использую базу данных с обычным текстовым файлом. На изготовление и повторный перелом уходит много часов, но это работает отлично. Запросы будут выполняться намного быстрее с полностью проиндексированной и оптимизированной базой данных. Однако я избегаю запросов, сохраняя метаданные и тщательно организовывая и структурируя их. Когда мне нужны данные, я получаю их без for loop(если я не использую все данные в папке), поэтому он работает намного быстрее, чем база данных. Я бы подробно остановился и дал очень хороший ответ, но, к сожалению, этот вопрос закрыт.
Дэн Брэй

Ответы:

75

Ну какова природа плоских баз данных. Они большие или маленькие. Это простые массивы с массивами в них? если это что-то простое, скажем, профили пользователей, созданные как таковые:

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

и сохранить или обновить запись базы данных для этого пользователя.

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

и загрузить запись для пользователя

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

но, опять же, эта реализация будет зависеть от приложения и характера необходимой вам базы данных.

W-LL
источник
48

Вы можете рассмотреть SQLite . Это почти так же просто, как плоские файлы, но вы получаете механизм SQL для запросов. Он также хорошо работает с PHP .

yukondude
источник
6
SQLite был встроен в 5.0+ по умолчанию, но со скидкой (!), Начиная с PHP 5.4+ !!! На момент написания этой статьи в июле 2012 года SQLite по умолчанию больше не будет работать в современных системах. Официальное заявление здесь
Sliq
Установить драйвер SQLite PDO довольно просто, если у вас есть доступ к серверу. В Ubuntu / Debian, на котором запущен Apache2, просто выполните apt-get install php5-sqlite service apache2 restart
Siliconrockstar,
4
В ответ на комментарий от @Sliq утверждение, что «SQLite был ... прекращен», является своего рода правдой: расширение с именем «SQLite» было прекращено, а «SQLite3» теперь включен по умолчанию. php.net/manual/en/sqlite.installation.php «Начиная с PHP 5.0 это расширение было связано с PHP. Начиная с PHP 5.4, это расширение доступно только через PECL». php.net/manual/en/sqlite3.installation.php "Расширение SQLite3 включено по умолчанию, начиная с PHP 5.3.0." «Это расширение кратко было расширением PECL, но эта версия рекомендуется только для экспериментального использования».
Пол ван Левен,
Вы не ответили на вопрос
JG Estiot
20

На мой взгляд, использование «базы данных плоских файлов» в том смысле, в котором вы имеете в виду (и ответ, который вы приняли), не обязательно лучший способ действовать. Прежде всего, используя serialize()иunserialize() может вызвать БОЛЬШИЕ головные боли, если кто-то войдет и отредактирует файл (на самом деле, они могут помещать произвольный код в вашу «базу данных» для выполнения каждый раз).

Лично я бы сказал - почему бы не заглянуть в будущее? Было так много раз, что у меня были проблемы, потому что я создавал свои собственные «проприетарные» файлы, и проект взорвался до такой степени, что ему нужна база данных, и я думаю: «Вы знаете, я бы хотел Я написал это для начала "базы данных" - потому что рефакторинг кода требует слишком много времени и усилий.

Из этого я узнал, что будущая проверка моего приложения, так что когда оно станет больше, мне не придется тратить дни на рефакторинг, - это путь вперед. Как мне это сделать?

SQLite. Он работает как база данных, использует SQL и довольно легко перейти на mySQL (особенно, если вы используете абстрактные классы для управления базой данных, как я!)

Фактически, особенно с методом «принятого ответа», он может резко сократить использование памяти вашим приложением (вам не нужно загружать все «ЗАПИСИ» в PHP)

Mez
источник
Это правда. serialize()может быть очень полезным и для этого. Я думаю, что уловка для создания жизнеспособной системы заключается в том, чтобы найти способ индексировать узлы данных, не убивая себя сложностью.
saint_groceon 01
12

Одна из структур, которую я рассматриваю, - это платформа для ведения блогов. Поскольку практически любое возможное представление данных, которое вы хотели бы, было бы отсортировано по дате, я думал об этой структуре:

Один каталог на узел содержимого:

./content/YYYYMMDDHHMMSS/

Поддиректории каждого узла, включая

/tags  
/authors  
/comments  

А также простые текстовые файлы в каталоге узлов для контента до и после рендеринга и т.п.

Это позволило бы простому glob()вызову PHP (и, вероятно, обращению массива результатов) запрашивать практически все в структуре содержимого:

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

Вернет пути, включая все статьи с меткой "смешно".

saint_groceon
источник
9

Вот код, который мы используем для Лилины:

<?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);
    }
}

?>

Он хранит каждую запись в виде отдельного файла, который, как мы обнаружили, достаточно эффективен для использования (ненужные данные не загружаются и их быстрее сохранять).

Райан МакКью
источник
8

Если вы собираетесь использовать плоский файл для сохранения данных, используйте XML для структурирования данных. PHP имеет встроенный анализатор XML .

Джейсон
источник
И следуйте правилам XML для удобочитаемости, иначе вы можете использовать сериализацию, json или что-то еще.
Бен
Очень плохой совет. XML никогда не следует использовать. Это жирная аберрация.
JG Estiot
@JGEstiot Хотите объяснить дальше?
UncaughtTypeError
7

Если вы хотите, чтобы результат был удобочитаемым, вы также можете использовать этот тип файла:

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

Таким образом, у вас есть только один файл, вы можете легко отлаживать его (и вручную исправлять), вы можете добавлять поля позже (в конце каждой строки), а код PHP прост (для каждой строки, разделенный в соответствии с |).

Однако недостатком является то, что вы должны анализировать весь файл для поиска чего-либо (если у вас есть миллионы записей, это не нормально), и вы должны обрабатывать разделитель в данных (например, если ник WaR | ordz).

ofaurax
источник
7

Я написал две простые функции, предназначенные для хранения данных в файле. Вы можете судить сами, пригодится ли это в данном случае. Дело в том, чтобы сохранить переменную php (если это либо массив, строка или объект) в файл.

<?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
источник
Мне это показалось интересным, и это ЛУЧШЕ, потому что мы просто выгружаем форматированный массив в файл. Строить заново не нужно, просто прочтите. Кроме того, редактировать переменные немного проще. Я никогда не буду использовать это для хранения больших данных, но я нашел практичным хранить программные модули без базы данных. Спасибо.
m3nda
7

Это вдохновляет как практическое решение:
https://github.com/mhgolkar/FlatFire.
Он использует несколько стратегий для обработки данных ...
[Скопировано из файла Readme]

Бесплатные, структурированные или смешанные

- 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
Омран
источник
7

ИМХО, у вас есть два варианта, если вы хотите избежать домашнего пивоварения:

  1. SQLite

    Если вы знакомы с PDO, вы можете установить драйвер PDO, поддерживающий SQLite. Никогда не использовал его, но я много использовал PDO с MySQL. Я собираюсь попробовать это в текущем проекте.

  2. XML

    Это делалось много раз для относительно небольших объемов данных. XMLReader - это легкий класс в стиле курсора с упреждающим чтением. SimpleXML упрощает чтение XML-документа в объект, к которому вы можете получить доступ, как и к любому другому экземпляру класса.

siliconrockstar
источник
5

Просто укажу на потенциальную проблему с базой данных плоских файлов с этим типом системы:

data|some text|more data

row 2 data|bla hbalh|more data

...и т.д

Проблема в том, что данные ячейки содержат символ "|" или "\ n", данные будут потеряны. Иногда было бы проще разделить на комбинации букв, которые большинство людей не использовали бы.

Например:

Разделитель колонки: #$% (Shift+345)

Разделитель строк: ^&* (Shift+678)

Текстовый файл: test data#$%blah blah#$%^&*new row#$%new row data 2

Затем используйте: explode("#$%", $data); use foreach, the explode again to separate columns

Или что-нибудь в этом роде. Кроме того, я мог бы добавить, что базы данных с плоскими файлами хороши для систем с небольшими объемами данных (например, менее 20 строк), но становятся огромными потребителями памяти для больших баз данных.

Майкл Берт
источник
Хорошие моменты. Сделав еще один шаг, PHP может очень легко сериализовать JSON. Экранирование ввода намного проще, поэтому вам не нужно использовать забавные комбинации строк, чтобы файл был более читабельным.
Cypher