平面文件数据库

在PHP中创建平面文件数据库结构的最佳做法是什么?我看到的很多更成熟的PHP平面文件框架试图实现类似于SQL的查询语法,这在大多数情况下(我只是在那个时候使用数据库)在我的目的中处于顶端。

有没有什么优雅的技巧可以让一个小代码的开销获得良好的性能和功能,首先要解决这个问题?

0
额外 编辑
意见: 11
我想补充一点,这里有一个用于平面文件数据库的软件包 github.com/tmarois/Filebase 我知道这是一个老问题,但是这个软件包是最新的版本和维护版本,加上全部功能是最容易忽视的。
额外 作者 timothymarois,

12 答案

我正在考虑的一个框架将用于博客平台。由于几乎所有可能的数据视图都会按日期排序,所以我在考虑这种结构:

每个内容节点一个目录:

./content/YYYYMMDDHHMMSS/

每个节点的子目录包括

/tags  
/authors  
/comments  

以及节点目录中的简单文本文件以用于预渲染内容和后渲染内容等。

这将允许调用一个简单的PHP glob() (并且可能会颠倒结果数组)可以查询内容结构中的任何内容:

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

会返回路径,包括标记为“有趣”的所有文章。

0
额外

确实如此。 serialize()对于这个也是非常有用的。

我认为,招想出一个可行的制度是找到某种方式来索引数据节点而不复杂杀死自己。

0
额外

那么,扁平数据库的性质是什么?他们是大还是小?它是简单的数组与阵列在他们?如果它简单的说userprofiles是这样构建的:

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

并为该用户保存或更新 db记录

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

但是这个实现会根据您需要的数据库的应用和性质而有所不同。

0
额外

您可以考虑 SQLite 。它几乎与平面文件一样简单,但您确实会得到一个用于查询的SQL引擎。它也可以与PHP兼容

0
额外
默认情况下,SQLite被构建为5.0+,但是从PHP 5.4+开始优化(!)!当我在2012年7月写这篇文章时,默认情况下,SQLite将不再适用于最新的系统。官方声明此处
额外 作者 Sliq,
如果您有服务器访问权限,安装SQLite PDO驱动程序非常简单。在运行Apache2的Ubuntu / Debian上,只需执行apt-get install php5-sqlite service apache2 restart
额外 作者 siliconrockstar,
在对@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的延伸,但这个版本只推荐用于实验。”
额外 作者 Paul van Leeuwen,

恕我直言,你有两个选择,如果你想避免homebrewing的东西:

  1. SQLite

    If you're familiar with PDO, you can install a PDO driver that supports SQLite. Never used it, but I have used PDO a ton with MySQL. I'm going to give this a shot on a current project.

  2. XML

    Done this many times for relatively small amounts of data. XMLReader is a lightweight, read-forward, cursor-style class. SimpleXML makes it simple to read an xml document into an object that you can access just like any other class instance.

0
额外

如果你想要一个可读的结果,你也可以使用这种类型的文件:

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

这样,您只有一个文件,您可以轻松地调试(并手动修复),您可以稍后(在每行的末尾)添加字段,并且PHP代码很简单(对于每行,根据|分割)。

然而,缺点是你应该解析整个文件来搜索一些东西(如果你有几百万条记录,那就不好),你应该在数据中处理分隔符(例如,如果缺口是WaR | ordz)。

0
额外

如果您打算使用平面文件来保存数据,请使用XML来构造数据。 PHP有一个内置的XML解析器

0
额外
并遵循人类可读性的xml规则,或者您可以使用序列化或json或其他。
额外 作者 Ben,

在我看来,在某种意义上使用“平面文件数据库”(以及您接受的答案)并不是解决问题的最佳方式。首先,如果有人进入并编辑文件,使用 serialize()unserialize()可能会导致MAJOR头痛(事实上,他们可以将arbritrary代码放入您的“数据库”每次运行。)

就个人而言,我会说 - 为什么不展望未来?曾经有过很多次我遇到过问题,因为我一直在创建自己的“专有”文件,而且项目已经爆炸到需要数据库的地步,我想“你知道,我希望我为数据库编写了这个代码“,因为代码的重构花费了太多的时间和精力。

从这里我已经了解到,我的应用程序的未来证明,以便当它变得更大时,我不必去,并花费重构的日子是前进的方式。我该怎么做呢?

SQLite的。它用作数据库,使用SQL,并且很容易转换到mySQL(特别是如果您像我一样使用抽象类进行数据库操作!)

事实上,特别是使用“接受的答案”的方法,它可以大幅度降低应用程序的内存使用量(您不必将所有“记录”加载到PHP中)

0
额外

以下是我们用于Lilina的代码:

<?php
/**
 * Handler for persistent data files
 *
 * @author Ryan McCue 
 * @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);
    }
}

?>

它将每个条目存储为单独的文件,我们发现该文件的使用效率足够高(不需要加载不必要的数据,而且保存起来更快)。

0
额外

我写了两个简单的函数,用于将数据存储在文件中。你可以自己判断这种情况是否有用。 重点是将一个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;
}
0
额外
我发现这很有趣,这是更好的方式,因为我们只是将格式化的数组转储到文件中。我们不需要再构建它,只需读入。另外,编辑变量有点简单。我永远不会使用它来存储大量数据,但是我发现在没有数据库的情况下存储程序模块非常实用。谢谢。
额外 作者 erm3nda,
这是一个四年的问题,有一个可接受的答案和许多其他答案。考虑把重点放在较新的问题上,除非这里接受的答案显然是错误的或不足的。
额外 作者 mcknz,

只需用这种类型的系统指出平面文件数据库的潜在问题:

data|some text|more data

row 2 data|bla hbalh|more data

...等等

问题在于单元格数据包含“|”或“\ n”,那么数据将会丢失。有时,通过大多数人不会使用的字母组合来拆分会更容易。

例如:

Column splitter: #$% (Shift+345)

Row splitter: ^&* (Shift+678)

Text file: test data#$%blah blah#$%^&*new row#$%new row data 2

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

或者沿着这些线路。另外,我还可以补充说,平面文件数据库对于具有少量数据(即少于20行)的系统是有利的,但是对于较大型的数据库而言却变成了巨大的内存消耗。

0
额外
好点。更进一步,PHP可以很容易地序列化JSON。转义输入非常简单,因此您不需要使用有趣的字符串组合,因此文件更具可读性。
额外 作者 Cypher,

This one is inspiring as a practical solution:
https://github.com/mhgolkar/FlatFire
It uses multiple strategies to handling data...
[Copied from Readme File]

免费或结构化或混合

- 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
0
额外