- 表示できるチャンネルをアニメ以外にも拡大した: 各チャンネルを適当なカテゴリで分けたが、このカテゴリでいいの? というのは若干ある。REALITY SHOW は果たしてドキュメンタリーなのか? とか。あるいは、よくわからないチャンネルはすべてバラエティに突っ込んだがそれでいいのか? とか。これは文句が出たらその都度対応することにしよう
- 各チャンネル群を従来は table 要素でマークアップしていたが、CSS flex を用いるようにした。これによりナウじゃないブラウザでは表示できなくなった可能性がある。しかし 21 世紀も 16 年も経って flex に対応していないブラウザが存在していたならば、それはそれが一方的に根本的に全て何もかも悪い。という方向でそろそろ行きたい。なにしろ flex 自体は Presto Opera ですら対応しているのだ(といいつつ、Presto Opera での表示確認は一切していないのだが)
- 各番組の放映開始に併せて、虹裏では番組の実況スレというものが立つことが多い。このスレッドの本文を生成する助けになることを意図して、共有機能を実装した。共有機能というのはなんかよく意味がわからないが、生成したテキストをそのままツイートできるようにした上での命名だ。というのはこのツイート機能がないと「実況スレ本文生成」みたいな更によく分からない機能になってしまって、これを呼び出すためのボタンのラベルが冗長になってしまうからだ
- ところで本文を生成する際、特定のマーカーは番組の情報に置換される。たとえば [cci][start-at][/cci] が 2016/12/06(火) といった具合だ。ここで、2016年12月06日 の形式がいい! といった場合にはマーカーのオプションとして書式を付加することができる。[cci][start-at{%m月%d日(%a)%p%l:%M}][/cci] などと書くと {〜} の中身を strftime(3) で評価した結果で置換される
- 番組開始通知時のオプションとして、番組終了時に自動的にチャンネルのタブを閉じる機能を追加した
- 特にチャンネルの拡大を虹裏 img でアナウンスした時なのだが、投げ銭してもいい的なレスをいくつか受けた。ありがたいことだが、しかし所詮は「」の言うことであって、9割方口だけである。が、人の心を失っていないまっとうな「」もいることを期待して、一応そのための窓口は設けた。はやくお酒飲みたいなあ
- 通知機能のためのブラウザ拡張は、相変わらずその申請が降りるまでの時間が各ブラウザで違う。Chrome は機械的に 1 時間もすれば通る。Opera は人がレビューするが、大体 3 営業日くらいで通る(土日はお休みだ)。Firefox は、なんか以前 AMO 公式のブログや、あるいはニュースメールなどでいっぱい KAIZEN してレビューも早くなったよ!ほめて!的なアピールをやたら目にしたことがあったが、全然変化ないように思える。すなわち申請からだいたい 1 ヶ月はかかる見込みである。ぶっ飛ばされたいのか。wasavi と同様 Firefox 版だけはストアに置かない形式にするかもしれない
Cannot log in to wordpress!!
というわけでなんか書こうかと wordpress にログインしようとしたら、ログインページでログインボタンを押した途端に 403 Forbidden と書かれただけのページに飛ばされてログインできない。
どうもそのページは Apache 備え付けのエラーページのようだ。そこで、wp-login.php で適当にログを吐かせてみたところ、確かに GET でアクセスされた際はログが追記されるが、POST では何も書かれない。php の実行に入る前に Apache によって弾かれているようだ。
wp-login.php をバックアップして、wp-login.php の名前でもっとシンプルな、単に POST するだけのページを書き、試してみたところやはり 403 になる。どうも、Apache の静的な設定の段階で wp-login.php への POST アクセスを弾いているようだ。なんか、海外からのクラッキングを防ぐ的なアレなんだろうか。障害情報やメンテ情報の最近のエントリにはそれっぽいものは見つからなかったが…。
とりあえず .htaccess でアクセス制御を上書きすることで解決。解決…なのかどうか。いやまあ wp-login.php に対して Basic 認証くらいはかけているのだが。
The TV program time table #6: regalized program name
あべアニで番組開始の通知をする際「のんのんびより 開始1分前です」的な読み上げを行わせることができる。PhoneticNews に引き続き、この読み上げは voicetext を利用している。ただまあ仕方ないことだとは思うが、たまに正しく読み上げないことがある。忍ペンまん丸をしのぶぺんまんまるなどと読み上げたりする。これをなんとかできないだろうか。
アニメ作品のデータベースとしては animedb というプロジェクトがあり、その中で作品名のふりがなも管理されている。これを利用できるかもしれない。
ただ、
- ドキュメントにも記載されているが若干表記の揺れが残っている。たとえば鷹の爪で grep すると
$ grep "鷹の爪" google-ime-dict.txt
ザフロッグマンショーヒミツケッシャタカノツメ THE FROGMAN SHOW「秘密結社鷹の爪」 固有名詞
ヒミツケッシャタカノツメザムービーソウトウハニドシヌ 秘密結社 鷹の爪 THE MOVIE ~総統は二度死ぬ~ 固有名詞
ヒミツケッシャタカノツメザムービーツーワタシヲアイシタクロウーロンチャ 秘密結社 鷹の爪 THE MOVIEⅡ ~私を愛した黒烏龍茶~ 固有名詞
ヒミツケッシャタカノツメカウントダウン 秘密結社鷹の爪カウントダウン 固有名詞
ヒミツケッシャタカノツメ 秘密結社鷹の爪 固有名詞
ヒミツケッシャタカノツメザムービースリータカノツメジェイピーハエイエンニ 秘密結社 鷹の爪 THE MOVIE 3 http://鷹の爪.jpは永遠に 固有名詞
ヒミツケッシャタカノツメザムービーフォーカスベルスキーヲモツオトコ 秘密結社鷹の爪 THE MOVIE 4 カスベルスキーを持つ男 固有名詞
タカノツメネオ 鷹の爪 NEO 固有名詞
ヒミツケッシャタカノツメジェーピー 秘密結社鷹の爪.jp 固有名詞
タカノツメマックス 鷹の爪 MAX 固有名詞
タカノツメゴーウツクシキエリエールショウシュウプラス 鷹の爪GO 美しきエリエール消臭プラス 固有名詞
ヒミツケッシャタカノツメドットジェイピーブルーレイボックスジョウカンカンゼンシンサクエイゾウ 秘密結社 鷹の爪.jp Blu-ray BOX上巻[完全新作映像] 固有名詞
シネマトラベルタカノツメタカノツメダンシネマトラベルヘイクノマキ シネマ・トラベル × 鷹の爪 鷹の爪団! シネマ・トラベルへ行くの巻! 固有名詞
ヒミツケッシャタカノツメドゥー 秘密結社鷹の爪 DO 固有名詞
などと「秘密結社」の有無、あるいは「秘密結社」に続いて空白が入っているかなどが揺れている。それともそれぞれの作品で正式名称の表記が揺れているのが正確な状態なんだろうか? よく知らない - 上記の例で Blu-ray BOX 上巻云々が含まれているものがあるがこれは作品名なのか? 製品名ではないのか?
- タイトルに含まれる空白がよみがなでは省略されているが、これをそのまま読み上げさせると不自然なアクセントになってしまう。できればよみがなでも空白は維持してほしい。Google 日本語入力用の辞書ファイルなのであえてそうなっているのかと思ったら元データである animedb.yml でも同一なのでそういうわけでもないようだ
というわけで、作品名の正規化に用いるには若干難しいかもしれない。しかし膨大なデータなのは確かなので何かに利用したいなあ。
The TV program time table #5
設定機能及び番組開始時の通知機能を実装した。
番組枠のポップアップに「通知」というボタンが追加されており、それを押すと予約される。時間になると自動的に Abema.tv の該当チャンネルを開いたり、音声でガイドしたり、OS が持つ通知機能を通してメッセージを表示したりする。これらの組み合わせは設定パネルで好きにできる。
その他、
- 予約したら、あべアニのページは閉じて良い
- 該当チャンネルを開く際、すでに何らかの Abema.tv 上のチャンネルを開いていたらそのタブを再利用する
といった特徴がある。
このような機能を実装するとして、最も単純なのは当然、あべアニのページで [cci]setTimeout()[/cci] することだ。これなら何も実装上で難易度の高いものはない。しかしやはり当然ながら、この方法だとあべアニのページをずっと開きっぱなしじゃないといけないのである。もしも実際のユーザの使用状況が、いったんあべアニを開いたらずっと開きっぱなしであるならこの方法で実装してもいいが、そうではないなら別の方法を考えないといけない。
というわけでアクセス解析を見てみたところ、だいたいあべアニを開いて閉じるまでが5分以内のユーザが62.37%、1時間以内なのが17.8%とかそんな感じだった。つまり、setTimeout 作戦ではダメなのである。
そこで、今流行りの Push API という規格を検討してみた。これは setTimeout 作戦との比較で言えば、ここの Web サーバが setTimeout の役目を肩代わりする。そして通知をすべき時間になったら、Push サーバというものを通してブラウザに通知を送る…という仕組みだ。かつてはブラウザへの Push といえばブラウザ側から接続をかけて long polling という手法がとられた。この規格を実現している Google と Mozilla の Push サーバが未だそういう手法なのかは知らないが、いずれにしても単純な http ではない特殊な接続方法でブラウザと通信する必要があるようなので、Push サーバが必要になる。
しかし検討した結果、これも実装したい機能にはちょっと力不足だった。
- 新しい規格なので、また仕様が固まっておらず、各種言語向けのライブラリなども揃いきってない。ペイロードを含んだ通知などではかなりめんどくさい暗号化を施さないといけないので一から作るのはちょっと大変。Node.js だと楽そうなのでビルドを試してみたがここのサーバの glibc のバージョンが異様に古くて動かない
- ここの Web サーバが通知を送るタイミングを管理するということは、一定期間ごとに適当なプログラムを自動起動させないといけない。実はここのサーバでも一応 crontab は編集できるのだが、それは1時間に1回までという限度があるのだった。もっと細かい時間単位で起動させるには別のサーバを使わないといけないので面倒
- ブラウザが通知を受け取ったあと、チャンネルを開くとして既存のタブを再利用するという芸当が多分現状の仕様ではできない。将来、API が拡充されればできるようになるかもしれないが、今はできない
というわけで、現実的な解としてブラウザの拡張機能との組み合わせで実現することにしてちょちょっと作った。
ナウいブラウザの拡張機能といえば、なんと言っても Firefox の WebExtensions である。この際なので WebExtensions で作ってみた。実際は作ってみたというほどのことではなくて、単純に Chrome 版のソースディレクトリを与えたらだいたい動いた、すごい! という程度のものだが。
それから当然ながら Chrome 版は Opera でもだいたい動くので Opera 版も作った。
だいたい動くというのは、例えば [cci]chrome.storage.sync[/cci] や [cci]chrome.runtime.onInstalled[/cci] など Firefox や Opera では微妙に実装されていないものがあるのでそういうものを使うのは避けないといけないということだ。このせいで拡張機能をインストールしたあといったんあべアニをリロードしないといけない。
ちなみに Chrome の拡張をパク…大いに参考にしたという意味では Edge でも動くはずだが、なんと Edge の場合 [cci]chrome.alarms[/cci] すら実装されていないそうな。もうちょっとがんばってくださいよ MS さん。
The TV program time table #4
いくつか、ユーザごとにカスタマイズできる項目を作りたい。そのためにとりあえず歯車アイコンを配置した。
ところで最近はこの手のページ全体に対する設定やらアクションやらを担当するリンクとしていわゆるハンバーガーアイコンがよく用いられるのだが、あれすごくダサいと思う。ダサいしそれを押すことで何が起きるのか連想できないという機能上の問題も抱えている。早くこの世からなくなって欲しい。
それはさておき、設定可能な項目をつらつらと挙げてみると:
- 各チャンネルのロゴ、番組枠及びその詳細リンクそれぞれをクリックした際のウィンドウ名: これを [cci]_blank[/cci] にすればクリックするごとに新しいタブが開く。特定の名前にすればクリックした時に既にそれが存在すれば再利用される(もちろんユーザにはウィンドウ名自体は重要ではないので、設定の際は単に「クリックした時タブを再利用するか否か」と簡略化される)
- 翌日の番組表へのリンクの背景として何時間分をチラ見せするか
- 番組枠上の詳細ポップアップの可否
- 表示するチャンネル、及びその並び
- 事前通知する場合の、通知方法
このうち 3. と 4. はどういう仕様にしたものかいろいろと考えることが必要だ。
The TV program time table #3
1 クール(12〜13話)分の一挙放送、といった番組は番組表においても縦方向に非常に長い面積を占める。その枠の先頭部分にタイトルとサムネイルを描画するわけなのだが残りの部分は全くの空白なので、始まって数時間もすると何を放送しているのか分からないただの枠が番組表に残るだけになる。これを何とかしたい。
これを何とかするのは、近年よく広告に使われる sticky positioning だ。sticky というのは日本語で言えばネバネバだ。ページをスクロールした時にできるだけビュー内にとどまろうと粘っこい動作をするアレである。現実には広告によく使われるが、本来 sticky な動作をして嬉しいのは上下に長い表のヘッダ部分などである。あべアニで言えばそれは正にチャンネル名の部分で、既にそういう動作をするように仕掛けてある。同じことを、それぞれの番組枠にも適用すればいい。
ところでこの sticky positioning というのは CSS3 の規格でまだ策定中の段階であり、現時点では Firefox を除くブラウザにはまだ実装されていない。なので javascript による Polyfill を使うことになる。そういうわけであべアニでは StickyFill というライブラリを使っている。ところが残念なことに、このライブラリに対してそれぞれの番組枠を sticky にするように指示しても上手くいかないのであった。上手くいかない上にスクロールもかなり重くなる。
Polyfill でやるべきことは
- window の scroll イベントをリスンして
- sticky 動作の対象となっている要素を走査し、その領域内にビューの上端が含まれていたら要素を position:fixed にする。このとき、その親要素の領域内に収まることを優先しつつ、要素の内容がビューに収まるように位置を微調整する。それから、領域の周辺の要素の位置を動かさないためにダミーの置換要素を生成したりも必要ならする
ということにつきる。しかしやることは単純だが、重い。まず scroll イベントは相当な頻度で発生する。それから要素を走査する際にその配置情報を逐一取得するのも重い。StickyFill は汎用的に使えるようにするためにその辺を素直にやっているので重い。
そういうわけで仕方ないので番組枠の sticky 動作についてはページに最適化しつつ自前で書いた。たとえばいったん計算したら使いまわせるもの(margin や border のサイズ)は積極的にキャッシュしたり、そもそも sticky 動作させる必要のない番組枠(内容の高さが枠の高さ以上あるもの)は積極的に除外したりだ。それでもブラウザがスムーズスクロールする設定にされていると若干描画がバタつく。早くブラウザでネイティブサポートされて欲しい。
ついでに言うとネイティブサポートの際は stuck 状態かどうかを取れる CSS の擬似クラスを定義してくれるととても助かるのだが。そうすると stuck したときに限り box-shadow をかけるなどの小細工ができる。
Abeanibot introduced #2
前の記事で、
検索結果をローカルに保持してある sqlite のデータベースと照合する。新しいものがあればそれをツイートする
と書いた。ここのやり方は sqlite ならではのものになっているので一応メモしておく。
普通に考えると、番組 A が既にデータベースにあるかを照合し、その結果によって処理を振り分けるには
- [cci]select * from program where name=:A[/cci] する
- レコードセットが行を返さなかったら [cci]insert into program (name) values (:A)[/cci] する。そしてツイートする
と、1 番組につき 2 回クエリを発行しなければならない。これは無駄な感じがするので、例の sqlite 側の関数の実体を php で実装できる機能を使う。
$db = new SQLite3("program.db");
$db->createFunction("php_name", __NAMESPACE__ . "\\phpName");
$insertedNames = [];
function phpName ($name) {
global $insertedNames;
$insertedNames[$name] = 1;
return $name;
}
こんな感じにしといて、[cci]insert or ignore into program (name) values (php_name(:A))[/cci] とすると、番組 A がテーブルに存在していなかった時のみ php_name 関数が呼ばれ、そして行が追加される。php_name 関数自体は何も副作用をもたらさないのだがこの時 PHP 側に制御がいったん移るので、そこで適当なものに覚えておく。こうすると 1 番組につき 1 クエリで済むようになる。
ところで createFunction の第 2 引数には PHP 側の関数を指定する。PHP で関数オブジェクトを指し示す方法としては、関数名の文字列、[cci]array([$this object], [method name string])[/cci] のような配列、クラス名を前置した静的メソッド名文字列などが段階的に追加されてきた。加えて最近の PHP では function リテラルそのものも使えるようになっている(これは Closure クラスのインスタンスが実体だ)。
この段階的というのがミソで、sqlite3::createFunction は最も初歩的な関数名文字列しか対応していないっぽいのである(かろうじて、名前空間は認識するが)。このへんの「ううn…」さがまあ PHP らしさなのだが。
Abeanibot introduced
あべ☆アニ関連でちょっとした Twitter 上のボットを作った:
あべアニ ぼっと
https://twitter.com/Abeanibot
これは AbemaTV 上で新しく配信されるらしいアニメをつぶやく。例えばこんなふうに:
「てーきゅう3期」 10月08日 05:46から #AbemaTV 深夜アニメチャンネルで放映開始です
https://t.co/nWb49K4IGW pic.twitter.com/YAipB4aUcF— あべアニ ぼっと (@Abeanibot) September 25, 2016
新しくというのは、そのままの意味だ。例えば、すでにのんのんびよりは何度も配信されているので、もしまた再度第 1 話から配信されることになったとしてもそれはツイートされない。一方もし将来まじぽかの配信が決定した場合はそれはツイートされる。
どのように対象の番組を抽出しているか。情報源としては番組表データと、検索結果のデータが考えられるが、今回あべアニぼっとでは検索結果を使用している。というのは番組表データは当日から 1 週間先のものまでしか得られないが、検索であれば更にそれを飛び越えた期間の番組データを得ることができるからだ。
そういうわけでバックエンドでは 1 時間ごとに PHP スクリプトを起動し、AbemaTV の検索 API に対して 2 つのクエリを投げる。なぜ 2 つかというとこの API には若干の癖があるようで、どうも検索結果が 100 件を超えないように調整されるような感じがするのである。AbemaTV のアニメ系チャンネルの場合、タイトルに含まれる話数は必ず [cci]#\d+(〜\d+)?[/cci] の形式になっているようなので、第 1 話から新しく配信されていそうな番組を検索するには [cci]#1[/cci] で検索する。しかしこれには #10 とかも含まれて、直近の番組で 100 件近くに達してしまう。なのでこのクエリだと 1 週間先、2 週間先の新番組を抽出できない可能性がある。
そこで次に、[cci]#1〜[/cci] でも検索する。これなら確実に第 1 話の番組だけを抽出できる。ただしこれの場合複数話連続している配信形態であることが前提になるので、第 1 話を単体で配信するパターンに対応できない。それをカバーするのは最初に投げたクエリである。
この 2 つの検索結果をマージすることで最終的な結果が得られる。ここまで読めば分かるように、この方法だと 1 週間先、2 週間先で、かつ第 1 話を単体で配信するパターンの番組は検索から漏れてしまう可能性があるのだが、まあ仕方がない。クエリに単語区切りを指定するような特別な演算子を含めることができれば解決するのだが、クエリがどのような文法になっているのか分からない。
いずれにしても得た検索結果をローカルに保持してある sqlite のデータベースと照合する。新しいものがあればそれをツイートするという流れだ。
ところで Twitter ボットを作ったのは自由帳ボットに続いて 2 つめなのだが、どちらも純粋にツイートするだけの機能しかない。巷の高機能なボットはフォロー返しや、メンションに対するある程度の反応を行うものもある。これらのボットにもそういう機能が必要だろうか? 要調査。
server status
先週ふたばのみならず、多くのサイトが DDoS 攻撃に晒されサービスを正常に提供できなくなる事件が起きた – 経過。その攻撃自体は今は沈静化している。また、ふたば側ではユーザーとサーバとの間に CloudFlare を挟むようになり、DDoS 耐性が増強された。
で。ふたばに関してはサーバの状態を観測するには公式の接続テストページか、拙作のふたば鯖☆偽監視所がある。今回はふたばの全サーバが攻撃されたので、公式の接続テストページは本来の役目を果たさない。従ってサーバの状態を確認したいユーザのアクセスが偽監視所に集中し、今まではだいたい日に 100 件とかそこらのアクセスだったのが、400000 アクセスとかに増えるようになった日が数日続いた。
偽監視所にアクセスして返される内容は gzip された静的な html であり、内容に含まれるグラフっぽい何かはすべて CSS で表現される軽量なテキストデータなので、サイズは高々 10 KB 程度だ。それでも、400000 アクセスされると一日の転送量は 3GB くらいになる。3GB というのは xrea の一日の転送量リミットの上限の数値だ(ただ、これが絶対の上限だと明言されているわけではない。これを超えると何がどうなると明言されてるわけでもない。このゆるゆるでテキトーでいい加減な管理ポリシーが今となっては xrea の最大の魅力だ)。従っていますぐ xrea を追い出されるということではないのだが、もしも転送量が倍々ゲームになるようだといろいろと対策を考えないといけない。
というわけでいろいろ改修を施した:
- PHP のバージョンが上がることによって発生していた warning を潰した。これらはクラスのインスタンスを保持するのに [cci]=&[/cci] を使っていたとか、クラスのメソッドを static に呼び出していたという、PHP4 からの移行期の遺物だった。なにしろ偽監視所を作ったのは 2010 年のことでその後ずっとほったらかしのまま稼働させていたので仕方がない
- 従来の仕様では、同じ IP アドレスを持つ複数の仮想名を持つサーバに関しては最初の 1 つだけ HEAD を飛ばし、それを残りのサーバにも適用していた。しかし今回 CloudFlare を挟むようになったことですべてのサーバが見かけ上同一の IP アドレスを共有するようになったので、HEAD は律儀に個別に飛ばすようにした
- 従来の仕様では、サーバ名から IP アドレスを正引きのは単に [cci]gethostbyname()[/cci] していた。今回やはり CloudFlare を挟むようになったことで、複数の CloudFlare 側のアドレスが割り当てられるようになり、単に [cci]gethostbyname()[/cci] を呼ぶだけではラウンドロビンにより呼び出しの度異なるアドレスが返されるようになった。そこで [cci]gethostbynamel()[/cci] を使うようにし、ソートした上で先頭の IP アドレスを使うようにした
- ユーザに返す html は 3 分ごとにリロードするようにしていたが、5 分 15 秒ごとにした
もうひとつ重要な心残りがある。偽監視所のグラフはいわば時系列を示している。しかし一般的な時系列グラフが、右端が最新を示すのに対して偽監視所は全くその逆で左端が最新なのである。6 年前の自分がなぜそういう仕様にしたのかこれっぽっちも全く覚えちゃいないのだが、今回見なおしたらやたら気になった。そこで虹裏に適当なスレが立った時に「」に聞いてみたところ、慣れたのでこれでいいという言質がとれた。これはつまり、これ以降「このグラフの仕様変じゃね?」という意見が出たとしても「」の総意ですからということでおあしすできるということだ…!
Enable mecab on xrea server
ぼちぼちあべ☆アニに検索機能を追加したい。この機能は:
- 当日から1週間先までの範囲内で
- タイトル、説明に含まれる任意の文字列を全文検索する
という仕様にしておこう。まずサーバ側で必要なものを考えるととりあえずは全文検索機能を備えたデータベースだ。以前タテログを作った時は MySQL を使った。しかし xrea サーバ上の MySQL というのは、当然だがルートユーザの権限が必要なチューニングは一切できないのがネックだ(実は裏技がなくもないのだが、それをここでは公開できない)。タテログの場合も全文検索用のメモリの割り当てをもうちょっと増やしたいのだが、いかんともしがたい。そういう訳で今回は SQLite を使ってみたい。
次に必要なのは、日本語を形態素解析するライブラリだ。タテログの場合は大掛かりな機構を使わず、2-gram で済ませたのだがやはりこのへんはきっちりやりたい。フリーの形態素解析器といえばなんといっても Mecab とか Chasen とかだ。これらを xrea 上で使えると嬉しい。さらに欲を言えば、これらを子プロセスとして呼び出すのではなく PHP エクステンションとして扱えるとなお良い。その場合のバインディングは php-mecab を使うことになる。
さて、実は xrea サーバにはすでに mecab はインストールされている。ただし、バージョンは 0.93 と若干古い。また、PHP バインディングは用意されていない。なので今回はこれは使わず、自前で mecab と php-mecab を xrea サーバ上でビルドすることにしよう。幸い xrea サーバには ssh でログインでき、また gcc 等々のビルド環境及び PHP エクステンションをビルドするための phpize ツールなどもあらかじめインストールされている。
Mecab と辞書のビルド
- 作業用ディレクトリを予め掘っておく。今回は ~/devel (/virtual/akahuku/devel) とした
- Mecab 公式からソースを落としてくる
$ tar zxf mecab-0.996.tar.gz
$ cd mecab-0.996
$ ./configure --prefix=$HOME --enable-utf8-only --with-charset=utf8
$ make
$ make check
$ make install
- 同様に辞書を落としてくる: とりあえず今回は IPA 辞書を落とした
$ tar zxf mecab-ipadic-2.7.0-20070801.tar.gz
$ cd mecab-ipadic-2.7.0-20070801
$ ./configure --prefix=$HOME --with-mecab-config=/virtual/akahuku/bin/mecab-config --with-charset=utf8
$ make
$ make install
php-mecab のビルド
PHP エクステンションをビルドするには、前述の phpize を使う。これ自体はちょっとしたシェルスクリプトで、php のインクルードファイル等々を走査しつつ configure スクリプトを生成する。ここで困ったことが 1 つある。xrea のサーバは複数のバージョンの PHP が同時にインストールされていて、コントロールパネルからどれを使用するか選択できるのだが、どれを選択しても /usr/local/include/php 以下の内容は最も古い php5.3 のもののままなのである。従って他のバージョンの PHP 向けに普通に make すると php 本体とエクステンションのバージョン不整合が起こり認識されない。ちなみに appsweets.net で使用している PHP は 5.5.35 だ。そこで:
- PHP 公式から該当 PHP のソースを落として、展開して、configure *だけ* やっておく
- php-mecab のソースディレクトリ(今回は /virtual/akahuku/devel/php-mecab/mecab/)で
$ php55ize
$ ./configure --prefix=$HOME --php-config=/usr/bin/php55-config --with-mecab=/virtual/akahuku/bin/mecab-config
- 生成された Makefile を vi で開き、/usr/local/include/php を含んでいるマクロの該当部分をすべて自前の PHP のソースディレクトリ(今回は /virtual/akahuku/devel/php-5.5.35)に置き換える
$ make
$ make test
- make install はしない(というか権限の関係上できない)。modules ディレクトリに出力された mecab.so をそのまま使う
PHP への組み込み
xrea の場合、現在は PHP を各ユーザの ~/.fast-cgi-bin 以下の php.ini 及び起動スクリプトによって起動させている。php55.ini を vi で開き
extension="/virtual/akahuku/devel/php-mecab/mecab/modules/mecab.so"
mecab.default_dicdir="/virtual/akahuku/lib/mecab/dic/ipadic"
を追記。
あとは fcgi による php が再生成された時に phpinfo(); して mecab が組み込まれていることを確認。
* * *
という感じになる。これにより、
$mecab = new MeCab_Tagger("すもももももももものうち");
echo $mecab->parse($str);
というように PHP から直接 Mecab の機能を使えるようになる。ここで更にもうひとひねりある。PHP の SQLite には面白い機能がある。SQLite3#createFunction() で SQL のクエリ内で使用できる関数を PHP の関数で自由に定義できるのである。つまり、PHP 側で
function php_tokenize ($arg) {
return (new Mecab_Tagger(["-O" => "wakati"]))->parse($arg);
}
$db = new SQLite3("foo.sqlite");
$db->createFunction("tokenize", "php_tokenize");
などとすると、クエリで直接
insert into table (content, tokens) values ("すもももももももものうち", tokenize("すもももももももものうち"));
などと書ける。これは面白い。データベースが PHP と同じプロセスで動いているからできる芸当だ。文字列のマーシャリングとか問題ないのか若干心配がないこともないが…。
ところで createFunction()、createAggregate()、createCollation() を使う際には第一に気をつけるべきことがある。もしその PHP 側のコールバック関数内で例外が発生して PHP の実行が中断された場合、SQLite 側のトランザクションは正しくロールバックされず、ジャーナルファイルが作られたまま、そしてデータベースファイル自身はロックされたまんま(ロック元は、mod_php 環境であれば http サーバだ。fcgi 環境では知らない)になってしまう。つまりコールバック関数内では出来うる限りガチガチにエラーチェックする必要があるということだ。