Picoのプラグインは、pluginsというディレクトリの中に、所定のメソッドを実装したPHPクラスを配置するだけ。メソッドはそれぞれページレンダリングの特定のタイミングで呼び出されており、それを処理することで、Picoに機能を追加したり、編集機能を設けたりすることが出来るようです。既存のプラグインは、PicoCMSのWikiページにあります(…が、2015/06/04現在はプラグインの仕様が固まる前に作られたものも多く、「これを使ってなんか作る」というより「これを参考にしたりフォークして新規に自分で作る」ほうが早いかもしれません)。
プラグインをとりあえず作ってみる
プラグインを作るにあたり気をつけるべきことは3つ
- pluginsディレクトリにファイルを配置すること(サブディレクトリの下でも良い)
- phpファイルと、中で定義するクラス名は、同じとすること(大文字小文字は区別しない)
- プラグインフックメソッドに渡される引数が、値渡しか参照渡しかよく見る(これを間違えると、せっかく加工した処理が反映されない なんてことになる)
pluginsディレクトリには、最初から「pico_plugins.php」というファイルが置いてあるので、それをコピーして好きなように作り替えて使うと良いです。
プラグインのメソッド呼び出しタイミング
ここからが厄介なところ。このプラグインのメソッド呼び出しタイミングですが、見たところ公式のドキュメントには載っていません。ソースコードは前述の通り一つですのでそんなに追いづらくはないのですが、以下にまとめてみました(記載は呼び出し順)
- plugins_loaded
- config_loaded
- request_url
- before_load_content
- before_404_load_content(読み込もうとしたファイルが存在したときには、呼び出されない
- after_404_content(同上)
- after_load_content(以降、コンテンツがあってもなくても呼び出される)
- before_read_file_meta(一回のアクセスにつきコンテンツファイルの数(contents/ディレクトリにあるmdファイルの数)ぶん呼び出される)
- file_meta
- before_parse_content
- after_parse_content
- content_parsed(非推奨)
- get_page_data(before_read_file_metaと同様、コンテンツファイル数ぶん呼び出される)
- get_pages
- before_twig_register
- before_render
- after_render
とくに上でも括弧書きしたとおり、before_read_file_meta
とget_page_data
は、一回のアクセスごとに、コンテンツファイル数ぶん呼び出されます。全てのコンテンツに追加のメタデータを定義したり、そのメタデータに何らかの加工を施したい場合は、これらを使うといいです。
なお、公式ページのサンプルにあるとおり、before_read_file_meta
ではメタデータフィールドの追加のみが行えます(メタデータの加工は行えません)。before_read_file_metaでメタデータのフィールド名を追加して、get_page_dataでメタデータを参考に処理を追加していく という感じがベストっぽいです。
お勧めの使い方?
いちおういくつかプラグインを作ってみた感じのメモ
config_loaded
config.phpの設定が読める唯一のフックメソッドです。設定値を使う場合はここでデータを取得し、クラスのフィールドに格納しておきましょう。
また、インターネットのコンテンツを受信し、それをページとして追加するようなプラグインを書く場合は、ここで処理をすませておくと良いです。
class Plugin_Demo{
private $base_url;
public function config_loaded(&$settings) {
$this->base_url = $settings['base_url'];
$this->content_dir = $settings['content_dir'];
}
// ・・・
}
request_url
実際にアクセスするURLが確定したタイミングで呼び出されます。タグページなど実際には存在しないページを存在すると見せかける必要があるときに呼び出されています。
before_read_file_meta
メタデータのヘッダを追加できるメソッドです。それ以外のことは出来ません。
class Plugin_Demo{
// ・・・
public function before_read_file_meta(&$headers)
{
$headers['image'] = 'Image';
}
// ・・・
}
after_parse_content
パースした直後のファイル(マークダウンから変換した後のHTMLデータ)にアクセスできます。
たとえばヘッダタグの番号を書き換えるなど、HTMLに関する加工を行う場合はここで行います。
class Plugin_Demo{
public function after_parse_content(&$content)
{
$content = preg_replace_callback('/(<\/?h)(\d)([^>]*>)/', function($m){
return $m[2] + 3 <= 6 ? $m[1] . ($m[2] + 3) . $m[3] : "";
}, $content);
}
}
get_page_data
メタデータとページデータにアクセスできます。メタデータの加工を行う場合はここでやっておくと良いです。
class Plugin_Demo{
// ・・・
public function get_page_data(&$data, $page_meta)
{
$file_url = substr($data["url"], strlen($this->base_url));
if($file_url[strlen($file_url) - 1] == "/") $file_url .= 'index';
if (strlen($page_meta['image']) > 0 && preg_match('/^(.+\/)[\w\.-]+?$/', $file_url, $m)) {
if($page_meta['image'][0] == '.'){
$data['image'] = $this->base_url . "/" . $this->content_dir . "$m[0]$page_meta[image]";
}else{
$data['image'] = $this->base_url . "/" . $this->content_dir . "$m[1]$page_meta[image]";
}
} else {
$data['image'] = NULL;
}
// ・・・
}
get_pages
全てのページ情報にアクセスできるメソッドです。処理が重くなりがちなので、なるべく使わない方が良さそうです。
なお、古いプラグイン仕様ではこのメソッドで「ファイルが存在すれば処理を、しなければそのエントリをpagesリストから消す」という処理をやっているものがあります。
class Plugin_Demo{
// ・・・
public function get_pages(&$pages, &$current_page, &$prev_page, &$next_page) {
$new_pages = array();
foreach ($pages as $page) {
// ・・・
}
$pages = $new_pages;
}
// ・・・
}
before_render
ほぼ最後に呼び出される処理です。Twig変数に直接アクセスできるので、ページの並び替えや、グループ化して別の変数を作るなどの処理を行う場合はここが良いです。
class Plugin_Demo{
// ・・・
public function before_render(&$twig_vars, &$twig, &$template) {
$pages = $twig_vars["pages"];
$curdir = array();
// ・・・
$twig_vars["dir_pages"] = $curdir;
}
// ・・・
}
プラグインの呼び出し順は?
変更出来ません。pico.phpのget_files
メソッドを見た感じ、PHPのディレクトリ処理関数におまかせのようです。
よって、○○のプラグイン処理の後に△△のプラグインの処理を実行して・・・ というようなことは出来ないっぽいです。