複数Webサーバ、単一セッション管理でセッションかぶっちゃう問題ですが、調べたら5人日ぐらいかかりそう・・・とか思ってましたが、この間の勉強会で聞いたら、

「session_id()でセッションID設定できる。」

一瞬で解決しました。

解決策をまとめると、

「セッション維持してくれるロードバランサーを使う」 OR 「セッションIDにアドレスをくっ付ける」

となりました。
実装はこんな感じか!?

session_id(str_replace(".", "a", $_SERVER["SERVER_ADDR"]).session_id());

Mojavi2で使うためにIPでユニークなセッションIDを使うコンテナ、IpUniqueSessionContainerを作ってみました。

<?php
require_once (USER_DIR."SessionContainer.class.php");

class IpUniqueSessionContainer extends SessionContainer {
    function load(& $authenticated, & $attributes, & $secure) {
        if (ini_get("session.auto_start") != 1) session_start();
        session_id(str_replace(".", "a", $_SERVER["SERVER_ADDR"]).session_id());
        if (!isset($_SESSION["attributes"])) {
            $authenticated = false;
            $attributes = array();
            $secure = array();
        } else {
            $authenticated = $_SESSION["authenticated"];
            $attributes = $_SESSION["attributes"];
            $secure = $_SESSION["secure"];
        }
        $_SESSION["authenticated"] =& $authenticated;
        $_SESSION["attributes"] =& $attributes;
        $_SESSION["secure"] =& $secure;
    }
}
?>

IpUniqueSessionContainer-0.1.zip

index.phpのUSER CONTAINER SETTINGSのところでこんな感じで使う。

require_once(BASE_DIR."opt/user/IpUniqueSessionContainer.class.php");
$user =& $controller->getUser();
$user->setContainer(new IpUniqueSessionContainer());

ref: PHPのセッションID2

この間のおざきさんの話がすごい気になってたんですが、対策できました!

Mojaviのすべての基本となるSCRIPT_PATH定数をこんな感じで設定してやれば、この間つくったURIUtil::redirect()もSmartyプラグインのmojavi_uriも絶対URIを吐いてくれます。

/**
* An absolute web path to the index.php script.
*/
define('SCRIPT_PATH', strtolower(trim(array_shift(split("/", $_SERVER["SERVER_PROTOCOL"]))))."://".$_SERVER["HTTP_HOST"].dirname($_SERVER["PHP_SELF"])."/index.php");

きもちー。

ref: リダイレクト面倒

2005/07/08追記:

こっちの方がよい(via MugeSo)

/**
* An absolute web path to the index.php script.
*/
define('SCRIPT_PATH', strtolower(trim(array_shift(split("/", $_SERVER["SERVER_PROTOCOL"]))))."://".$_SERVER["HTTP_HOST"].$_SERVER["PHP_SELF"]);

CSRF(クロスサイトリクエストフォージェリ)の対策を自分の中で決めてみましたが、根本的に間違った理解をしてることがわかりました

CSRFはそのユーザ自身(のクライアント)がアクセスする(させる)ことを利用する攻撃なんですな!

高木浩光さんの記事(via ファイさん)を元にPHPでのサンプルを作ってみました。

ログインページ(index.php):

<?php
session_start();
?>
<?="<?xml version=\"1.0\" encoding=\"utf-8\"?>"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<title>ログイン</title>
</head>
<body>
<h1>ログイン</h1>
<p>ログインしました。</p>
<p><a href="confirm.php">退会</a></p>
</body>
</html>

確認ページ(confirm.php):

<php
session_start();
?>
<?="<?xml version=\"1.0\" encoding=\"utf-8\"?>"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<title>退会処理 確認画面</title>
</head>
<body>
<h1>退会処理 確認画面</h1>
<p>本当に退会しますか?</p>
<form method="post" action="resign.php">
<strong>&lt;input type="hidden" name="sessionid" value="&lt;?=session_id()?&gt;" /&gt;</strong>
&lt;input type="submit" value="退会する" /&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;

処理ページ(resign.php):

&lt;?php
session_start();
if (<strong>session_id() == $_POST["sessionid"]</strong>) {
    $message = "退会しました。";
} else {
    $message = "退会・・・するかよ!";    
}
?&gt;
&lt;?="&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;"?&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja"&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&gt;
&lt;meta http-equiv="Content-Style-Type" content="text/css" /&gt;
&lt;meta http-equiv="Content-Script-Type" content="text/javascript" /&gt;
&lt;title&gt;退会処理 完了画面&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;退会処理 完了画面&lt;/h1&gt;
&lt;p&gt;&lt;?=$message?&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;

