HHVM Hack (hacklang) を使った型安全なWordPressプラグイン開発で必要なこと

このブログで使っている WordPress プラグインの開発を Hack 言語を使ってやってみたのでその結果わかったことを書きます。

前置きその1 なぜWP-Pluginを開発したのか?

WordPressのユーザーの多くは自分でプラグインを開発しないでしょう。既に多くの実用的なプラグインがあるからです。

僕がこのブログのためにプラグインを開発した理由は、次の二つです。

  • テーマに対して透過的にブログを開発したかった。
  • 成功しているプラグインアーキテクチャの一例としてWordPressを勉強したかった

前者は、僕はデザインができないので、デザインはテーマを頼らざるを得ませんから、テーマの中にちょっとしたカスタマイズを入れたい場合でも極力テーマのファイルをいじりたくないためです。

簡単なロジックでもテーマに書いてしまうと、いざテーマを変更したいと思ったときに自分でいじった部分を新しいテーマに移植しなければなりません。

それは避けたいので、ブログパーツを入れる程度のことでも、wp_headやwp_footer、the_contentにadd_actionやadd_filterしました。

Hackとは

Facebookが開発した新しいプログラミング言語です。今年の3月に公開されたばかりの非常に新しい言語ですが、僕の見解では今すぐ使える実用的な言語だと思っています。理由は次の通りです。

  • PHPコードをrequireできる
  • PHPコードからrequireできる
  • PHPの機能がほぼ全部使える

つまり次のような事例で活用できると思います

  • 楽にWeb開発したいけど、型チェック付きの安全な開発がしたい
  • PHPの資産を活用したい

おまけに、Hackの実行環境であるHHVMはPHPも併せて高速がウリです。

ただし、現在HHVMを動かすレンタルサーバーは存在しないので自分でVPSやIaaSを契約してWebサーバを構築する必要があり、作ったプラグインを他人に配布してもHHVM実行環境がなければ動かせないという問題があります。

WordPressプラグインを公開して公式サイトから配布することもできませんね。

型チェックの仕方

プラグインのディレクトリ直下で

$ touch .hhconfig

とタイプして.hhconfigファイルを作成します。そうすると、このディレクトリはhh_clientで監視することができるようになります。

そのまま

$ hh_client

と打つと例えば以下のように

$ hh_client
/hello.php:4:13,21: Typing error
/hello.php:4:13,21: This is a num (int/float) because this is used in an arithmetic operation
/hello.php:4:13,21: It is incompatible with a string

のように、エラーがあったら表示してくれます。ちなみにエラーになってしまった行には次のように記述していました。

print (1 + "hello, \n");

このコード、Webから実行すると、実行することは出来てしまいます。つまり、PHP文法的にOKならばWebプログラムとして動かせるが、Hack言語的にNGかどうかを事前にチェックできるようにして、バグを防ぎ、必要なユニットテストのケース数を減らすことに寄与してくれます。

Hackでプラグインを作る方法

単純に、プラグイン用のPHPファイルの先頭を

<?php

の代わりに

<?hh

で書き始めるだけです。
あとは通常のプラグインとして開発すれば良いです。

Hackでプラグインを開発するときの注意点

globalが使えない

WordPressでは、#postや$wp_queryのようなグローバス変数に便利な情報が格納されています。

しかし、これを使おうとすると、実際はうごくのですが、hh_clientを実行するとエラーになってしまいます。例えば次のような感じです。

<?hh
require("hello2.php"); // この中でグローバル変数 $hello が定義されていることとする。
function init() {
        global $hello;
        print (1 + "hello, " . $hello . "\n");
}
init(); 
$ hh_client
/hello.php:4:16,21: Expected ;
/hello.php:5:25,30: Undefined variable: $hello
/hello.php:5:13,21: Typing error
/hello.php:5:13,21: This is a num (int/float) because this is used in an arithmetic operation
/hello.php:5:13,21: It is incompatible with a string

実はPHPのすべての機能をHackが提供しているわけではありません。下記のページにサポートしない機能が記載されています。

http://docs.hhvm.com/manual/en/hack.unsupported.php

globalのほかに、if: endif構文やeval $$ なんかもサポートしていないのですね。

回避方法

HackはPHPをrequireできる特徴を活用してこの問題は次のように解決できました。
hello2.php

<?php
        function get_php_global($name) {
                global $$name;
                return $$name;
        }

global $hello;
$hello = "world";

こうして、hello2.phpをhello.phpからrequireしておけば、

print (1 + "hello, " . get_php_global('hello') . "\n");

のようにしてグローバルな値を取得することができます。

わー!こんぐらちゅえーしょん!

フック関数の登録時にはfun関数を使おう(2014-08-19追記)

WordPressプラグイン開発ではadd_actionやadd_filter関数を使用してフック関数を登録していきます。

こんな感じで書きます。

function init() {
    add_action('wp_head', 'seo_metadatas');    
}
function seo_metadatas() {
    // 省略
}
init();

この時、うっかりタイプミスをして関数名seo_metadatasを書き間違えるとどうなるでしょうか?特に何もエラーを這い出さず、単にカスタマイズが反映されないだけになります。いくらhh_clientで型チェックをしていても無視されます。add_actionの第2パラメータはただの「文字列」だからです。

結論から言うと、次のようにすればこの問題は解決できます。

add_action('wp_head', fun('seo_metadatas'));

Hack and HHVM: fun - Manual

fun関数は文字列引数を受け取って呼び出し可能な「関数型」として値を返します。逆に言えば、<?hhタグ内ではPHPみたいに$文字列変数を使って動的な関数呼び出しを本来はサポートしていないようです。

fun関数の結果をvar_dumpしてみるとただの文字列だと出力されるので、PHPとしては「文字列」として扱われます。

まとめ

  • HHVMで配信しているWordPressだったら、特に苦も無くHackでプラグインが開発できそう
  • globalなど、Hack非サポートの機能を使う場合はPHPで記述してrequireすればよい!
  • add_action, add_filterの引数はfun関数で囲って渡す

ちなみに、型安全なのは実行時じゃなくて、チェック時なので、くれぐれも、本番環境で直接vimでコードをいじったりしないように。それじゃバグが見つかった瞬間に本番でバグってますから!w

僕は、開発用のフォルダを掘ってチェックOKだったらrsyncでwp-content/pluginsに同期するようにスクリプトを組んでます。恥ずかしいけどさらしときます。

#!/bin/bash
rsync -av --delete --exclude=.git ${1} ~/wordpress/wp-content/plugins/${1}

これ書くまでrsyncってリモートじゃなくても使えるなんて知らなかった程度のコマンド弱者ですががんばってます。w

では、
Have a good Hack life!

コメントを残す