JavaScript
- 2008/04/21 21:36
- カテゴリー:制作, プログラム
- タグ:JavaScript
よーし、基礎から勉強しなおすぞー!
流行のライブラリも扱えるようになるぞー!
…と宣言してみるテスト。
気ままな雑記帳&実験場。好き勝手に色々やっています。
カテゴリー「プログラム」の検索結果は以下のとおりです。
よーし、基礎から勉強しなおすぞー!
流行のライブラリも扱えるようになるぞー!
…と宣言してみるテスト。
「MySQL、SQLite、ファイルに対応」の実装方法を考えているのですが、なかなか進まない…。
「同じ命令でデータを扱えるように、専用の命令を作成」ができればいいけど「プラグインで独自にデータを保持できるように」まで考えると、なかなか自分の頭ではまとめきれない…。
命令の仕様が複雑になっても嫌だし…。
だからといって、「単純にプログラム内で条件分岐する」でも、それはそれでプラグインを作るときの労力が大きいし…。
ひとまずデータベースは置いといて、ファイルのみに対応させるかなぁ…。その後、可能ならデータベースへの対応を考えるか、難しければデータベース対応版を別に作るか。
Web Diary Professional Ver4 の次期バージョンにあたるツールのメモ。
素直に Ver5 とはせずに、名前も変えて作る可能性が高いです。(開発言語もPHPに変えるし。)
また、Ver4 の配布は終了せずに、平行して開発するつもり。
以下、ツールのメモ。実現可能かどうかは考慮せずに「できたらいいな」を書いているだけ。完成日は不明。完成する日が来るかどうかも不明。(ぉ
思いついたら追記していきます。
プログラムを作る本人は、あまり絵を描けないというのが悲しい…。でも、管理するための仕組みを作るのは大好きなのです。
またもPHPでの排他処理について。ファイルに書き込みを行う場合は
$fp = fopen('data.txt', 'r+') or exit('error!');
flock($fp, LOCK_EX);
~ここで書き込みを行う~
flock($fp, LOCK_UN);
fclose($fp);
で問題ないと思ったけど、まだ駄目っぽい。ロックの解除に問題があり、ファイルの破損は完全に回避できないらしい。調べてみると色々出てきた。
fcloseの前にflock(ファイルポインタ, LOCK_UN) する人は実に多いのですが、これははっきりと間違いだと断言します。flockをfcloseの前に解除するということは、fcloseの前に他のプロセスが割り込む可能性が出るということです。ファイルへの書き込みは、fwriteとかfputsとかしてからfflushまたはfclose実行までのどこかで行われる、というのがファイル周りのI/Oの基本です。なので、fcloseもロックの範囲内に入れなければなりません。fcloseでflockが解除されるのはそういう理由があるのです。
PHP/ファイルロック/設計 - TestWiki - PukiWiki Plus!
プロセスレベルの書き込みバッファによって、書き込みデータがOSに渡されずに残っている可能性がある。
fflush($fp); flock($fp, LOCK_UN); fclose($fp);
で明示的にフラッシュするか、flock($fp,LOCK_UN)しないで
fclose($fp); // LOCK_UN してはならない
をすることで、バッファがフラッシュされた後、ロックが解除される。
ロックの解放には fclose() でを使用します (これは、スクリプトが終了した場合にも自動的にコールされます)。
公式サイトの解説は「ロックの解放には fclose() でを使用します」と書いている割に、サンプルコードでは flock($fp, LOCK_UN);
で解除しています。こんな風に書かれていたら、どっちでも大丈夫なのかと思ってしまうよ…。
公式サイトのサンプルコードは、あまり信用しないほうがいいのかなぁ…。公式サイトなのに。
ついでに書くと「でを使用します」って誤字ですよね…?
ともかく、ロックの解除は fclose()
にまかせて
$fp = fopen('data.txt', 'r+') or exit('error!');
flock($fp, LOCK_EX);
~ここで書き込みを行う~
fclose($fp);
が正しいっぽい。
PHPでデータベース対応のプログラムをいくつか作ってきましたが、データベースとファイルの両方に対応したプログラムを作成しようと考え中。PHP Labo のプログラムとは別に、1つ徹底的に高機能なプログラムを作りたいのです。
その際、データを扱う処理をどんなふうに書くか悩み中。
同じ命令でMySQLもSQLiteもファイルも扱えるような、有名どころのクラスがあればいいけど、どうも無いっぽい。うーん、プログラム内で単純に条件分岐して各処理を併記するかなぁ…。可読性が下がりそうで少し嫌だけど。
排他ロックを利用しつつ、どのタイミングでプログラムが強制終了しても、ファイルがクリアされないようにしてみた。
/* ファイルの先頭に追加 */
$fp = fopen('data.txt', 'r+') or exit('error!');
flock($fp, LOCK_EX);
$no = 0;
$data = '';
while ($line = fgets($fp)) {
if (intval($line) > $no) {
$no = intval($line);
}
$data .= $line;
}
$no++;
rewind($fp);
fwrite($fp, "$no\t" . date('H:i:s') . "\n$data");
flock($fp, LOCK_UN);
fclose($fp);
/* ファイルの末尾に追加 */
$fp = fopen('data.txt', 'a+') or exit('error!');
flock($fp, LOCK_EX);
$no = 0;
while ($line = fgets($fp)) {
if (intval($line) > $no) {
$no = intval($line);
}
}
$no++;
fwrite($fp, "$no\t" . date('H:i:s') . "\n");
flock($fp, LOCK_UN);
fclose($fp);
/* 任意の行を編集 */
$fp = fopen('data.txt', 'r+') or exit('error!');
flock($fp, LOCK_EX);
$no = 3; //No.3のデータを編集
$data = '';
while ($line = fgets($fp)) {
if (intval($line) == $no) {
$data .= "$no\t" . date('H:i:s') . "\n";
} else {
$data .= $line;
}
}
rewind($fp);
fwrite($fp, $data);
ftruncate($fp, ftell($fp));
flock($fp, LOCK_UN);
fclose($fp);
/* 任意の行を削除 */
$fp = fopen('data.txt', 'r+') or exit('error!');
flock($fp, LOCK_EX);
$no = 3; //No.3のデータを削除
$data = '';
while ($line = fgets($fp)) {
if (intval($line) != $no) {
$data .= $line;
}
}
rewind($fp);
fwrite($fp, $data);
ftruncate($fp, ftell($fp));
flock($fp, LOCK_UN);
fclose($fp);
色々読んでみて、'r+'
モードは「ファイルの内容が減る可能性がある」という場合には使えないのかと思ったけど、どうやらそうでもないっぽい。
そんな訳で改良版。↓
/* 任意の行を編集 */
$no = 3; //No.3のデータを編集
$data = '';
$fp = fopen('data.txt', 'r+') or exit('error!');
flock($fp, LOCK_EX);
while ($line = fgets($fp)) {
if (intval($line) == $no) {
$data .= "$no\t" . date('H:i:s') . "\n";
} else {
$data .= $line;
}
}
rewind($fp);
fwrite($fp, $data);
ftruncate($fp, ftell($fp));
flock($fp, LOCK_UN);
fclose($fp);
rewind()
とか ftell()
とか ftruncate()
でファイルポインタを操作してやれば、編集によってファイルサイズが減る場合でも対応できるみたい。
これなら素直に排他処理ができるし、どのタイミングでプログラムが強制終了してもデータが吹っ飛ぶことは無いみたいです。
また近々詳しく検証してみるとします。
この記事の続き。ファイル入出力の具体的なコード。
/* ファイルの先頭に追加 */
$no = 0;
$data = '';
$fp = fopen('data.txt', 'r') or exit('error!');
while ($line = fgets($fp)) {
if (intval($line) > $no) {
$no = intval($line);
}
$data .= $line;
}
fclose($fp);
$no++;
$fp = fopen('data.txt', 'w') or exit('error!');
fwrite($fp, "$no\t" . date('H:i:s') . "\n$data");
fclose($fp);
/* ファイルの末尾に追加 */
$no = 0;
$data = '';
$fp = fopen('data.txt', 'r') or exit('error!');
while ($line = fgets($fp)) {
if (intval($line) > $no) {
$no = intval($line);
}
$data .= $line;
}
fclose($fp);
$no++;
$fp = fopen('data.txt', 'w') or exit('error!');
fwrite($fp, "$data$no\t" . date('H:i:s') . "\n");
fclose($fp);
/* 任意の行を編集 */
$no = 3; //No.3のデータを編集
$data = '';
$fp = fopen('data.txt', 'r') or exit('error!');
while ($line = fgets($fp)) {
if (intval($line) == $no) {
$data .= "$no\t" . date('H:i:s') . "\n";
} else {
$data .= $line;
}
}
fclose($fp);
$fp = fopen('data.txt', 'w') or exit('error!');
fwrite($fp, $data);
fclose($fp);
/* 任意の行を削除 */
$no = 3; //No.3のデータを削除
$data = '';
$fp = fopen('data.txt', 'r') or exit('error!');
while ($line = fgets($fp)) {
if (intval($line) != $no) {
$data .= $line;
}
}
fclose($fp);
$fp = fopen('data.txt', 'w') or exit('error!');
fwrite($fp, $data);
fclose($fp);
でも、単にファイルの末尾にデータを追加し続ける場合は、'a+'
を使って、ロックも自前で行ったほうが圧倒的に効率が良さそう。(この場合は、一時的にデータを保存する変数のために、無駄に巨大なメモリを消費する必要は無いので。)
/* ファイルの末尾に追加 */
$fp = fopen('data.txt', 'a+') or exit('error!');
flock($fp, LOCK_EX);
$no = 0;
while ($line = fgets($fp)) {
if (intval($line) > $no) {
$no = intval($line);
}
}
$no++;
fwrite($fp, "$no\t" . date('H:i:s') . "\n");
flock($fp, LOCK_UN);
fclose($fp);
今のところ、こんな感じで。
ファイル入出力と排他処理のメモ。
主にこのあたりを読んでみて、結局のところどうやってロックするか。すごく今更ですが、PHPからはデータベースばかり使っていたので自分用にメモしておきます。あくまでもメモであって、上の内容のまとめとかでは無いです。ツッコミは歓迎。
data.txt
に、1行を1件として処理番号と処理時間を記録していくとする。具体的には以下のようなデータファイルを扱うとする。(<tab>
の部分はタブが入る。)
4<tab>17:02:08
3<tab>16:53:20
2<tab>16:43:03
1<tab>16:42:02
データの破損を避けるために、ファイル入出力の際はロック処理を行う。
$fp = fopen('data.txt', 'r+') or exit('error!');
flock($fp, LOCK_EX); //有効なロック
fwrite($fp, '追記したいデータ');
flock($fp, LOCK_UN);
fclose($fp);
ただし、'r+'
だとファイルの内容がクリアされないので、文章を編集したり一行削除したりする場合には利用できない(以前の内容がファイル内に残ってしまう可能性がある。)
つまり「ファイルサイズが増えることがあっても減ることはない」という場合は 'r+'
でのロックは有効だが、「ファイルの内容が減る可能性がある」という場合には使えない。
このような場合には 'w'
でオープンすると、以前の内容がクリアされる。ただし、ロック方法に注意する。
$fp = fopen('data.txt', 'w') or exit('error!');
flock($fp, LOCK_EX); //fopenの時点でファイルがクリアされるので、このロックは無意味
fwrite($fp, '書き込みたいデータ');
flock($fp, LOCK_UN);
fclose($fp);
この処理はロックができていないけど、ロック処理の例としてこのコードが紹介されていることは多い気がする…。
で、'w'
を使う場合の、定番のロック方法は無いっぽい。
処理方法を色々変えるのもヤヤコシイので、入出力を行うファイル自体にはロックを行わず、ロック専用ファイルを用意した方が良さそう。
以下の自作関数で、ファイルロックを行う。
function file_lock() {
$fp = fopen('lock.txt', 'w') or exit('error!');
flock($fp, LOCK_EX);
return $fp;
}
function file_unlock($fp) {
flock($fp, LOCK_UN);
fclose($fp);
}
file_lock()
でロックを行い、file_unlock()
でロックを解除する。つまり、ファイルロック用に lock.txt
を用意し、このファイルに排他処理を行う。実際に読み書きするファイルにはロックを行わない。(排他ロック中は他のプロセスが割り込めないので、ロックは1つかければ十分。)
具体的には以下のような流れで処理する。
$lp = file_lock();
~ここでファイルの読み書き~
file_unlock($lp);
ファイル入出力を行う際は「ファイルの内容を読み込みモードで開き、一旦変数に読み出す。その後ファイルを書き込みモードで開き、一気に書き込む」に統一してみる。
続く。
この記事の続き。数日前から自作のデータベース管理ツールを使っています。基本的には phpminiadmin 以上 SQLiteManager 未満くらいの機能ですが、なかなか便利に使えています。(自分用に作ったから当然か。) スクリーンショットはこんな感じ。↓
phpMyAdmin や SQLiteManager を使い慣れている方には役不足なツールですが、「データを少し編集したい」という場合には便利かもしれません。一応さらしておきますので、よければどうぞ。
以下、簡単に特徴を記載しておきます。
ライセンスはGPL。サポート無しですが、不具合はこっそり教えてくれると嬉しいです。「このツールを使ったらデータが全部消えた」とか言われても責任は持てませんので、使用する前にバックアップはとっておきましょう。(^^;