在PHP中创建平面文件数据库结构的最佳实践是什么?
我看到的很多比较成熟的PHP平面文件框架试图实现类似SQL的查询语法,在大多数情况下,我的目的是超出顶部(我当时只使用数据库)。 / p>
是否有任何优雅的技巧可以通过较小的代码开销获得良好的性能和功能?
答案 0 :(得分:73)
那么,平面数据库的本质是什么。它们是大还是小。它是带有数组的简单数组吗?如果它的简单说明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));
}
但是这种实现方式将根据您所需的数据库的应用程序和性质而有所不同。
答案 1 :(得分:48)
您可以考虑SQLite。它几乎和平面文件一样简单,但你确实得到了一个用于查询的SQL引擎。它也是works well with PHP。
答案 2 :(得分:21)
在我看来,使用“平面文件数据库”就意味着你的意思(以及你接受的答案)并不是最好的方法。首先,如果有人进入并编辑文件,使用serialize()
和unserialize()
可能会导致严重的问题(事实上,他们可以在每次运行“数据库”中放置arbritrary代码。)
就个人而言,我会说 - 为什么不展望未来?有很多次我遇到过问题,因为我一直在创建自己的“专有”文件,而且项目已爆炸到需要数据库的程度,我想“你知道,我希望我为数据库编写了这个“以” - 因为代码的重构需要花费太多的时间和精力。
从这一点开始,我已经了解了我的应用程序的未来证明,当它变得更大时,我不必花费数天时间来进行重构是前进的方法。我该怎么做?
SQLite的。它作为一个数据库,使用SQL,并且很容易转换到mySQL(特别是如果你像我一样使用抽象类进行数据库操作!)
事实上,特别是“接受答案”的方法,它可以大大减少你的应用程序的内存使用量(你不必将所有“记录”加载到PHP中)
答案 3 :(得分:12)
我正在考虑的一个框架是博客平台。由于几乎任何可能的数据视图都按日期排序,我正在考虑这种结构:
每个内容节点一个目录:
./content/YYYYMMDDHHMMSS/
每个节点的子目录,包括
/tags
/authors
/comments
以及节点目录中的简单文本文件,用于预呈现和后期呈现的内容等。
这将允许简单的PHP glob()
调用(可能是结果数组的反转)来查询内容结构中的任何内容:
glob("content/*/tags/funny");
将返回路径,包括标记为“funny”的所有文章。
答案 4 :(得分:10)
以下是我们用于Lilina的代码:
<?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);
}
}
?>
它将每个条目存储为一个单独的文件,我们发现它的使用效率足够高(没有加载不需要的数据,保存起来更快)。
答案 5 :(得分:9)
如果您要使用平面文件来保存数据,请使用XML来构建数据。 PHP有一个built-in XML parser。
答案 6 :(得分:8)
我编写了两个简单的函数,用于将数据存储在文件中。如果它在这种情况下有用,你可以自己判断。 关键是将一个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;
}
答案 7 :(得分:7)
如果您想要人类可读的结果,您也可以使用这种类型的文件:
ofaurax|27|male|something|
another|24|unknown||
...
这样,你只有一个文件,你可以轻松调试它(并手动修复),你可以稍后添加字段(在每一行的末尾),PHP代码很简单(对于每一行,根据|)
然而,缺点是您应该解析整个文件以搜索某些内容(如果您有数百万条目,那就没有问题)并且您应该处理数据中的分隔符(例如,如果缺口是WaR | ordz)。
答案 8 :(得分:7)
恕我直言,如果你想避免自制东西,你有两个选择:
答案 9 :(得分:7)
这是一个鼓舞人心的实用解决方案:
https://github.com/mhgolkar/FlatFire
它使用多种策略来处理数据...
[从自述文件复制]
- 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
答案 10 :(得分: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行),但对于大型数据库来说却是巨大的内存占用。