自分のための次世代CMS製作メモ
Jeans & Development | 電子ブロック工房 | 三日坊主 | フロントページ |
coreを基底クラスに? [General]
2008年2月17日
coreクラスは、config.phpをインクルードしたときに一番初めに定義されるクラスだ。今、このcoreクラスをすべてのクラスの基底クラスにするかどうかで悩んでいる。
例えば、プラグインで文字出力を行う場合、現在の仕様では
のように記述するが、coreクラスを基底クラスとしてプラグインクラスを作成すると、
のように記述してもよいし、
のように記述することもできる。PHP5ではメソッドのオーバーライドができないように設定できるから、プラグインでpメソッドやechohtmlメソッドを書き換えて変なことをするのも禁止できる。
例えば、プラグインで文字出力を行う場合、現在の仕様では
core::p($text); core:echohtml('<p>id: <%1%></p>',$id);
のように記述するが、coreクラスを基底クラスとしてプラグインクラスを作成すると、
$this->p($text); $this->echohtml('<p>id: <%1%></p>',$id);
のように記述してもよいし、
self::p($text);
のように記述することもできる。PHP5ではメソッドのオーバーライドができないように設定できるから、プラグインでpメソッドやechohtmlメソッドを書き換えて変なことをするのも禁止できる。
pluginoptionクラス [General]
2008年2月9日
プラグインオプションの値は、plugionoptionクラスのオブジェクトに保持させることにした。
pluginクラス内にて、
もしくは、
のように使う。
pluginクラス内にて、
$this->option=new pluginoption($this->id);
もしくは、
public function blogoption($bid){ $obj=new pluginoption($this->id,'blog',$bid); return $obj; }
のように使う。
プラグイン周りの仕様 [General]
2008年2月9日
現在のところ、コアではほとんどのコードでクラスのスタティックメソッドを利用するため、あまりオブジェクト指向とはいえないコードになっている。
一方で、プラグインの部分は、オブジェクト経由でアクセスする予定。理由は、次のとおり。
Nucleusと同じく、プラグインはすべてpluginクラスを継承したクラスとして記述することになる。プラグインのクラスをスタティックなメソッドの集合体として扱った場合、複数のプラグインのクラスが単一のpluginクラスを継承しているため、親クラスであるpluginクラスのそれぞれのメソッドで、各プラグインに固有の値(プラグインオプションなど)を処理できないのである。
一方で、Nucleusがやっているようにすべてオブジェクト経由で扱った場合、pluginクラスのメソッドでは$thisオブジェクトを経由することでこれを行うことができる(他方、コアの各クラスは、複数のインスタンスを持たせる必要が無いため、スタティックなメソッドやプロパティを扱うやり方で、今のところコードに混乱は無い)。
プラグインの仕様については、Nucleusのプラグインとほぼ同一のものを提供する予定である。が、もともとこのプロジェクトをはじめた動機のひとつとして、プラグインを書くときに楽をしたいというのもあるので、そうなるような仕様の変更を考えている。
現在のところ、どんなことで楽ができるかというと、
1)sql_table() 関数を使う必要が無い。
2)global $manager, $CONF, $DIR_LIBS; といった記述が不要。
3)echo htmlspecialchars($text,ENT_QUOTE,_CHARSET); の代わりに core::p($text); で良い。
4)3と同様だが、クエリーでaddslashes()やmysql_escape_string()なども使う必要が無い。
など。
一方で、プラグインの部分は、オブジェクト経由でアクセスする予定。理由は、次のとおり。
Nucleusと同じく、プラグインはすべてpluginクラスを継承したクラスとして記述することになる。プラグインのクラスをスタティックなメソッドの集合体として扱った場合、複数のプラグインのクラスが単一のpluginクラスを継承しているため、親クラスであるpluginクラスのそれぞれのメソッドで、各プラグインに固有の値(プラグインオプションなど)を処理できないのである。
一方で、Nucleusがやっているようにすべてオブジェクト経由で扱った場合、pluginクラスのメソッドでは$thisオブジェクトを経由することでこれを行うことができる(他方、コアの各クラスは、複数のインスタンスを持たせる必要が無いため、スタティックなメソッドやプロパティを扱うやり方で、今のところコードに混乱は無い)。
プラグインの仕様については、Nucleusのプラグインとほぼ同一のものを提供する予定である。が、もともとこのプロジェクトをはじめた動機のひとつとして、プラグインを書くときに楽をしたいというのもあるので、そうなるような仕様の変更を考えている。
現在のところ、どんなことで楽ができるかというと、
1)sql_table() 関数を使う必要が無い。
2)global $manager, $CONF, $DIR_LIBS; といった記述が不要。
3)echo htmlspecialchars($text,ENT_QUOTE,_CHARSET); の代わりに core::p($text); で良い。
4)3と同様だが、クエリーでaddslashes()やmysql_escape_string()なども使う必要が無い。
など。
セキュリティー対策、あれこれ [Security]
2008年2月3日
先の記事もそうであるが、セキュリティー対策のためのコードをいくつか追加したので、改めてメモ。
1)SQLインジェクション対策
sql::query()メソッドで、簡易プリペアードステートメントを使う。MySQL5やPostgreSQLほど本格的なプリペアードステートメントではないが、この方法でほぼ100%、SQLインジェクションは防げる。SQLステートメントでフィールド部分を入力値にしたがって変更したい場合は、core::fill()メソッドを用いて正規表現でホワイトリスト形式で入力値をチェックしながら値を挿入する。
2)XSS対策
core::echohtml()メソッドで、プリペアードステートメント様の機能が使える。正規表現で入力値をチェックすることも可能。
3)リモートスクリプトインクルード対策
クラス名を適切に選んでおけば、PHPファイルのインクルードはコアが自動的に行ってくれる。include, require などのステートメントを使うことはほとんどないはず。
4)ディレクトリトラバーサル対策
file_exists()の代わりに、core::checkFile()メソッドを使用する(下のコードを参照)。
5)ヌルバイト攻撃対策
リクエストにヌルバイトがあると、コア呼び出し時に自動的に停止する(先の記事のコードを参照)。
1)SQLインジェクション対策
sql::query()メソッドで、簡易プリペアードステートメントを使う。MySQL5やPostgreSQLほど本格的なプリペアードステートメントではないが、この方法でほぼ100%、SQLインジェクションは防げる。SQLステートメントでフィールド部分を入力値にしたがって変更したい場合は、core::fill()メソッドを用いて正規表現でホワイトリスト形式で入力値をチェックしながら値を挿入する。
2)XSS対策
core::echohtml()メソッドで、プリペアードステートメント様の機能が使える。正規表現で入力値をチェックすることも可能。
3)リモートスクリプトインクルード対策
クラス名を適切に選んでおけば、PHPファイルのインクルードはコアが自動的に行ってくれる。include, require などのステートメントを使うことはほとんどないはず。
4)ディレクトリトラバーサル対策
file_exists()の代わりに、core::checkFile()メソッドを使用する(下のコードを参照)。
5)ヌルバイト攻撃対策
リクエストにヌルバイトがあると、コア呼び出し時に自動的に停止する(先の記事のコードを参照)。
リクエストに関する規約 [Regulations]
2008年2月3日
$_GET, $_POST, $_COOKIE 使用時の規約を定めることにした。こうすることで、少ないリソース量で有効なセキュリティ対策が行える。
1)キー名が『binary』で終わる場合以外は、ヌルバイトは使用禁止。
2)キー名が『id』で終わる場合は、数値のみを利用。
3)キー名が『path』で終わる場合、URLもしくはファイルなどのパスを指定できる。ただし、日本語使用不可。
4)キー名が『text』で終わる場合、ヌルバイト以外なら何でも使用可。
5)上のどれにも当てはまらない場合、英数字、ピリオド、ハイフン、アンダースコア、スラッシュおよび日本語のみが使用可能。
6)URIに『#』および2つ以上の『?』を含むものを禁止。
(080208 改定)
キー名が『binary』で終わる場合に、ヌルバイトを受け付けるようにした。また、6)の条件を追加した。
1)キー名が『binary』で終わる場合以外は、ヌルバイトは使用禁止。
2)キー名が『id』で終わる場合は、数値のみを利用。
3)キー名が『path』で終わる場合、URLもしくはファイルなどのパスを指定できる。ただし、日本語使用不可。
4)キー名が『text』で終わる場合、ヌルバイト以外なら何でも使用可。
5)上のどれにも当てはまらない場合、英数字、ピリオド、ハイフン、アンダースコア、スラッシュおよび日本語のみが使用可能。
6)URIに『#』および2つ以上の『?』を含むものを禁止。
(080208 改定)
キー名が『binary』で終わる場合に、ヌルバイトを受け付けるようにした。また、6)の条件を追加した。
sqlite_popen(`:memory:`) [General]
2008年1月28日
『sqlite_popen(':memory:')』を使うと、メモリ上にテーブルを構築できるらしい。
ひょっとしたら、何かのキャッシュに使えるかも?忘れないように、ここにメモ。
ただし、レンタルサーバなどでのセキュリティー上の問題が発生する可能性あり。要チェック。
<html><body><?php $db=sqlite_popen(':memory:'); @sqlite_query($db,'CREATE TABLE test(id INTEGER NOT NULL PRIMARY KEY, test TEXT)'); for($i=0;$i<3;$i++) sqlite_query($db,"INSERT INTO test(test) VALUES('".md5(rand())."')"); echo count(sqlite_array_query($db,'SELECT * FROM test')); //sqlite_close($db); ?></body></html>
ひょっとしたら、何かのキャッシュに使えるかも?忘れないように、ここにメモ。
ただし、レンタルサーバなどでのセキュリティー上の問題が発生する可能性あり。要チェック。
権限管理 [General]
2008年1月25日
スキン変数を実装していく段階で、ブログチームの管理部分に行き着いたのであるが、一からCMSを書くのであれば、さまざまな権限を別個に設定できるようにしたい。
今のところ、jeans_loginテーブルにstatusとフィールドを設けていて、ここで各メンバーのステータスを決定することにしている。どんなステータスがあるかは、
で取得できるはず。現在のところは『admin』ステータスだけであるが、『blogadmin』『poweruser』など、さまざまなステータスをここに設定できる。いろいろ考えた結果、権限管理にはもう一つテーブルを作るだけでよさそう。
今のところ、jeans_loginテーブルにstatusとフィールドを設けていて、ここで各メンバーのステータスを決定することにしている。どんなステータスがあるかは、
SELECT status FROM jeans_login GROUP BY status
で取得できるはず。現在のところは『admin』ステータスだけであるが、『blogadmin』『poweruser』など、さまざまなステータスをここに設定できる。いろいろ考えた結果、権限管理にはもう一つテーブルを作るだけでよさそう。
あるアイテムと前後のものを同時に取得 その2 [General]
2008年1月19日
4つの方法を用い、スピードを比較した。ここで用いているデータは、およそ250個のアイテムがすでに挿入されている、Nucleus-SQLiteのnucleus_itemテーブルを用いた。
方法1:すべてのアイテムのid,title,timeを一旦メモリに取り込んでおいて、その後に処理。
方法2:方法1と同じだが、sqlite_array_query を使用。
方法3:先の記事で一番最後に述べた方法。
方法4:クエリーを3回実行。一回目であるアイテム、2回目で前を、3回目で後を取得。
結果(それぞれの方法にかかった秒数で表示)
結局は、もっとも単純だと思われる4の方法が一番早かった。SQLのクエリーを複雑に記述する『技』におぼれる弊害が現れているということか。
方法1:すべてのアイテムのid,title,timeを一旦メモリに取り込んでおいて、その後に処理。
方法2:方法1と同じだが、sqlite_array_query を使用。
方法3:先の記事で一番最後に述べた方法。
方法4:クエリーを3回実行。一回目であるアイテム、2回目で前を、3回目で後を取得。
結果(それぞれの方法にかかった秒数で表示)
Method1: 0.018667936325073 Method2: 0.015633106231689 Method3: 0.0060269832611084 Method4: 0.00052618980407715
結局は、もっとも単純だと思われる4の方法が一番早かった。SQLのクエリーを複雑に記述する『技』におぼれる弊害が現れているということか。
あるアイテムと前後のものを同時に取得 [General]
2008年1月16日
表題のためのクエリ:
ただし、最新の記事もしくは最古の記事でこれを行うと、失敗する。
SELECT i.inumber as thisid, j.inumber as previd, k.inumber as nextid FROM nucleus_item as i,nucleus_item as j,nucleus_item as k WHERE i.inumber=10 AND j.itime<=i.itime AND i.itime<=k.itime AND NOT (i.inumber=j.inumber OR i.inumber=k.inumber) ORDER BY TIMEDIFF(j.itime,k.itime) DESC LIMIT 1クエリー実行 0.0021 秒
ただし、最新の記事もしくは最古の記事でこれを行うと、失敗する。