PHPでフラットファイルデータベース構造を作成する際のベストプラクティスは何ですか?
多くのより成熟したPHPフラットファイルフレームワークがあり、SQLのようなクエリ構文を実装しようとしています。これは、ほとんどの場合、私の目的では上回っています。(その時点でデータベースを使用します)。
小さなコードオーバーヘッドで優れたパフォーマンスと機能を実現するためのエレガントなトリックはありますか?
さて、フラットデータベースの性質は何ですか。それらは大きいか小さいか。配列を含む単純な配列ですか?そのように構築されたユーザープロファイルと言う単純なものの場合:
$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));
}
ただし、この実装は、アプリケーションと必要なデータベースの性質によって異なります。
私の意見では、あなたが意味している意味で(そしてあなたが受け入れた答えで)「フラットファイルデータベース」を使用することは、必ずしも最善の方法ではありません。まず第一に、 and を使用するserialize()
とunserialize()
、誰かがファイルに侵入して編集した場合に大きな頭痛の種になる可能性があります (実際、彼らはあなたの「データベース」に任意のコードを入れて、毎回実行することができます)。
個人的には、未来に目を向けてみませんか?独自の「専有」ファイルを作成していたため、問題が発生したことが何度もあり、プロジェクトはデータベースが必要なところまで爆発しました。これは、最初からデータベース用に書いたものです」 - コードのリファクタリングには時間と労力がかかりすぎるためです。
このことから、アプリケーションが大きくなったときにリファクタリングに何日も費やす必要がないように、アプリケーションの将来を保証することが前進する方法であることを学びました。どうすればいいですか?
SQLite。データベースとして機能し、SQL を使用し、MySQL に簡単に移行できます (特に、私のようにデータベース操作に抽象化されたクラスを使用している場合)。
実際、特に「受け入れられた回答」の方法では、アプリのメモリ使用量を大幅に削減できます (すべての「RECORDS」を PHP にロードする必要はありません)。
私が検討しているフレームワークの1つは、ブログプラットフォーム用です。必要なデータのほぼすべてのビューが日付でソートされるため、私はこの構造について考えていました。
コンテンツノードごとに1つのディレクトリ:
./content/YYYYMMDDHHMMSS/
を含む各ノードのサブディレクトリ
/tags
/authors
/comments
また、事前および事後レンダリングされたコンテンツなどのノードディレクトリ内の単純なテキストファイル。
これにより、単純なPHPglob()
呼び出し(およびおそらく結果配列の反転)で、コンテンツ構造内のほぼすべてのものを照会できるようになります。
glob("content/*/tags/funny");
「面白い」とタグ付けされたすべての記事を含むパスを返します。
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);
}
}
?>
各エントリを個別のファイルとして保存します。これは、使用するのに十分効率的であることがわかりました(不要なデータが読み込まれず、保存が高速です)。
私見ですが、自家醸造を避けたい場合は、2つ...えーと3つの選択肢があります。
PDOに精通している場合は、SQLiteをサポートするPDOドライバーをインストールできます。使用したことはありませんが、MySQLでPDOを1トン使用しました。これを現在のプロジェクトで試してみます。
比較的少量のデータに対してこれを何度も実行します。 XMLReaderは、軽量で先読みのカーソルスタイルのクラスです。 SimpleXMLを使用すると、他のクラスインスタンスと同じようにアクセスできるオブジェクトにXMLドキュメントを簡単に読み込むことができます。
少量のデータに適したオプションです。ファイルとjson_decode/json_encodeを読み取り/書き込みするだけです。ただし、PHPがJSONツリーをすべてメモリにロードせずにナビゲートする構造を提供しているかどうかはわかりません。
フラット ファイルを使用してデータを永続化する場合は、XML を使用してデータを構造化します。PHP にはXML パーサーが組み込まれています。
これは実用的なソリューションとして刺激的です:
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
ファイルにデータを格納するように設計された 2 つの単純な関数を作成しました。この場合に役立つかどうかは、自分で判断できます。ポイントは、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;
}
人間が読める結果が必要な場合は、次のタイプのファイルを使用することもできます。
ofaurax|27|male|something|
another|24|unknown||
...
このように、ファイルは 1 つしかなく、簡単にデバッグ (および手動で修正) でき、後で (各行の最後に) フィールドを追加でき、PHP コードは単純です (各行は | に従って分割されます)。
ただし、欠点は、ファイル全体を解析して何かを検索する必要があり (何百万ものエントリがある場合は問題ありません)、データ内のセパレーターを処理する必要があることです (たとえば、ニックネームが WaR|ordz の場合)。
このタイプのシステムでのフラット ファイル データベースの潜在的な問題を指摘するだけです。
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 行未満) のシステムには適していますが、大規模なデータベースでは大量のメモリを消費することも付け加えておきます。