Movable Typeのプラグインを今の自分ならどう作るかをまとめてみる

# この記事は？

この記事は [Movable Type Advent Calendar 2019](https://adventar.org/calendars/4113) 21日目の記事です。

元々「Vue.jsにMTMLを移植してみよう」と思って進めていたんですが、どうしても上手くいかない部分があり断念（それはそれで別途まとめたい）。その後、AWS LambdaでMT Data APIを動かすべくトライしていたのですが、動作一歩手前で期日までに乗り越えられるか微妙な不具合を踏んだので断念（それもそれで別途まとめたい）。

丁度、先日 [こちらの記事](https://qiita.com/uehatsu/items/55504c8804bbede3a6bb) でMTの開発環境をローカルに建てた所だったので、「それならプラグインでも書いてみるか」と。「プラグインの書き方なんて今更書いたって」とも思ったんですが、逆に「今の自分だったら、どんな風にプラグインを作っていくのか」を手順を追って書いていくと、ニッチな所に需要があるかもしれないとポジティブに考えて進める事にします。

# 簡単に自己紹介

- 元Six Apart社員
- TypePad（現Lekumo）チームで開発 ⇒ Movable Typeチームでドキュメント（プラグイン開発ガイド、MTMLガイド、等） ⇒ 退社してからはMTプラグイン構築 ⇒ 現在はMTからは遠ざかってます
- 今はPHP(Laravel), node.js(Vue.js)を中心にFlutterやらKotlinやら雑食系エンジニアやってます
- 今回久々にAdvent Calendarに参加

# 開発環境

- MacBook Pro 2018 : macOS Mojave 10.14.6
- IntelliJ IDEA Ultimate 2019.3 + Perlプラグイン
- Docker for Mac 2.1.0.5(40693)

## OS

上にも書いてあるようにMac使いです。Catalinaにして痛い目にあったのでMojaveに戻しました。

## IDE

以前、RubyMineを使う機会があり、それ以来JetBrainsのIDEに惚れ込んでます。RubyMine ⇒ WebStorm ⇒ PHPStorm ⇒ Rider ⇒ IntelliJ IDEA Ultimateと流れて来ました。
Perlを使うにはサードパーティー製のPerlプラグインを入れる必要があるんですが、先日のIntelliJ IDEAのアップデートで突然 Perl Docker プラグインが動作しなくなり（IDE起動時にこける）、かなり焦りました。外すと動作するのと、結局使っていなかったので大事には至りませんでした。とはいえ、本当なら他の言語みたいにJetBrainsの純正サポートが欲しい所。Perlはもう、、、（言葉少なめ
PerlプラグインはWebStormやPHPStormでもインストールできるので、そちらを普段使っている人ならインストールして利用出来るはずです。お試しあれ。

## Docker

Six Apart在席時はVM WareにLinux入れて作業していた記憶があるのですが、もう昔過ぎて記憶の彼方。その後、VirtualBox + Vagrantを使って開発をし、今ではすっかりDocker + docker-composeで作業するようになりました。今回もDockerを使って開発を進めていきます。

# 今宵、どんなプラグインを作ろうか？

大学時代、動画圧縮演算の高速化を研究室でずっとやっていたのを今思い出しました。その際大事だったのは「一に計測、二に計測、三四がなくて、五に計測」。そもそもMTの再構築が遅いのがずっと疑問だったので、それを解く所まではいかなくても内部で何をやっているのかを計測する所までやってみようと思います。

名前は `Performance Counter Plugin` とでもしておきましょう（安直）。

# 開発開始

## Dockerにて環境を作る

上でも紹介した[記事](https://qiita.com/uehatsu/items/55504c8804bbede3a6bb)で作成したリポジトリからDocker関連のファイルをcloneします。

```bash:terminal
$ cd ${WORK_DIR}
$ git clone https://github.com/uehatsu/mt-docker.git
```

cloneしたらbuildします（しばらくかかります）。

```bash:terminal
$ cd mt-docker
$ docker-compose build
```

以下の位置にMovable Typeのコード一式を配置します。（2019/12/21 20:47修正 `Movalbe Type` => `Movable Type` 西山さん感謝）

`${WORK_DIR}/mt-docker/movabletype/` ← ここの直下に`mt.cgi`などが置かれるように

起動してみます。

```bash:terminal
$ docker-compose up
```

`mt-check.cgi`にブラウザからアクセスしてみます。動いているようです。

http://localhost/cgi-bin/mt/mt-check.cgi

<img width="1289" alt="スクリーンショット 2019-12-17 21.35.48(4).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65508/88a96144-cbf9-6ca4-66e8-a98b4048a653.png">

`mt-wizard.cgi`でmt-config.cgiを作ります。手順はここでは省略。

その他、諸々手動で設定を追加して`mt-config.cgi`は以下の感じになりました。特にプラグイン開発のために`PluginPath`を追加しているのがミソでしょうか。
`docker-compose.yml`と同じ階層の`plugins`ディレクトリが`/usr/local/src/plugins/`にマウントされています。こうすることでMT直下の`plugins`ディレクトリと`/usr/local/src/plugins/`ディレクトリの両方がプラグインを入れておけるディレクトリとして認識されます。
この後のプラグイン開発は`${WORK_DIR}/mt-docker/plugins/PerformanceCounter/`以下で行います。

```
#======== REQUIRED SETTINGS ==========

CGIPath        /cgi-bin/mt/
StaticWebPath  /mt-static/
StaticFilePath /usr/local/apache2/cgi-bin/mt/mt-static

#======== DATABASE SETTINGS ==========

ObjectDriver DBI::mysql
Database mt
DBUser admin
DBPassword pass
DBHost mysql

#======== MAIL =======================
EmailAddressMain test@example.com
MailTransfer smtp
SMTPServer mailhog
SMTPPort 1025
MailEncoding UTF-8

#======== PLUGIN PATH ================
PluginPath plugins
PluginPath /usr/local/src/plugins

#======== Memcached ==================
MemcachedDriver Cache::Memcached
MemcachedNamespace MT
MemcachedServers memcached:11211
    
DefaultLanguage ja

ImageDriver ImageMagick
```

# 開発

## MT Plugin開発最初の一歩

最初の一歩はやはりYAMLファイルを作って、プラグイン名を表示させるところでしょうか。

何年も前に自分で書いたであろう文章を紐解きます。

https://github.com/movabletype/Documentation/wiki/Japanese-plugin-dev-1-1

`${WORK_DIR}/mt-docker/plugins/PerformanceCounter/config.yaml`を作ります。

```YAML:config.yaml
id: PerformanceCounter
name: Performance Counter Plugin
version: 0.1
description: Performance Counter with Devel::NYTProf
author_name: Hatsuhito UENO
author_link: https://uehatsu.info/
doc_link: https://uehatsu.info/
```

MT管理画面の「システム＞設定＞プラグイン＞プラグイン設定」の一覧に「Performance Counter Plugin 0.1」が表示されていたらOKです。

## ついでなので多言語対応

多言語対応と言っても日英だけですが、こちらも以下のドキュメントを元に行っておきます。

`config.yaml`を以下のように書き換えます。

```YAML:config.yaml
id: PerformanceCounter
name: <__trans phrase="Performance Counter Plugin">
version: 0.1
description: <__trans phrase="_PLUGIN_DESCRIPTION">
author_name: <__trans phrase="_PLUGIN_AUTHOR">
author_link: https://uehatsu.info/
doc_link: https://uehatsu.info/
l10n_class: PerformanceCounter::L10N
```

`plugins/PerformanceCounter/lib/PerformanceCounter/L10N.pm`を以下のように作ります。

```perl:L10N.pm
package PerformanceCounter::L10N;
use strict;
use warnings;
use base 'MT::Plugin::L10N';

1;
```

この状態で管理画面を見て見ると、以下のようになっているはずです。

<img width="690" alt="スクリーンショット 2019-12-17 22.06.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65508/9068b81f-bbae-aa0a-21cd-5c9d4292e0a9.png">

`<__trans phrase="foo bar">`と書かれていたとすると、そのまま`foo bar`が表示されています。

`plugins/PerformanceCounter/lib/PerformanceCounter/L10N/en_us.pm`を以下のように作ります。

```perl:en_us.pm
package PerformanceCounter::L10N::en_us;

use strict;
use warnings;
use base 'PerformanceCounter::L10N';
our %Lexicon;

%Lexicon = (
    '_PLUGIN_DESCRIPTION' => 'Performance Counter with Devel::NYTProf',
    '_PLUGIN_AUTHOR' => 'Hatsuhito UENO',
);

1;
```

この状態で管理画面を見て見ます。

<img width="680" alt="スクリーンショット 2019-12-17 22.11.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65508/62c72d56-ec94-9bdd-ca80-af05656a4d99.png">

詳細（Description)と作者(Author)が英語表記に変わりました。L10N.pmを導入する前の情報になりましたね。

さて、最後は日本語化です。`plugins/PerformanceCounter/lib/PerformanceCounter/L10N/ja.pm`を以下のように作ります。

```perl:ja.pm
package PerformanceCounter::L10N::ja;

use strict;
use warnings;
use base 'PerformanceCounter::L10N::en_us';
our %Lexicon;

%Lexicon = (
    'Performance Counter Plugin' => 'パフォーマンス計測プラグイン',
    '_PLUGIN_DESCRIPTION' => 'Devel::NYTProf を利用したパフォーマンス計測を行います',
    '_PLUGIN_AUTHOR' => '上野 初仁',
);

1;
```

<img width="682" alt="スクリーンショット 2019-12-17 22.15.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65508/d77b0798-0a25-f050-24e8-c7e1aea22eb9.png">

日本語化されました。

ここまででドキュメントとは違ったことが書かれている事に気付かれた方もいらっしゃると思います。

`use warnings;`と`our %Lexicon;`です。

ドキュメントには`use warnings;`は書かれていません。`our %Lexicon;`も`use vars qw( %Lexicon );`と元々は書かれています。
これは、IntelliJ IDEAのPerlプラグインが「イマドキのお作法に則ってないよ」と教えてくれたので書き換えました。すでに10年近く前のドキュメントなので、お作法が変わっているのですね。最近のPerlのお作法は追っていないので勉強になりました。

## 先に進む前にDevel::NYTProfを試してみる

プラグイン開発を進める前に、ちょっと`Devel::NYTProf`を試してみたいと思います。

先ほど紹介した`mt-docker`には`Devel::NYTProf`がインストール済みなのですぐ使えます。他の環境の方は`cpanm Devel::NYTProf`としてインストールしてから進めてください。Pure Perlモジュールではないのでmakeが必要になります。

## 再構築用のデータを準備する

実際に計測を始める前に再構築時のパフォーマンスを計測したいのでテスト用のデータを準備します。
データの作成には[Postman](https://www.getpostman.com/)を利用します。

サイトIDが1となるようなサイトを作成し、そこにEntryをData APIを使って500件投入する事にします。Data API便利（今更感）。

- METHOD
    - POST
- エンドポイント
    - {{url}}/v4/sites/1/entries
- Headers
    - Content-Type
        - application/x-www-form-urlencoded
    - X-MT-Authorization
        - MTAuth accessToken={{accessToken}}
- Body
    - enry
        - {{entry}}
    - publish
        - 0
- Pre-request Script
    - （下記）
- Environment
    - url
        - http://localhost/cgi-bin/mt/mt-data-api.cgi
    - username
        - （ユーザ名）
    - password
        - （CMSのログインパスワードではなく、Webサービスパスワード）
    - clientId
        - （任意の値）
    - accessToken
        - （空欄）
    - sessionId
        - （空欄）
    - i
        - 0
    - entry
        - （空欄）

```javascript:Pre-request_Script
const postRequest = {
    url: pm.environment.get('url') + '/v4/authentication',
    method: 'POST',
    header: [
        {
            key: 'Accept',
            value: 'application/json',
        }
    ],
    body: {
        mode: 'urlencoded',
        urlencoded: [
            {
                key: 'username',
                value: pm.environment.get('username'),
            },
            {
                key: 'password',
                value: pm.environment.get('password'),
            },
            {
                key: 'clientId',
                value: pm.environment.get('clientId'),
            },
        ],
    },
}
pm.sendRequest(postRequest, function (err, res) {
    if (err) {
        console.error(err)
    }
    if (res.code === 200 && res.status === 'OK') {
        console.log(res.json())
        pm.environment.set('accessToken', res.json().accessToken)
        pm.environment.set('sessionId', res.json().sessionId)
        
        var i = pm.environment.get('i') * 1 + 1
        entry = {
            'title': 'Test Entry ' + i,
            'body': '卍'.repeat(1024)
        }
        pm.environment.set('entry', JSON.stringify(entry))
        pm.environment.set('i', i)
    }
});
```

上記のような設定で、PostmanのCollection Runnerから500回投入を実行すれば、'Test Entry 123'といった連番のタイトルのエントリーが500件作成されます。本文はなんとなく「卍」を1024文字連続させたものになっています。テストデータなので、まぁ、こんな所で。

結構な時間かかりますが、POST時に`publish`を0に設定してありますので投稿時の再構築はしていません。この設定しないと、もっと時間がかかるはずです。手元環境では30分ちょっとかかりました。

## mt.cgiにDevel::NYTProfをしかけてみる

では、mt.cgiを以下のように書き換えてみます。（mt.cgiはバックアップを取るなどしておいてください）。あと絶対本番環境では試さないでください。色々と問題がありますので。

```perl:mt.cgi
#!/usr/bin/perl -w

# Movable Type (r) (C) 2001-2019 Six Apart, Ltd. All Rights Reserved.
# This code cannot be redistributed without permission from www.sixapart.com.
# For more information, consult your Movable Type license.
#
# $Id$

use strict;
use Devel::NYTProf;
use lib $ENV{MT_HOME} ? "$ENV{MT_HOME}/lib" : 'lib';
DB::enable_profile("/usr/local/src/plugins/nytprof.out");
use MT::Bootstrap App => 'MT::App::CMS';
DB::disable_profile();
```

`Devel::NYTProf`をしかけた状態で、先ほどエントリーを500件しかけたサイトを再構築してみます。`Devel::NYTProf`自体がボトルネックになるので、通常よりも再構築はかなり遅くなります。
6分半ほどで再構築が完了しました。実行が終わると`plugins`直下に`nytprof.out`という計測結果を格納したDBファイルが出来ているはずです。このままでは見られないので、dockerコンテナに入って専用のコマンドを実行してHTMLファイルに整形します。

```bash:terminal
$ docker-compoase exec apache bash
# cd /usr/local/src/plugins/
# nytprofhtml -f nytprof.out
Reading nytprof.out
Processing nytprof.out data
Writing line reports to nytprof directory
 100% ... 
Extracting subroutine call data ...
Extracting subroutine links
Generating subroutine stack flame graph ...
```

`plugins/nytprof`というディレクトリが作られているので、ブラウザでこのディレクトリの中の`index.html`を開いてみます。

<img width="1291" alt="スクリーンショット 2019-12-18 00.24.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65508/cabb8345-63bb-4209-8589-6c9bdf792d6f.png">

色々と出てきます。`Top 15 Subroutines`のうち、一番上は`Devel::NYTProf`自体なので、それ以下を見ますが、あまりピンときません。その場合は、その下に書かれている`treemap of subroutine exclusive time`のリンクをクリックしてみます。すると関数一つを一つの四角として視覚的に全体が表示されます。

<img width="1276" alt="スクリーンショット 2019-12-18 01.07.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65508/9b04aea7-7f16-6d7d-bbbb-fa2ee09eba8b.png">

実際に出てきた結果を色々と見て見ると、`MT::Template::Handler::invoke`の辺りが香ばしいように見て取れます。手元の結果ではExclusive Timeに14.0ms、Inclusive Timeに1.97sかかっています。

<img width="765" alt="スクリーンショット 2019-12-18 01.06.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65508/b7dd20c0-eeda-3f0f-957d-c80b4c047778.png">

さて、これ以上深掘りしているといつまでたってもプラグインができないので、この辺りで`Devel::NYTProf`のお触りは終わらせましょう。`mt.cgi`をバックアップから戻しておきます。

## プラグインとしてDevel::NYTProfを仕掛ける場所を考える

先ほどは`mt.cgi`の動作全体に`Devel::NYTProf`をしかけました。プラグイン化する際もほぼ同じ挙動となるように、MTの動作の開始と終了時に`DB::enable_profile()`, `DB::disable_profile()`をしかけたいと思います。
（本当はリビルドの開始と終了に仕掛けたかったのですが、該当するフックポイントが見当たらず断念しました）

`config.yaml`を以下のように書き換えます。callbacks行以下が追加されていますね。

```yaml:config.yaml
id: PerformanceCounter
name: <__trans phrase="Performance Counter Plugin">
version: 0.1
description: <__trans phrase="_PLUGIN_DESCRIPTION">
author_name: <__trans phrase="_PLUGIN_AUTHOR">
author_link: https://uehatsu.info/
doc_link: https://uehatsu.info/
l10n_class: PerformanceCounter::L10N

callbacks:
  MT::App::CMS::pre_run: $PerformanceCounter::PerformanceCounter::Callbacks::pre_run
  MT::App::CMS::post_run: $PerformanceCounter::PerformanceCounter::Callbacks::post_run
```

以下の風になっていて、コールバックに対して、どんな関数を実行するかを指定します。

```yaml
calbacks:
  （コールバック名）: $（プラグインID）::（プラグインクラス）::（ハンドラ名）
```

ハンドラを`L10N.pm`と同じ階層の`Callbacks.pm`に実装してみます。まずはログ出力のみです。これでMTの動作スタート時にログに`pre_run`と表示され、動作終了時に`post_run`と表示されるはずです。

```perl:Callbacks.pm
package PerformanceCounter::Callbacks;

use strict;
use warnings;

use Time::HiRes qw(gettimeofday);
use Time::Piece;

sub pre_run {
    my ($cb, $app) = @_;

    my ($sec, $microsec) = gettimeofday();

    doLog(sprintf("pre_run: %s.%06d", localtime($sec)->strftime('%F, %T'), $microsec))
}

sub post_run {
    my ($cb, $app) = @_;

    my ($sec, $microsec) = gettimeofday();

    doLog(sprintf("post_run: %s.%06d", localtime($sec)->strftime('%F, %T'), $microsec))
}

sub doLog {
    my ($msg, $class) = @_;
    return unless defined($msg);

    require MT::Log;
    my $log = new MT::Log;
    $log->message($msg);
    $log->level(MT::Log::DEBUG());
    $log->class($class) if defined $class;
    $log->save or die $log->errstr;
}

1;
```

テストしてみます。MTの管理画面をリロードした上でシステムのログを見てみます。

<img width="830" alt="スクリーンショット 2019-12-20 23.15.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65508/b4056414-c4ad-8fc6-7aab-6c43fcd89dff.png">

ぱっと見`post_run`の後に`pre_run`が並んでいて変ですが、マイクロ秒まで見ると表示順が間違っていて、ちゃやんと`pre_run` => `post_run`の順番に動いているのが分かります。最後に`pre_run`が一個だけあるのはログ取得のAPIを内部で叩いた際のスタートを拾っているものと思われます（深追いはしません）。

これでMTの動作開始と終了を取れる事は確認出来たので、実際に`Devel::NYTProf`を仕掛けてみます。以下のように`Callbacks.pm`を修正します。

```perl:Callbacks.pm
package PerformanceCounter::Callbacks;

use strict;
use warnings;

use Devel::NYTProf;

sub pre_run {
    my ($cb, $app) = @_;

    DB::enable_profile("/usr/local/src/plugins/nytprof.out");
}

sub post_run {
    my ($cb, $app) = @_;

    DB::disable_profile();
}

1;
```

`nytprof.out`ファイルを削除した上で再構築を実施すると、同じ位置に`nytprof.out`が作られ、`Devel::NYTProf`がプラグイン経由で実施されたことがわかります。

## プラグイン設定でnytprof.outファイルの書き出し先を変更してみる

管理画面のプラグイン設定は以下の資料にやり方が書いてあります。

https://github.com/movabletype/Documentation/wiki/Japanese-plugin-dev-3-1

今回はシステム設定のみを利用してブログ設定は利用しないので、以下のように`config.yaml`を書き換えます。

```yaml:config.yaml
id: PerformanceCounter
name: <__trans phrase="Performance Counter Plugin">
version: 0.1
description: <__trans phrase="_PLUGIN_DESCRIPTION">
author_name: <__trans phrase="_PLUGIN_AUTHOR">
author_link: https://uehatsu.info/
doc_link: https://uehatsu.info/
l10n_class: PerformanceCounter::L10N

callbacks:
  MT::App::CMS::pre_run: $PerformanceCounter::PerformanceCounter::Callbacks::pre_run
  MT::App::CMS::post_run: $PerformanceCounter::PerformanceCounter::Callbacks::post_run

system_config_template: path_setting_system.tmpl
settings:
    path_setting_system:
        default: /tmp/nytprof.out
        scope: system
```

プラグインの`lib`ディレクトリと同じ階層に`tmpl`ディレクトリを作って、その中に`path_setting_system.tmpl`を以下の内容で作成します。

```html:path_setting_system.tmpl
<mtapp:setting id="path_setting_system" label="<__trans phrase='_PerformanceCounter_Path'>"
    hint="<__trans phrase="Please input the nytprof.out path.">" show_hint=1>
    <input type="text" name="path_setting_system" id="path_setting_system"
        value="<mt:GetVar name="path_setting_system">" />
</mtapp:setting>
```

L10Nファイルも修正しておきます。

```perl:en_us.pm
package PerformanceCounter::L10N::en_us;

use strict;
use warnings;
use base 'PerformanceCounter::L10N';
our %Lexicon;

%Lexicon = (
    '_PLUGIN_DESCRIPTION'      => 'Performance Counter with Devel::NYTProf',
    '_PLUGIN_AUTHOR'           => 'Hatsuhito UENO',
    '_PerformanceCounter_Path' => 'nytprof.out PATH',
);

1;
```

```perl:ja.pm
package PerformanceCounter::L10N::ja;

use strict;
use warnings;
use base 'PerformanceCounter::L10N::en_us';
our %Lexicon;

%Lexicon = (
    'Performance Counter Plugin'         => 'パフォーマンス計測プラグイン',
    '_PLUGIN_DESCRIPTION'                => 'Devel::NYTProf を利用したパフォーマンス計測を行います',
    '_PLUGIN_AUTHOR'                     => '上野 初仁',
    '_PerformanceCounter_Path'           => 'nytprof.out 出力パス',
    'Please input the nytprof.out path.' => 'nytprof.out の出力パスを入力してください。',
);

1;
```

プラグイン設定を見てみます。

<img width="949" alt="スクリーンショット 2019-12-20 23.50.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65508/ccfeb8a1-a326-ce07-2924-48cd783bc44c.png">

スタイルが当たってませんが気にせず先に進みます（というか今のMTのお作法分からない、、、）。

`Callback.pm`を以下のように書き換えます。

```perl:Callback.pm
package PerformanceCounter::Callbacks;

use strict;
use warnings;

use Devel::NYTProf;

sub pre_run {
    my ($cb, $app) = @_;

    DB::enable_profile(get_path());
}

sub post_run {
    my ($cb, $app) = @_;

    DB::disable_profile();
}

sub get_path {
    my $plugin = MT->component("PerformanceCounter");
    return $plugin->get_config_value("path_setting_system", "system");
}

1;
```

関数`get_path()`でプラグイン設定の`path_setting_system`が取れるようになっています。この状態でMTをリロードして、`/tmp/nytprof.out`が作成されているかをコンテナに入って確認します。

```bash:terminal
 docker-compose exec apache bash
root@246da37f8d78:/usr/local/apache2# ls /tmp/
nytprof.out
```

ありますね。では、次は設定でパスを`/tmp/nytprof_2.out`に変えてMTをリロードしてみます。

```bash:terminal
root@246da37f8d78:/usr/local/apache2# ls /tmp/
nytprof.out  nytprof_2.out
```

ちゃんと設定が保存された上で動いていますね。

# いきなりのまとめ

本当ならもうちょっとテスト周りなど突っ込んで書きたかったのですが、12月21日（土）の0:00を回ってしまったので、ここまでにしたいと思います。DIなど使いづらい所をどうやってテストするかなど含めて調べて書きたかったんですが、、、
そもそも今のMTプラグインの書き方のお作法を分かっていない状態で、古い知識のおぼろげな記憶と、自分の書いた大昔のドキュメントのみを頼りに書いたので、どこまで「今っぽいか」は自分では分からないまま。できればコメントで色々とお教え頂きたいです。
このプラグインで取得した結果を使って、MTのパフォーマンスチューニングできないかなと淡い野望を抱いておりますが、どうなることやら。
ともかく自分なりに「今風な開発環境」が作れたと思っております。さて、年末年始はプラグイン書くかな(^^)