動いてるサンプル:http://p0t.jp/anti-csrf/

ソース:anti-csrf-0.2.zip

外部サイトでは(本人がアクセスしたとしても)対象サイトのクッキーを取ることができないっていうのがミソ・・・だと思う。
あってるのかどうか気になります!

それほど面倒ではないのでコンシューマサイトは全部これで行くことにします!

ref: CSRF対策俺ルール

2005/07/08追記:

$_COOKIE[””PHPSESSID”“] から session_id() に変更しました。(via ishinao

PECL :: Package :: ssh2

Provides bindings to the functions of libssh2 which implements the SSH2 protocol. libssh2 is available from http://www.sourceforge.net/projects/libssh2

libssh2のPHPバインディング。これ知っておくと仕事で要件を意外な方法で片付けたりできる場面がある・・・気がする!

Trackbackサーバを書いたんですがXMLがちょっと面倒くさい。
そこでRESTより簡単なRPCを考えてみました。

サーバ:

&lt;?php
echo var_export(array("foo", "bar", "buz"));
?&gt;

クライアント:

&lt;?php
function var_export_rpc($uri) { return eval('return '.file_get_contents($uri).';'); }
var_dump(var_export_rpc("http://localhost/workspace/samples/var_export_rpc_server.php"));
?&gt;

VAR_EXPORT_RPC

XMLじゃなくてPHPコードを返す。
クライアントがPHPじゃないと死ぬほど面倒なことになる方式です。(Webサービスの利点台無し)
あぁ、しかもオブジェクト渡せない・・・。

SourceForge.net: Project Info – AwesomeFramework

This is PHP application framework of an extremely simple Plain Old PHP File base. It is possible to use it for not only a browser but also the command line application.

1行のフレームワークAwesomeFrameworkをsourceforge.netにプロジェクト登録しました。

POPFとか勝手に言っていいのかよ!
プロジェクト申請を通すために大層なこと書いてるけどソースは1行。嘘は言ってないからよしとします!

AwesomeFramework-0.4のソース:

&lt;?php include $_SERVER["argv"][1] ? preg_replace('/^--action=/', "", $_SERVER["argv"][1]) : ($_GET["action"] ? $_GET["action"] : 'default.php'); ?&gt;

しかし、この1行から恐るべき機能の数々が(実装したじゃなくて)発見されたので次の勉強会のネタはこれで行こうかと。

AwesomeFrameworkにForwardを実装しました。

AwesomeFramework-0.2.zip

&lt;?php
include $_GET["action"] ? $_GET["action"].".php" : "default.php";
function forward($action) { include $action.".php"; }
?&gt;

なんて高機能なんだ。
これでViewにforwardすることでModelとViewとControllerがそろってると言えるのではないだろうか!!

AwesomeTemplateEngineと同様、PHPファイル自体をテンプレートとして使用しますがAwesomeFrameworkはあくまで、$GLOBALS経由でテンプレートに値を渡します!

Actionのファイル:

&lt;?php
$GLOBALS["message"] = "World";
forward("view");
?&gt;

Viewのファイル:

Hello &lt;?=$GLOBALS["message"]?&gt;!

あと、今思いついたんですが、

module/
 action.php
index.php
index.php?action=module/action

ってやってモジュールによって管理する機能搭載!とか言えないでしょうか・・・。

Harukiからの平面波

個別処理に関数やクラスを使っても良いが、各caseで関数などのコールが入るだけで、やることは変わらない。

PHPのフレームワークは、個別処理をどう別けて記述するかを決めるところが肝であって、mojaviとか見たいに、コントローラクラスなんて作らなくてもいいやんと言う気もする。柔軟性高いし速いし。

Harukiさんのフレームワークについての考察。
振り分け処理(あえてコントローラーとは言わない)の最小限サンプルを書かれています。

確かにFront Controller Patternといってもやりたいことはこれだけスね。
それに比べた場合、他のフレームワークの便利な点は何か?

  • 名前空間が分かれている
  • エントリポイントに制約(仕様)がある

名前付けとか開放・閉鎖原則とか単にオブジェクト指向の便利な点と変わらなくなってしまいました。

クラスになると名前空間を分けることができるけれど、(クラスに対してコンパイラがやっているように)関数名と変数名にPrefixを付ければ同じことになる。

JavaやPHP5のように仕様を強制できる仕組み(インターフェースとか)があると個別処理が交換可能なことが保障されるので便利。だけど仕様を覚えるのが面倒。

これを全部とっぱらって柔軟性が高くて習得コストが低いのを売りにしたフレームワーク(的なもの)っていう方向性も面白いなと思いました。

そこで、昔面白いなと思った「7行のテンプレートエンジンAwesomeTemplateEngine」に対抗して「1行のフレームワークAwesomeFramework」を作ってみました。

AwesomeFramework 0.1(HelloWorldサンプル付属)

そのソース:

&lt;?php include $_GET["action"] ? $_GET["action"].".php" : "default.php"; ?&gt;

これを使ってある程度のアプリを作ろうとすると、逆に名前空間のありがたさとか(グローバル変数地獄)を身をもってわかって良いかも知れない!

先週、社内でPHPに関する質問メールを頂きました。

「MojaviのRequestクラスのsetAttributeメソッドとsetAttributeByRefメソッドの違いがわからない。」

というものです。
値渡し参照渡しの違いですが、おれ自身、参照を本当に理解しているかあやしいもんです。本当に理解していないとわかりやすく説明することはできません。
概念を本当に理解したかどうかは言語化(コード化)、もしくは図にできるかどうかで判断することにしています。

Prove it.(証明せよ)」ということでMojaviでのサンプルを作ってみました。

ref-0.1.zip

&lt;?php
class DefaultIndexAction extends Action {
    function execute(&$controller, &$request, &$user) {
        $value = "foo";

        $request-&gt;setAttribute("value", $value);        // real
//        $request-&gt;setAttributeByRef("value", $value);    // ref

        $value = "bar";

        echo $request-&gt;getAttribute("value");
        return VIEW_NONE;
    }
}
?&gt;

setAttributeとsetAttributeByRefで動作が変わる最小限サンプルです。
質問への回答はメールに返信してこのサンプルも一緒に送ったんですが、果たして解りやすい説明だったかどうか・・・自信がイマイチです。

[を] PHP をいじってみた

PHP、すごく手軽で良いですね。セッション管理が楽だー。
これならさくさくといろいろ面白いサービスが作れそうですね。
とりあえず、今後はDBとの連携を一通りやってみるつもり。

いつも楽しみに見ているたつをのChangeLogのたつをさんがPHPをはじめられたっ!

せっかくなので自分が「そういうの最初から教えておいてくれよ」と思ったことを書き出してみます。

PHP Switcher Q&A

Q. GETやPOSTを手っ取り早く取りたいんだけどCGIモジュール的なものは?

A. PHPコアの機能に含まれています。GETやPOSTのデータは自動的にグローバル変数の$_GET, $_POSTに連想配列として入っています。

Q. セッションは?

A. PHPコアの機能に含まれています。$_SESSIONというグローバル変数(連想配列)に代入するだけでセッションにデータが入ります。

Q. 配列と連想配列はどう違う?

A. 配列と連想配列は内部的には同じモノです。添字に文字列を指定すると連想配列になります。(添字に数字と認識できる文字列を入れると自動的に数値に変換されます。)

Q. ヘッダはどうやって書く?

A. Apacheモジュール版PHP(mod_php4)ではヘッダは自動的に付与されます。明示的に書きたい場合はheader関数を使います。それに対してコマンドライン版PHP(cli)が存在します。

Q. サードパーティ製モジュールは?

A. PerlでいうCPANにあたるPEARというリポジトリサイトがあります。(モジュールの登録には他のPEARメンテナの賛成投票が5票以上必要でstable/unstableという分類もあるため、CPANよりモジュール数が少ない代わりに一定の品質は保障されています。)

Q. PHP自体に機能を追加したい

A. Pure PHPのみのPEARに対して、Cで書かれたPHP extentionのリポジトリサイトとしてPECL(ぴくる)があります。

Q. Template-toolkit的なものは?

A. Smarty Template Engine。

Q. PHP自体がHTMLに埋め込めるんだからテンプレートエンジンなんていらないんじゃないの?

A. そういう考えのもと、PHP自体をテンプレートとして扱うSavantというテンプレートエンジンもあります。

Q. Webアプリケーションフレームワークは?

A. Mojavi
その他、国産フレームワークのMapleEthna、Java Seasar2のPHPポーティングS2PHP5などが有名です。

Q. O/Rマッパーは?

A. PEAR DB_DataObject。PHP5ではPropelなど。

PHP全体の印象としては、便利なものは少々言語デザイン的に美しくなくても標準関数にしちゃえ的節操のなさが良いところでもあり悪いところでもある。といったところでしょうか。

これを期に他言語をメインにしている人もちょっとPHPを触ってみる、もしくは多言語のライブラリやツールをPHPに移植してみるというのはどうでしょうか。