PHP 5.3.0 ではマジックメソッド __callStatic が追加された。グローバルに使用されるユーティリティクラスで、動的にメソッド名を生成したいときに便利。
class FooUtil {
static protected $_inc = array(’test1′ => 1,
‘test2′ => 2);
public static function __callStatic($method, $args) {
return isset(static::$_inc[$method])
? static::$_inc[$method] + $args[0] : $args[0];
}
}
echo FooUtil::test1(100); // => 101
echo FooUtil::test2(100); // => 102
echo FooUtil::testxxx(100); // => 100
同等の機能を [...]
せっかく私のpatchが採用されたので、ブログにも書いておきます。
CakePHP 1.3からはプラグイン内の DataSource が使えるようになりますが、1.3RC1からは DataSource の driver もプラグインから読み込めるようになりました。
たとえばGithubのCakePHP datasources pluginでは、Cakeコアに含まれない様々なデータベースに対するDboドライバが提供されていますが、RC1ではこれを次のようにして読み込むことができます。
class DATABASE_CONFIG {
var $sqlite3 = array(’driver’ => ‘Datasources.DboSqlite3′,
…);
}
また driver を使用する DataSource をプラグインとして提供することも可能です。拙作のKeyValueSourceは次のように使用できます。
class DATABASE_CONFIG {
var $memcache = array(’datasource’ => ‘KeyValueStore.KeyValueSource’,
[...]
拙作のKeyValueSource(key-valueストアのためのDataSource)で、スキーマ・レスなデータを保存できるようにしてみました。
cakephp-key-value-source – GitHub
(このプロジェクトは実用より実験を重視しているので、頻繁に仕様が変わります)
モデルクラスで $looseSchema というプロパティを設定すると、プライマリキーidを除いて、どんな構造のデータでも保存できるようになります。
class SchemalessUser extends AppModel {
var $useDbConfig = ‘memcache’;
var $looseSchema = true;
}
// in Controllers
$SchemalessUser->save(array(’id’ => 1234,
‘name’ => ‘John Smith’,
[...]
CakePHP から レコメンデーションエンジン Cicindela にアクセスするためのライブラリを公開しました。
http://github.com/tkyk/cakephp-cicindela
使い方はREADMEを読んでください。Cicindela本体のWeb APIとほとんど同じ構造なので、特に迷うことはないだろうと思います。
ちなみに Configure::read(‘debug’) > 1 で実行すると、DboSourceと同じようにリクエスト情報が一覧表示されます。デバッグに便利です。こんな感じ:
(cicindela) 2 request(s)
NrURLStatusTook (ms)
1http://host/cicindela/recommend?set=pick&op=for_item&item_id=RubyHTTP/1.1 200 OK2887
2http://host/cicindela/recommend?set=pick&op=for_item&item_id=PHPHTTP/1.1 200 OK1346
….
以下、このライブラリを作りながら考えたこと。
このライブラリはDataSource + Behaviorという2クラス構成になっている。最初はDataSourceだけでできるかと思っていたのだが、無理だった。現在のCakeのModelクラスは、DataSourceとDboSourceの関係で言うなら”DboModel”であって、非DboのDataSourceを扱うには適していない。
現在のModelが”DboModel”であるように、XXXSourceに対してはXXXModelが存在することが望ましい。CakePHP 1.3からはDataSourceがプラグイン化できるようなので、同時にModelのベースクラスを提供するようにすれば、ある程度は改善されると思う。ただそれでも結局(プラグインの)ユーザがいちいちクラスの継承関係に気を使わなければいけない点が煩わしい。理想を言うなら、1.非常に簡素なBaseModelクラスを用意する、2.DataSourceにモデル初期化用コールバックを設ける、3.初期化用コールバックにおいてBehaviorをattachする、DataSource固有の機能はBehaviorに実装する……という構成にできれば良いのではないか。
先日使い方を学んだ MessageEvent と、location.href+javascript:ハックとJSDeferred userscript版とを組み合わせ、XPCNativeWrapper と unsafeWindow の間で同期処理を行うコードを書いてみた。
まずは MessageEvent を使って前回 evalInPage 相当の処理を作る。キャンセル処理も入れたかったので UnsafeWrapper というオブジェクトにまとめることにした。前回より長くなったように見えるが、実際には MessageEvent の lastEventId が使えるようになった分、簡潔になっている。
var UnsafeWrapper = new (function() {
var seqId = 0, waiting = {}, self = this, noop = function(){};
function dispatch(data, id) {
var e = document.createEvent("MessageEvent");
e.initMessageEvent(’GM_UnsafeWrapper_returned’, true, false,
[...]
DB内のMEMORYテーブルのサイズを、テーブルごとにグラフ化するための munin プラグインを作ってみました。
mysql_memory_tables_
インストール
munin-node 本体のプラグインディレクトリ(/usr/share/munin/plugins など)にコピーしてください。
設定
ワイルドカードプラグインなので、mysql_memory_tables_{チェック対象となるDB名} という名前でシンボリックリンクを張ってください。
ln -s /usr/share/munin/plugins/mysql_memory_tables_ /etc/munin/plugins/mysql_memory_tables_db1
データを取得するには対象DBの INFORMATION_SCHEMA にアクセスする権限が必要です。プラグイン設定ファイル(/etc/munin/plugin-conf.d/munin-node など)で mysql コマンドに渡すユーザ名・パスワードを指定してください。
[mysql_memory_tables_*]
env.mysqlopts -u cicindela –password=hogehoge
動作確認
munin-run コマンドでテストができます。
# 値の表示
munin-run mysql_memory_tables_db1
# グラフ設定情報の表示
munin-run mysql_memory_tables_db1 config
動作が確認できたら munin-node を再起動してください。
XPCNativeWrapperの外側でスクリプトを評価する関数。Firebugも同じことをしている。unsafeWindowを触らないので安全。
function evalInPage(fun) {
location.href = "javascript:void (" + fun + ")()";
}
SmartLDR更新 – 素人がプログラミングを勉強するブログ
これは面白い!Function.prototype.toString が関数のソースコードを返すことを利用した、巧妙にして簡潔なハック。
次のように引数を渡せるように改良すればさらに強力になる(引数に渡せるのはJSON化可能な値のみ)。
function evalInPage(func, args) {
var argStr = JSON.stringify(args || []);
location.href = "javascript:void "+ func +".apply(null,"+ argStr +")";
}
GM_xmlhttpRequest を使って別ドメインから得たデータを、unsafeWindow 上のライブラリを使って表示する、といったコードが安全かつ自然に書けるようになる。
// データの取得は Greasemonkey で行う
GM_xmlhttpRequest({ method:"GET",
[...]
確実にグローバル関数/変数にアクセスしたいとき。あるいはglobalオブジェクトの名前が分からないときに。
var global = (function(){ return this })();
ブラウザ上では window がglobalオブジェクトを指すことは暗黙の前提になっているが、そうでない環境もあり得る。
ちなみに Greasemonkey と Chrome user-script ではともに window でglobalオブジェクトにアクセスできるようだ。
(CakePHP 1.2.5)
ビヘイビアに定義されたメソッドは、モデル自身のメソッドであるかのように呼び出すことができます。
class FooBehavior extends ModelBehavior {
function foo($model){ /* … */ }
}
class Bar extends AppModel {
var $actsAs = array(’Foo’);
}
// in Controllers
$this->Bar->foo();
しかしこの方法では Dynamic Finder のように動的に名前が変化するメソッドは実装できません。
ModelBehavior クラスの $mapMethods プロパティを使用すると、この制約を乗り越えることができます。$mapMethods は配列で、次のような構造をしています:
array( ‘/メソッド名に対する正規表現/’ => ‘実際に呼び出されるメソッド’ )
例えば ‘/^list/’ => ‘_genericList’ という設定を行った場合、listから始まる全てのメソッド呼び出しが、ビヘイビアの _genericList メソッドの呼び出しに変換されます。このとき _genericList メソッドの引数には元のメソッド名と引数が渡されます。ちょうど __call によるメソッドのオーバーロードと同じです。
この機能を活用して、あるカラムの値を配列として取得するための listColumn メソッドを作ってみます(Column はカラム名)。このメソッドは ListingBehavior に実装することにしましょう。
class ListingBehavior extends ModelBehavior {
[...]
ow.lyやoneclip.jpなど、元コンテンツをiframe内に表示するタイプのURL短縮サービスから、自動的に元URLにリダイレクトするuser scriptを作りました。
Redirect to the iframed URL
同様の機能を持つものとしてiframe redirectorがあり、今まで私も使用していたのですが、対象サービスのURLやHTML構造に依存するコードが含まれているため、対象サービスの構成が変化すると動かなくなってしまいます(少し前に am6.jp の仕様が変わって動かなくなりました。現在は修正されています)。
そのため拙作のスクリプトではURLやHTMLに依存するコードは使わず、「面積比でbodyの4分の1を占めるiframe」を元コンテンツと判断するようにしました。おそらくほとんどのケースで問題なく動作するものと思われます。
デフォルトでは以下のURLで動作します。
http://ow.ly/*
http://oneclip.jp/*
http://am6.jp/*
今後新たに同種のサイトが登場した場合は(登場してほしくないのですが)、Greasemonkeyの管理ダイアログから「ユーザスクリプトを実行するページ」にURLを追加してください。スクリプトの修正は不要です。
Firefox 3.5.7 + Greasemonkey 0.8.2 および Mac版 Google Chrome 4.0.295.9 で動作確認しています。
参考
URL短縮サービス ow.ly は実URLが表示されずに危険 → グリモン書いた – まちゅダイアリー(2009-11-05)