在PHP中创建平面文件数据库结构的最佳实践是什么?
许多成熟的PHP平面文件框架在那里,我试图实现类似SQL的查询语法,在大多数情况下,对于我而言,这是最重要的。 (那时我只会使用数据库)。
是否有任何优雅的技巧可以在不增加代码开销的情况下获得良好的性能和功能?
好吧,平面数据库的本质是什么。他们是大还是小。它是其中包含数组的简单数组吗?如果很简单,则说用户配置文件是这样构建的:
1 2 3 4
| $user = array("name" =>"dubayou",
"age" => 20,
"websites" => array("dubayou.com","willwharton.com","codecream.com"),
"and_one" =>"more"); |
并保存或更新该用户的数据库记录。
1 2
| $dir ="../userdata/"; //make sure to put it bellow what the server can reach.
file_put_contents($dir.$user['name'],serialize($user)); |
并为用户加载记录
1 2 3
| function &get_user($name){
return unserialize(file_get_contents("../userdata/".$name));
} |
但是同样,此实现也会因您需要的数据库的应用程序和性质而异。
您可能会考虑使用SQLite。它几乎和平面文件一样简单,但是您确实获得了用于查询的SQL引擎。它也适用于PHP。
我认为,在某种意义上使用"平面文件数据库"(以及您已经接受的答案)不一定是解决问题的最佳方法。首先,如果有人进入并编辑文件,使用serialize()和unserialize()可能会引起严重的麻烦(实际上,他们可以在每次运行的"数据库"中放入任意代码)。
我个人想说-为什么不展望未来?因为我一直在创建自己的"专有"文件,所以出现了很多次问题,并且该项目已经发展到需要数据库的地步,我在想"您知道,我希望我将其写成数据库的开头",因为代码的重构花费了太多的时间和精力。
由此我了解到,将来可以对我的应用程序进行验证,这样,当应用程序变得更大时,我就不必花很多时间进行重构。我该怎么做呢?
SQLite。它可以作为数据库使用SQL,并且很容易转换为mySQL(特别是如果您像我一样使用抽象类进行数据库操作!)
实际上,尤其是使用"可接受的答案"的方法,它可以大大减少应用程序的内存使用(您不必将所有" RECORDS"都加载到PHP中)
我正在考虑的一个框架是博客平台。由于几乎所有想要的数据视图都将按日期排序,因此我在考虑以下结构:
每个内容节点一个目录:
1
| ./content/YYYYMMDDHHMMSS/ |
每个节点的子目录包括
1 2 3
| /tags
/authors
/comments |
以及节点目录中用于预渲染和后渲染内容等的简单文本文件。
这将允许简单的PHP glob()调用(可能是结果数组的反转)来查询内容结构中的几乎所有内容:
1
| glob("content/*/tags/funny"); |
将返回路径,包括所有标记为"有趣"的文章。
这是我们用于Lilina的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| <?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);
}
}
?> |
它将每个条目存储为一个单独的文件,我们发现该文件足够有效地使用(不会加载不需要的数据,并且保存起来更快)。
如果要使用平面文件来保存数据,请使用XML来结构化数据。 PHP具有内置的XML解析器。
这是一个令人鼓舞的实用解决方案:
https://github.com/mhgolkar/FlatFire
它使用多种策略来处理数据...
[从自述文件复制]
自由或结构化或混合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| - 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 |
我编写了两个简单的函数,旨在将数据存储在文件中。您可以自己判断在这种情况下是否有用。
关键是将php变量(如果是数组,字符串或对象)保存到文件中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <?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'."
\$".$varname.'='.var_export($var, true).";
");
return true;
} |
恕我直言,如果您想避免自带东西,您有两种选择:
SQLite的
如果您熟悉PDO,则可以安装支持SQLite的PDO驱动程序。从未使用过它,但我在MySQL上大量使用了PDO。我将在当前项目中对此进行介绍。
XML格式
对于相对少量的数据,做了很多次。 XMLReader是一个轻量级的,可转发的,光标样式的类。 SimpleXML使将XML文档读入一个对象变得很简单,您可以像访问任何其他类实例一样访问该对象。
如果您希望获得人类可读的结果,也可以使用以下类型的文件:
1 2 3
| ofaurax|27|male|something|
another|24|unknown||
... |
这样,您只有一个文件,可以轻松调试(和手动修复),以后可以添加字段(在每一行的末尾),PHP代码也很简单(每一行,根据|拆分)。
但是,缺点是您应该解析整个文件以搜索某些内容(如果您有数百万个条目,那就不行了),并且应该处理数据中的分隔符(例如,如果昵称是WaR | ordz)。
只需指出这种系统的平面文件数据库可能存在的问题:
1 2 3
| 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行)的系统,但对于较大的数据库却成为巨大的内存消耗。