canvas と Web 仕掛け絵本

明けましておめでとうございます。 🙂

昨年のご愛顧も込めまして、「WordPress デザインワークブック」共著のコモモさんこと高橋朋代さんと Web 仕掛け絵本のサイトをつくりました。とてもかわいい感じになりましたので、良ければ見てください!

バッタになりたい魔法使い – pararaehon.com

PC/Mac はモダンブラウザ(IE8 以下はごめんなさいです)に対応しています。

モバイル系は iOS は 6 以降の Mobile Safari、Android は 4 以降の Chrome が良い感じで見れます。Android 2 の方は Firefox 17 を使うとゆっくりですが動作します。3G 回線の場合は、ちょっと読み込みに時間かかりますので、白くなったりしたらリロードしてみてください。キャッシュするときれいに動きます。

iPhone/iPad は 4S 以降(Apple A5)でフルスペックで動くはずです。すごい性能。iPhone の場合は Retina ででます。絵本的になりますので iPad3 もおすすめです。ベンチマークにもどうぞ。

自分の本職はバックエンド側の処理、業務系アプリの処理やインフラなどなのですが、WordPress を含めこういった Web サイトをつくる場合には、芸術性のあるデザインやブラウザフロントエンド処理が不可欠です。

WordPress デザインワークブック” の執筆ではこの部分にコモモさんの全面支援をもらっていますが、”バッタになりたい魔法使い”では絵をコモモさんに、ひろましゃはお話と、なんと初 JavaScript をやっております。 動きも面白いですので良ければその辺もご覧ください。 🙂

さて、去年はこのブログで「WordPress 徹底解析」シリーズを週1でやろうと始めておりましたが、少し忙しくなってしまい止まってしまってごめんなさい。2月くらいからまた時間が出来ると思いますので再開したいと思います。

今年もどうぞよろしくお願いいたします!


WordPress ではないですが、せっかく Web 系のネタですのでこの仕掛け絵本の仕組みを解説したいと思います。 まずは動作見て頂ければ。スクロールを上下するとぴょこぴょこ動くのが分かると思います。 🙂

バッタになりたい魔法使い

Web 仕掛け絵本で実現したかったことは2つでした。

  1. 60FPS のフルフレームで動作させる
  2. スマートフォン・タブレットでも動かす

絵本という前提があるので、コンピュータ的なフレーム落ちしたガタガタした動作は物語の世界を壊すと考え、ブラウザに 1/60 秒フレーム描画(canvas & requestAnimationFrame)をさせています。また、スマートフォン、タブレットでみれたら絵本っぽいなというのも考えました。

このため、canvas と requestAnimationFrame(もしくはベンダープレフィックス)が使えないブラウザでは動作しません。IE で言えば IE9 以降に対応します。ちなみに IE9 は結構速かったです。スマートフォン系では iOS6 以降、Android 4(標準ブラウザもしくは Chrome、Firefox17)が動作します。iOS5 の方、ごめんなさい…

この仕組みにパララ絵本という名称をつけていて、いわゆるパララックス効果も入っているのですが、通常のパララックスとは異なります。というのも、普通のパララックスはブラウザの縦座標(scrollTop)に対して、描画オブジェクトの位置を決めますが、パララ絵本ではスクロール位置を動かすタイミングとしてだけ使って、あとはタイマー処理しています。これは 60 フレーム描画をするためと、スマートフォン(Mobile Safari)がスクロール中動作にイベントが発生しないことに対する仕様です。

実際の実装ですが、絵本のコマに対して canvas タグを対応させ、CSS では canvas を display: block; してブラウザの描画領域を全て埋め、横幅、余白を含め全ての描画制御を JavaScript から行っています。たとえば、背景画像横幅が足りなければ CSS で真ん中にもっていくのではなく、”白”を画像の左右に描画しているイメージです。

ブラウザにやらせるよりも自前で制御したほうが、必ず見せたい領域を中央にもってくる処理や、 devicePixelRate(retina)の対応など、描いてもらった絵を忠実に再現させるのが楽だったというのが理由です。(このためちょっとブラウザリサイズの処理は遅いです)

