MeCabエラー対応

PHPでmecab.soを認識できないときの対処法

更新日:2019/12/23

先日、PHPを7.2にバージョンアップしたところmecab関連のソースが動作しなくなりました。

どうしよう・・・

ということで簡単な調査と、対応策をお伝えします。

 

mecab.soとは

mecab.soは、文章を単語に分解する形態素解析をおこなうMeCabというオープンソースの形態素解析エンジンをPHPで使用するための拡張モジュールです。

使用するためにはソースファイルをダウンロードし、コンパイルや設定ファイルの書き換えが必要ですが、ネット上にインストールの方法が多数掲載されているので、基本的にはコピペで問題ありません。

僕もコピペでインストールしましたが何をやっているか全く理解していなかったため、今回のような問題が発生した際に、非常に苦労しました(汗

 

mecab.soが使用できなくなった経緯

PHPを7.2にバージョンアップしたところ、ブラウザから該当ページにアクセスすると何も表示されないという現象が発生。
デバッグを有効にして実行してみると、次のエラーが表示されました。

uncaught error: class 'mecab\tagger' not found

どうやらmecab関連の関数が使用できないようです。

mecab.soが無効になっている?

そこで次のコマンドをコマンドラインから実行して、拡張モジュールが有効かどうか確認してみます。

$ php -m | grep mecab

mecab <= これが表示されれば有効

有効になっていました。
試しに、コマンドラインから該当するPHPファイルを実行してみると、問題なく動作しました。

どうしてWebだと動かない・・・

PHPがZTSモードで動作しているのが原因だった

結論からいうと、PHPがZTS (Zend Thread Safe) モードで動いていました。
何も考えずに、他サイトからコピペして導入したのが原因です(汗
原因わかりました

ZTSは、PHPをマルチスレッド環境で動作させるモードです。

ZTSモードかどうかは、PHPソース上で次のように記述することで確認できます。

<?php

echo PHP_ZTS;

結果が1なら、ztsモード。
0ならNTS (Non Thread Safe) モードです。

またはphpinfo()の「Scan this dir for additional .ini files」の値が、「/etc/php-zts.d」などになっていると、ZTSモードの可能性が高いです。

僕の環境でブラウザからアクセスすると、1が表示されました。
しかし、コマンドラインから実行すると0が表示されます。

なんでーーー!

実はZTSモードは「php」ではなくて、「zts-php」が実行されるようです。
zts-phpで、PHP_ZTSを確認してみます。

$ zts-php -r "echo PHP_ZTS;"

PHP Warning: PHP Startup: Unable to load dynamic library 'mecab.so' (tried: /usr/lib64/php-zts/modules/mecab.so (/usr/lib64/php-zts/modules/mecab.so: undefined symbol: compiler_globals), /usr/lib64/php-zts/modules/mecab.so.so (/usr/lib64/php-zts/modules/mecab.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
1

1が表示されたので、ZTSモードになっているのがわかりました。
しかもエラーが出ていて、mecab.soの読み込みも失敗しているのがわかりますね(汗

なおコマンドラインで、-vオプションをつけてPHPを実行することでモードの確認をすることもできます。

$ php -v
PHP 7.2.26 (cli) (built: Dec 17 2019 14:06:22) ( NTS )

$ zts-php -v
PHP 7.2.26 (cli) (built: Dec 17 2019 14:09:17) ( ZTS )

mecab.soをマルチスレッドに対応させる

PHPがZTSモードで動作してるなら、拡張モジュールはマルチスレッドに対応したものでなければいけません。
現状のmecab.soは対応していないため、再生成する必要があります。

(1) 実行前に、サーバーに次のコマンドがあるか確認します。
※zts-phpではなくphpコマンドがZTSモードで動作している場合は、従来の方法で再生成するとうまくいくかもしれません。

$ which zts-phpize

/usr/bin/zts-phpize
$ which zts-php-config
usr/bin/zts-php-config
$ which mecab-config
/usr/bin/mecab-config

(2) php.iniを修正

・php.iniの所在確認

$ zts-php -i | grep "Loaded Configuration File"

Loaded Configuration File ⇒ /etc/php.ini

・extension_dirや、mecab.soのextension指定がある場合、削除します。

(3) php-mecab最新バージョンから、最新のtar.gzのURLを取得する。
例:https://github.com/rsky/php-mecab/archive/v0.6.0.tar.gz

(4) 次の手順でmecab.soを再生成します。

$cd /home/xxxx <=適当な作業用ディレクトリに移動

$ wget https://github.com/rsky/php-mecab/archive/v0.6.0.tar.gz <=(2)で取得したURL
$ tar xvzf v0.6.0.tar.gz
$ cd ./php-mecab-0.6.0/mecab
zts-phpize
$ ./configure --with-php-config=/usr/bin/zts-php-config --with-mecab=/usr/bin/mecab-config
$ make
$ make test
$ make install

最後に、次のように表示されればOKです。

Installing shared extensions:     /usr/lib64/php-zts/modules/

念のため、インストール先のディレクトリが既定のものかどうか確認します。

$ zts-php -i | grep extension_dir

extension_dir => /usr/lib64/php-zts/modules => /usr/lib64/php-zts/modules

(5) 設定ファイルを作成します。

・作成先ディレクトリの取得。

$ zts-php -i | grep Scan

Scan this dir for additional .ini files => /etc/php-zts.d

・「extension=mecab.so」を入力

$vi /etc/php-zts.d/mecab.ini

extension=mecab.so

(6) 確認

$ zts-php -m | grep mecab

mecab

(7) 再起動

$ systemctl restart httpd

 

ApacheをHTTP/2に対応したのが原因だった

今回PHPをバージョンアップしたのは、ApacheをHTTP/1.1からHTTP/2に変更する作業を行った際に、PHPの再インストールが必要だったためです。

ApacheにはpreforkMPM、workerMPM、eventMPMの3つのモジュールのうち一つが動いていて、デフォルトはpreforkMPMです。

しかしHTTP/2を利用する場合は、eventMPMに変更する必要があります。
今回はこの変更をしたために、PHPがZTSで動作していました。

httpdの設定ファイル群のなかに、次のようなファイルがありました。

/etc/httpd/conf.modules.d/15-php.conf

#
# PHP is an HTML-embedded scripting language which attempts to make it
# easy for developers to write dynamically generated webpages.
#

# Cannot load both php5 and php7 modules

<IfModule !mod_php5.c>
 <IfModule prefork.c>
  LoadModule php7_module modules/libphp7.so
 </IfModule>
</IfModule>

<IfModule !mod_php5.c>

 <IfModule !prefork.c>
  LoadModule php7_module modules/libphp7-zts.so
 </IfModule>
</IfModule>

この中で、preforkならlibphp7モジュールを読み込む。
preforkでなければlibphp7-ztsモジュールを読み込むという設定をしています。

HTTP/1.1をHTTP/2に変更したら、mecab.soを再生成する必要があるということですね。

原因がわかって、スッキリしました。

更新日:2019/12/23

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。