<body id=&quot;parara&quot;>
<canvas></canvas>
<canvas></canvas>
...
</body>
body {
    margin: 0;
    padding: 0;
}

canvas {
    display: block;
}

JavaScript のソースはオーソドックスな、いわゆるゲーム描きです。requestAnimationFrame(render) と通常タイマー(update)の二つをつかった 1/60 処理です。

/**
 * Event initialize.
 */
Scene.prototype.start = function() {
    this.hack();
    window.addEventListener('load', this.init.bind(this), false);
    window.addEventListener('resize', this.init.bind(this), false);
    window.addEventListener('orientationchange', this.init.bind(this), false);
    window.addEventListener('scroll', this.update.bind(this), false);
    window.setInterval(this.update.bind(this), 1000 / ANIMATION_FPS);
    this.render();
};

/**
 * render.
 */
Scene.prototype.render = function() {
    requestAnimFrame(this.render.bind(this));
    // viewport range render.
    ...
    ...

...
/**
 * requestAnimationFrame wrapper.
 * context invalid?: Illegal operation on WrappedNative prototype object
 */
window.requestAnimFrame = (function() {
    return window.requestAnimationFrame
    || window.webkitRequestAnimationFrame
    || window.mozRequestAnimationFrame
    || window.oRequestAnimationFrame
    || window.msRequestAnimationFrame
    || function(/* function */callback, /* DOMElement */element) {
        window.setTimeout(callback, 1000 / ANIMATION_FPS);
    };
})();

オブジェクトの設計は、複数あるコマ(Scene)、コマ(Page)、コマ内の描画オブジェクト(Actor)と分割し、Scene ではブラウザのリサイズやスクロール位置処理と update render タイミングをとる処理、Page では Actor の実座標や動作タイマー(tick)制御、Actor は描画処理(canvas.drawImage)という感じになっています。

描画オブジェクトも全てビットマップ(Image)ですので、これらに対する DOM の操作はありません。全て canvas コンテキストに描いています。

Actor の座標はそれぞれに設定された動作開始スクロール位置からタイマーで +1 増加を始める tick 値によって決まり、逆スクロールでは時間(tick)を巻き戻すことで元の配置に戻るようにしています。つまり x += 1; とやるわけではなく x = tick とするイメージです。

隠しコマンドで「d」キーを押すとデバッグ表示になるようにしています。たぶん見るとすぐ分かると思います。

また、Actor の座標に関しては PararaEhon.js 初期化時にコンストラクタで定義できるようになっており、update や efect(drawImage する直前)をメソッドを処理時に差し込み、仕掛け絵本ライブラリとして使えるようにしています。

/**
 * バッタになりたい魔法使い.
 *
 * @author komomo and hiromasa
 * @version 1.0
 */
new PararaEhon.Scene({
    id : 'parara',
    width: 960,
    height: 600,
    book : [
        /**
         * ページ01
         */
        {
             bg: {
                 image: 'images/page01/01_bg.png',
                 fixed: true
             },
             wizard: {
                 image: 'images/page01/01_m.png',
                 x: 50,
                 y: 25
             },
             .....
        },
        /**
         * ページ02
         */
        {
             .....
             hitsuji01: {
                 image: 'images/page02/02_hitsuji01.png',
                 x: 15,
                 //y: 320,
                 start: 200,
                 update: function() {
                     this.y = 600 + (1 - Math.exp(-6 * (this.tick / 60))) * -280;
                     if(this.tick > 60) this.complete = true;
                 }
             },
             .....
/**
 * Constructor.
 */
Actor = function(context, object) {
    .......
    this.update = this.update ? object.update.bind(this) : null;
    this.effect = this.effect ? object.effect.bind(this) : null;
    .......
};

さて、キモとなる canvas.drawImage ですがブラウザによっていろいろ面白い動作がありました。Gecko や Webkit のソースまで見ていないので本当に見た目だけの判断です。間違っている可能性大きいです。

  1. Firefox17 と IE9 は スクロールで見えなくなっている canvas にもまじめに drawImage している?(見えているコマだけ render したら速くなりました)
  2. Firefox17 の Mac と Windows 版は描画が少し遅い。Linux 版(しかも性能の劣る機械)だとフルルフレームで描画される。(本当にこれはなんとなくなのですが Linux 以外は Image が GPU じゃなくてメインメモリから転送されているような。うまく VRAM に乗らなかった?気のせい?)
  3. Firefox では drawImage の引数で画像範囲外を誤って転送させても動くが、他のブラウザだと動作しない。(動かない動作は OpenGL で同様の間違いをしたときの動作に似ている)
  4. Mobile Safari に過激に複数の canvas をレンダリングさせると落ちる時がある
  5. Android Firefox 17 が結構がんばって canvas 描ける。(遅いですが)
  6. 速度の印象は Safari < Chrome < Firefox17 = IE9 という感じでした。がんばれ Firefox!(もうすぐでるであろう Firefox18 / IonMonkey が楽しみです)

なにぶんにもモダンな JavaScript をかくこともブラウザもほとんど初めてで、押し迫る期限の中座標処理でうひょ〜となってしまった部分もあり、まだまだ不出来ですがもし興味ありましたらソースを見てみてください。 🙂

PararaEhon.js

maho.js

というわけで。

[tegaki]みんなバッタになっちゃえ![/tegaki]

Amazon EC2 と Groovy と QuartzScheduler と commons-daemon

2012/11/11 のポッキーの日に「AWSで動かすWordPressとその高速化」セミナーに伺いまして、Amazon Web Service を堪能して参りました。 🙂

AWSで動かすWordPressとその高速化

アマゾン ウェブ サービス(AWS) にはウェブデザイナーにとっても魅力的なサービスがたくさんあります!
今回はAmazon Web Servicesのテクニカルエバンジェリストである堀内さんをお招きし、すぐにでも使えるサービスを実際に体験しながら(ハンズオンで学びならが)その魅力たっぷりのサービスについてを学びます。

また、AWS上でWordPressを使えるようにし、さらに高速化する方法について、 株式会社デジタルキューブ の小賀さんをお招きし、実際に体験しながらその方法についてを学びます!

tenpura さんや小賀さんにも久しぶりにお会いできまして楽しいひとときでした。

仕事柄、仮想化基盤には慣れてはいるものの、超高速チューンされた WordPress である「AMI 網元」がほとんどワンクリックでデプロイされる様子や、ストレージのスナップショットバックアップはやはり魅力的なフューチャーでした。

WordPress AMI – 超高速 WordPress AMI 網元

EC2のMicro Instance(無償枠あり)を利用すれば、圧倒的にハイパフォーマンスなWordPress環境をコンパネ操作のみで誰でも簡単に構築できます。WordPressの高速化対策、スマートフォン対策に効果的です。

現在のクラウドと呼ばれるサービスの根幹をなしているのは間違いなく仮想化技術です。 そして WordPress を実用的にクラウドにのせる試みはすごく面白いものでした。:)

AMI 綱元も使える Amazon EC2 のマイクロインスタンス(アカウント登録から1年無料とのこと)。

EC2 は root がもらえる IaaS。

[tegaki]むふふ..(←脱線したようだ[/tegaki]

というわけで、Groovy と QuartzSchedulercommons-daemon を使って怪しい(?)デーモンを動かしてみることにしました。 😛

デーモンは処理(スレッド)の途中終了をつくるのが結構大変だったりしますが、QuartzScheduler にそのへんを任せると定期実行する常駐ものが簡単につくれます。

以下の例は 20秒おきにログをはくデーモンです。実際には CronScheduleBuilder.cronSchedule で時間を決め、QuartzJob::execute() に好きな処理を実装します。

QuartzDaemon.groovy

package net.maple4ever.daemon

import org.apache.commons.daemon.*
import org.quartz.*
import org.quartz.impl.*
import org.apache.log4j.Logger

class QuartzDaemon implements Daemon {

    def static logger = Logger.getLogger(QuartzDaemon.class)
    def static daemon = new QuartzDaemon()
    def static sched = (new StdSchedulerFactory()).getScheduler();

    @Override
    public void init(DaemonContext arg0)
        throws DaemonInitException, Exception {
        // every 20 seconds
        def job = JobBuilder.newJob(QuartzJob.class)
            .withIdentity("job1", "group1")
            .build();
        def trigger = TriggerBuilder.newTrigger()
            .withIdentity("trigger1", "group1")
            .withSchedule(
                CronScheduleBuilder.cronSchedule("0/20 * * * * ?"))
            .build();
        sched.scheduleJob(job, trigger);
    }

    @Override
    public void start() throws Exception {
        logger.info("START QuartzDaemon")
        sched.start()
    }

    @Override
    public void stop() throws Exception {
        logger.info("STOP QuartzDaemon")
        sched.shutdown(true)
    }

    @Override
    public void destroy() {
        logger.info("DESTROY QuartzDaemon")
    }

    static main(args) {
        daemon.init(null)
        daemon.start()
        System.in.withReader {
            print  'Hit any key to exit.'
            println it.readLine()
        }
        daemon.stop()
    }
}

class QuartzJob implements Job {

    def static logger = Logger.getLogger(QuartzJob.class)

    def QuartzJob() {
    }

    @Override
    public void execute(JobExecutionContext context)
        throws JobExecutionException {
        // execute job
        JobKey jobKey = context.getJobDetail().getKey()
        logger.info("QuartzJob says: " + jobKey + " executing at " + new Date())
    }
}

commons-daemon のインターフェースに従ってクラスを定義して、init で QuartzScheduler のジョブ定義。 commons-daemon からのの start、stop をそのままQuartzSchedulerにパスしてあげればOKです。

また、この例のように static main メソッドをつくっておくとデバッグ時にデーモンではなくそのまま実行できるので便利です。

できたら groovyc でコンパイルしたクラスファイルを、commons-daemon の jsvc を次のようなシェルから起動してあげます。

setenv.sh

#!/bin/sh

SCRIPT_HOME=/opt/daemon
JSVC_USER=hiromasa

JAVA_HOME=/usr/lib/jvm/java

GROOVY_LIB=/opt/groovy-2.0.5/embeddable/*
LIB_HOME=$SCRIPT_HOME/lib
LIB_PATH=$GROOVY_LIB:$LIB_HOME/commons-daemon/*:$LIB_HOME/log4j/*:$LIB_HOME/quartz/*
LIB_PATH=$LIB_PATH:$SCRIPT_HOME/script
LIB_PATH=$LIB_PATH:$LIB_HOME/twitter4j/*:$LIB_HOME/javamail/*

#for command line(ex. $GG Foo.groovy)
export GG="java -cp $LIB_PATH groovy.ui.GroovyMain"

# twitter4j とか javamail とかは必要ありません。(ぼくの別デーモンで使われています 🙂

export している $GG はスクリプト作成時の実行用で、シェルから . ./setenv.sh などとして環境をもってきた後に、$GG QuartzDaemon.groovy とするとコンパイルせずに static main から実行できるようなっています。(UNIX 上の vi などで直接 .groovy をかくときに便利です)

ディレクトリ構成と lib は以下のような感じです。

[hiromasa@]$ ls -laF
合計 28
drwxr-xr-x 7 hiromasa hiromasa 4096 11月 18 08:57 2012 ./
drwxr-xr-x 5 root     root     4096 11月 18 09:38 2012 ../
drwxrwxr-x 2 hiromasa hiromasa 4096 11月 18 09:45 2012 bin/
drwxrwxr-x 7 hiromasa hiromasa 4096 11月 18 01:53 2012 lib/
drwxrwxr-x 2 hiromasa hiromasa 4096 11月 18 09:48 2012 log/
drwxrwxr-x 4 hiromasa hiromasa 4096 11月 18 10:56 2012 script/
drwxrwxr-x 2 hiromasa hiromasa 4096 11月 18 09:48 2012 tmp/
[hiromasa@]$ ls -laF lib/*
lib/commons-daemon:
合計 32
drwxrwxr-x 2 hiromasa hiromasa  4096 11月 17 11:43 2012 ./
drwxrwxr-x 7 hiromasa hiromasa  4096 11月 18 01:53 2012 ../
-rw-rw-r-- 1 hiromasa hiromasa 24242  2月 23 23:22 2012 commons-daemon-1.0.10.jar

lib/log4j:
合計 488
drwxrwxr-x 2 hiromasa hiromasa   4096 11月 17 12:01 2012 ./
drwxrwxr-x 7 hiromasa hiromasa   4096 11月 18 01:53 2012 ../
-rw-rw-r-- 1 hiromasa hiromasa 489883  5月  6 11:01 2012 log4j-1.2.17.jar

lib/quartz:
合計 1212
drwxrwxr-x 2 hiromasa hiromasa   4096 11月 18 07:15 2012 ./
drwxrwxr-x 7 hiromasa hiromasa   4096 11月 18 01:53 2012 ../
-rw-r--r-- 1 hiromasa hiromasa 608376  5月 20 11:12 2011 c3p0-0.9.1.1.jar
-rw-rw-r-- 1 hiromasa hiromasa 578548  8月  6 13:28 2012 quartz-all-2.1.6.jar
-rw-r--r-- 1 hiromasa hiromasa  25496  5月 19 20:22 2011 slf4j-api-1.6.1.jar
-rw-r--r-- 1 hiromasa hiromasa   9753  5月 19 21:02 2011 slf4j-log4j12-1.6.1.jar

quartz.sh

#/bin/sh

# setup
. /opt/daemon/bin/setenv.sh

RUN=net.maple4ever.daemon.QuartzDaemon

# check args
if [ $# -ne 1 ]
then
	echo "usage: quartz.sh [start|stop]"
	exit 1
fi

ret=9
case $1 in
	start )
		echo "starting daemon..."
		$SCRIPT_HOME/bin/jsvc \
			-pidfile $SCRIPT_HOME/tmp/$RUN.pid \
			-user $JSVC_USER \
			-home $JAVA_HOME \
			-cp $LIB_PATH \
			$RUN
		ret=$?
		;;
	stop )
		echo "stoping daemon..."
		$SCRIPT_HOME/bin/jsvc \
			-stop \
			-pidfile $SCRIPT_HOME/tmp/$RUN.pid \
			$RUN
		ret=$?
		;;
esac

exit $ret

手抜きシェルですが、./quartz.sh start|stop でデーモンの常駐制御ができます。

hiromasa 24923     1  0 09:48 ?        00:00:00 jsvc.exec -pidfile /opt/daemon/tmp/net.maple4ever.daemon.QuartzDaemon.pid -user hiromasa -home /usr/lib/jvm/java -cp /opt/groovy-2.0.5/embeddable/*:/opt/daemon/lib/commons-daemon/*:/opt/daemon/lib/log4j/*:/opt/daemon/lib/quartz/*:/opt/daemon/script:/opt/daemon/lib/twitter4j/*:/opt/daemon/lib/javamail/* net.maple4ever.daemon.QuartzDaemon
hiromasa 24924 24923  0 09:48 ?        00:00:06 jsvc.exec -pidfile /opt/daemon/tmp/net.maple4ever.daemon.QuartzDaemon.pid -user hiromasa -home /usr/lib/jvm/java -cp /opt/groovy-2.0.5/embeddable/*:/opt/daemon/lib/commons-daemon/*:/opt/daemon/lib/log4j/*:/opt/daemon/lib/quartz/*:/opt/daemon/script:/opt/daemon/lib/twitter4j/*:/opt/daemon/lib/javamail/* net.maple4ever.daemon.QuartzDaemon

このように jsvc プロセスが 2つできれば成功です。 🙂

commons-daemon と QuartzScheduler を使えば、cron などを使うのと違って簡単に無理なく状態遷移を制御できますので良いのではないかと思います。

さぁ、Amazon EC2 のマイクロインスタンスでどんなデーモン動かしましょう。 いろいろアイディアがでてきますが、ネットワークの中で生き続けるプログラムってやっぱり面白いですね。 😀

WordPress 徹底解析(カスタマイズとフィルターとアクション編)

週一回で始まった、WordPress 徹底解析シリーズ。 いきなり1週お休みをいただくというさすがぼく的な展開を見せていますが(ちょっと忙しかったのです・・・)、なんとかがんばって続けていきましょう。 🙂

前回まではこちら。

WordPress 徹底解析(PHP Development Tools インストール編)

また、テーマ制作中に WordPress 本体のソースを追うことも容易です。WordPress 本体のソースは WordPress のテクニックや考え方の宝庫です。

WordPress のコア(本体)ファイルを見ながら、WordPress で使えるテクニックと、おまけに PHP も面白く理解していこうというのが本シリーズの意図です。前回は準備としてコアのファイルが簡単にみれる環境を構築してみました。

WordPress のようなプログラマブルなアプリケーションを使いこなすには、そのコアのソースコードをみるのが、早く、はまらず、実装の間違えも少なくなると経験的に思います。 ソースコードを見るのは素早く作業を進めるための手段です。そのために、極力その手間を減らすために開発環境を構築しました。

あとは”とっかかり”が分かれば、、ということで今回は WordPress のカスタマイズを構成する要素について解説していきます。

WordPress のカスタマイズとは

まずは、このシリーズでいうところの “WordPress のカスタマイズ”について定義しましょう。

  1. 既存のテンプレートタグの動作や出力を変更する。(HTML の class 名追加や、出力条件、項目、表現の変更等)
  2. 管理画面に新しい機能を追加する、何かしらのタイミングで自分で記述した新しい処理を追加する。(投稿画面に新しい入力項目を追加し保存する、ポストしたタイミングで twitter に URL を投稿する等)
  3. ショートコードやウィジェットの追加。
  4. WordPress が標準で行う処理をまったく違うものに置き換えてしまう。(ログイン認証をユーザパスワード認証から OpenID や LDAP に変更する等)
  5. DB接続エラー画面の制御やメンテナンス中の画面、カスタマイズされた DB アクセスやキャッシュ制御を追加する。

ここでは上記を WordPress のコア(本体)ファイルを書き換えずに実現することをカスタマイズと呼ぶことにします。 WordPress では、そのためにいくつかの API や仕組みが用意されています。

  • フィルター
  • アクション
  • ショートコード、ウィジェットAPI
  • オーバーライドできる関数の上書き
  • ドロップインファイルの配置

今回から、それぞれの特徴と、その後にどこにどのように記述すれば動き出すかという部分を解説をしていきたいと思います。まずは、フィルター・アクションの紹介よりいってみましょう。

なお上記以外にも、テーマテンプレートファイル内で既存のテンプレートタグ(関数)を組み合わせて標準ではできない表現をすることなどもカスタマイズに入ると思います。 こちらについても大切な要素ですので PHP の知識とともに、追々フォローしていきたいと思います。

フィルターとアクション

フィルターとアクションとは、WordPress の動作を、事前に定義しておいたタイミングで処理の変更・追加を行うための仕組みの名称です。このことをフックと呼びます。フィルターフック、アクションフック、です。

フィルターフックは WordPress から入力されてくる値の変更を行い、アクションフックはそのタイミングにおける処理の追加を行います。 これらは、ぼくたちが利用するだけでなく、WordPress の内部処理においても積極的に使われているものです。

フィルターがコアで使われている代表例は、記事内に書いた顔文字変換  :D ←これとか、改行を二つ入れると <p> タグに変換されるフォーマット変換などです。 これらは入力されてきた記事の内容を出力時に値の変更を行っているイメージです。もちろん、変更できる対象は記事の内容にとどまらず、タイトルや HTML タグの属性まで多岐にわたります。

アクションの代表例はテンプレートファイルに記述する wp_head() や wp_footer() です。 これらを記述した部分にコアやプラグインから必要な HTML などが出力されているのはアクションフックの力です。 wp_head や wp_footer が動作したタイミングに合わせて、それぞれの処理が HTML タグを出力しています。

フィルター・アクションの仕組みはぼくたちにも開放されていますので、同様の動作をテーマやプラグインから追加できます。また、コアの定義で不必要なフックは削除することもできます。

気になるのは WordPress ではどんなタイミングのフックが配置されているかという部分・・・。先に申しますと、だいたい考え得るありとあらゆるところにあります。 なので、こんなことできたらいいなが代表例のような値の変更や処理の追加だった場合がフィルター・アクションの出番と覚えて差し支えないでしょう。 次回以降、その探し方を解説していきます。

さて、よくフィルターとアクションの差がよく分からないという声を聞きます。実はそれもその通り。 アクションを定義するコアの add_action 関数のコードを見てみると・・・

wp-includes/plugin.php

function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
    return add_filter($tag, $function_to_add, $priority, $accepted_args);
}

add_action はさらにフィルターを定義する add_filter で実装されていたりします。タイミングに対する処理という意味ではどちらも同じものなので内部的にはフィルターを使っているわけです。 違いは値の変更をするか、処理の追加をするかということになります。

それぞれに定義されたフックを呼び出すコアのコードは次のようなものです。

フィルター。 wp-includes/plugin.php::apply_filters 関数抜粋。

do {
    foreach( (array) current($wp_filter[$tag]) as $the_ )
        if ( !is_null($the_['function']) ){
            $args[1] = $value;
            $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
        }
    }
} while ( next($wp_filter[$tag]) !== false );

アクション。 wp-includes/plugin.php::do_action 関数抜粋。

do {
    foreach ( (array) current($wp_filter[$tag]) as $the_ )
        if ( !is_null($the_['function']) )
            call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
} while ( next($wp_filter[$tag]) !== false );

フィルター・アクションともに $wp_”filter” 変数を使うほとんど同じコードになっています。

異なるのはフィルターのほうには $value があり、なんとなくループで同じフィルター処理に対して数珠つなぎで $value が渡っていくのが見て取れます。 これが、たとえばコアで設定されている記事出力に対するフィルターに、顔文字フィルター、HTML フォーマットフィルターなどと複数の設定が可能で、最終的に合成された結果を返す秘密です。

さて、いくつかプログラムのソースコードと関係する関数名がでてきました。 add_filter、apply_filter と add_action、do_action。前から、フィルターの登録・適応とアクションの登録・適応するための関数になります。次回はこの名前をヒントに、フィルター・アクションの実際やそのタイミングの探し方を見ていくことにしましょう。

このシリーズはブログ記事なので、いきなり新しい言葉がでてきてちょっとよく分からなかった方もいるかもしれません。

ぼくも共著させていただいている書籍のほうでは、WordPress サイト制作の基本から応用まで言葉も含めて解説しておりますので良ければ手にとっていただければと思います。

さて、昔話になりますが、ぼくが小学生くらいだったときは、ソースコードとその考え方をおもしろく筆者の思考の流れとともに解説する月刊誌や技術書が沢山ありました。 きっと、同年代の方ではそのような雑誌で育った方も多いかと思います。

もしかすると、ぼくが書くブログや書籍はそんなみんなが忘れかけた、知らない方には新しい(?)表現が垣間見られてるんじゃないかな、、と最近思います。

いまだに大切にしている 1991 年の技術書があります。やっぱり今読んでも面白いのです。

ぼくはコンピュータにさわり始めたのが早いので、マイコン世代の方より少し若輩者ではありますが、本来楽しいはずであるソフトウェアの世界をお伝えすべく、、次回もお楽しみに。 🙂