WordPressのソースを検索するのに便利なコマンド

今日の記事は、WordPress開発で便利なコマンドの話です。

WordPressのプラグインを開発していると、フックを探したり特定の関数のソースを見る必要があったり、定数を確認したかったり、いろんな場面でWordPressのソースを検索する必要があります。

以下はLinuxやMacのターミナルでそれらを検索する方法です。

WordPressはコーディング基準が定められているので、こういう時には超便利です。

コマンドで複数ファイルの検索を行うには?

私は、find と xargs、grep という3つのコマンドを組み合わせて検索しています。

find /path/to/wordpress -name "*.php" | xargs grep "foo"

上記のコマンドでは、/path/to/wordpress ディレクトリ内(サブディレクトリも含む)の全ての.phpファイルから、ファイル内に”foo”が含まれるファイルを検索しています。

WordPressの全てのアクションフックを検索する。

あらかじめWordPressのディレクトリに移動してから以下のコマンドを入力してエンター。

find . -name "*.php" | xargs grep "do_action("

WordPressの全てのフィルターフックを検索する。

find . -name "*.php" | xargs grep "apply_filters("

WordPressで定義されている全ての定数を検索する。

find . -name "*.php" | xargs grep "define("

WordPressの特定の関数のソースを探す

たとえば、get_option() のソースがどこにあるのかを知りたければ、以下のようなコマンドで。

find . -name "*.php" | xargs grep "function get_option"

具体的に開発中にどう使うの?

たとえば、コンテンツthe_content()の中身を修正するプラグインを作成するには、the_content()のソースがどのファイルにあるのかを探します。

find . -name "*.php" | xargs grep "function the_content"

上記のコマンドを実行すると wp-includes/post-template.php というファイルにあるのがわかります。

で、実際にソースを見ると、the_content というフィルターフックがあるのがわかるので、そのフックを使用して書き換えるといった感じです。

実際には、対象の関数からさらに別の関数がコールされていたりするので、何回もこのコマンドを打つ感じになりますが。
(お役所のたらいまわしに似てるかも。)

「さくらのVPS」上のWebサーバーのパフォーマンス向上

このサイトは「さくらのVPS」を使用しているのだが、徐々にパフォーマンスが低下するという現象が発生するようになった。
ひどい時には1ページの表示に30秒以上かかる。。。

Apacheを再起動すると状況が改善するため、ApacheとかPHPのチューニングを行うことにした。

以下は次回以降「さくらのVPS」を使用する際のメモということで。

httpd.conf の修正

以下のように設定を変更した。

  • MaxKeepAliveRequestsの値を200から50に変更
  • KeepAliveTimeoutの値を5から2に変更

以上はTCPコネクションの無駄遣いを減らすために行った。
今回のように徐々にパフォーマンスが低下するのは関係ないような気もするけど、そのあたりは詳しくないので、これはなんとなく。

  • PreforkのMaxRequestsPerChildの値を4000から50に変更

このサイトはPHP(WordPress)で動作しており、こまめにプロセスを生成しなおしてメモリーリークなどに備えたいと考えたので。
Apacheが徐々に重くなったのは、この設定で改善されると期待している。

memcacheの導入

以下のコマンドを実行して必要なパッケージをインストールした。
インストールした後は、memcachedの起動とApacheの再起動をしただけで特に設定の変更は行わなかった。

# yum install memcached
# yum install php-pecl-memcache

これはかなり効果があった気がする。
ただ、一時的にパフォーマンスが改善しても、今回のように徐々に重くなった事に対しては、上述のhttpd.confの変更が重要な気がする。

WordPressのキャッシュプラグインについて

いれようかどうしようか迷ったのだが、結局入れなかった。

どのプラグインを入れるべきか悩んだ挙句に面倒になったのと、今回は直感的にボトルネックがWordPressではないような気がしたので。。。

現時点ではキャッシュ系のプラグインをインストールしなくても状況は大幅に改善された。

その他

今回の現象はこれをやっても改善されなかったが、数日前に一応対策済み。

[001387]さくらのVPSで「CentOS」を利用していますが、回線速度が遅くアクセスに時間がかかります。 | FAQ Search – さくらインターネット

Webサーバーの稼働状況を定期的にチェックする

先日データーセンターでネットワークトラブルがあって、そのトラブルそのものはうちの問題ではなかったので、おまかせにしていたら数時間で復旧したのだが、トラブルに弊社が気づくのが遅れてしまい、お客様に余計な心配をかけてしまった。

そんなわけで、外部のサーバーからWebサーバーの稼働状況を監視するPerlスクリプトを作成して、Cronでぶんまわすことにした。

仕組み

仕組みはとても簡単で、ざっと以下のような感じ。

  1. コマンドライン引数に渡されたURLに対してwgetを実行する。
  2. 実行した結果を評価して、エラーがあればメールを送信する。

という感じ。

wgetコマンドを実行する際に実際にファイルをダウンロードしちゃうと面倒なので、以下のようなオプションをつけた。

my $com = "/usr/bin/wget -q -o /dev/null --spider '".$url."'";

ちなみに、pingだとネットワークトラブルしか検出できないが、wgetならネットワークが生きていてapacheが死んでるなんていうシチュエーションも拾ってくれる。

使い方

以下のように、このPerlスクリプトにURLを引数で渡して、Cronに登録する。

*/10 * * * * /path/to/lifecheck.pl http://www.example.com/

ちなみに、Cronに自前スクリプトを登録する際は環境変数によって挙動が変わったりするので、以下のように環境変数をundefしちゃうと都合が良いと個人的に思うのだが。みんなはどうしているんだろう?

undef %ENV;

ソース

以下のソースをコピペして、lifecheck.plというファイル名で任意のパスに保存する。

#!/usr/bin/perl -wT

undef %ENV;

use strict;
use warnings;
use Encode;
use utf8;
use Mail::Sendmail;

my $from 	= 'admin@example.com';
my $to 		= 'you@example.com';

my $reg = q{^https?://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+$};

my $in = shift @ARGV;

my $url = '';
if ($in =~ /($reg)/) {
    $url = $1;
}

if (!$url) {
    die('url?');
}

$url =~ s/'/'\''/g;

my $com = "/usr/bin/wget -q -o /dev/null --spider '".$url."'";
system($com);

if ($?) {
    &mailto(
        $from,
        $to,
        '['.$url.'] Alert',
        "Can not connect Web Server.\nPlease check below.\n".$url
    );
}

sub mailto
{
    my ($from, $to, $subject, $body) = @_; 

    $subject = encode('MIME-Header-ISO_2022_JP', $subject);
    $body = encode('iso-2022-jp', $body);
    my %mail;
    $mail{'Content-Type'} = 'text/plain; charset="iso-2022-jp"';
    $mail{'From'} = $from;
    $mail{'To'} = $to;
    $mail{'Subject'} = $subject;
    $mail{'message'} = $body."\n";
    sendmail %mail;
}

twitterでフォローのお礼&フォロー返し

ご注意!

ここでご紹介しているスクリプトは、Twitter API の仕様変更により現在は動作しません!(2010/09/06)

twitter apiでフォローされたらそのメールをsmtpサーバーでキックして、フォロー返し&お礼を送信するbotを作成した。

当初は、cronでやっていたのだが、フォロワー一覧を取得するためのAPIがとても不安定だったのと、複数アカウントに対応するのに負荷を軽減したかったので、メールでキックする方法に変更した。

ちなみに、昨日の時点ではGoogle app engineを使おうと思ったのだが、あっさり気が変わった。

smtpサーバーの設定

今回はpostfixを使用した。
/etc/postfix/main.cfなどで正規表現を使ったエイリアスを設定するのがポイント。

詳細は省きます。

ソース(Perl)

標準入力から渡されたメールをパースして、ユーザー名、フォロワーなどを取得して、フォロー返し&お礼を送信する。

今回はフォローメール以外は何もしていないが、標準入力で渡されたメールをsendmailにパイプで渡せば転送できるような気がする。(スパムフィルターには引っかかるかも。)

複数アカウントに対応していますので、user01という記述がある当たりを修正してください。

あと、スパマーをフォローしないようにタイムゾーンでチェックしていますが、これでは不十分だと思いますので、今後工夫が必要ではないかと思います。

#!/usr/bin/perl -wT

binmode(STDOUT, ":utf8");

use strict;
use warnings;
use Encode;
use Net::Twitter;
use MIME::Parser;

# Config
my %user;
$user{'user01'} = 'pass';
$user{'user02'} = 'pass';
$user{'user03'} = 'pass';

my $tmp = '/tmp';
my $msg = '@%s フォローありがとうございます!よろしくお願いいたします。';
my $twt_clientname  = 'thanksbot';
my $twt_clientver   = '0.1';
my $twt_clienturl   = 'http://www.theta.ne.jp/';
# end config

my @mail = <STDIN>;
my $parser = new MIME::Parser;
$parser->output_dir($tmp);
my $entity = $parser->parse_data(join("", @mail));
my $header = $entity->head;

# get the $uid
my $uid = $header->get("X-Twitterrecipientscreenname");
chomp $uid;

# get the mail type
my $type = $header->get('X-TwitterEmailType');
chomp $type;
if ($type ne 'is_following') {
  exit;
}

# get the sender
my $sender = $header->get('X-Twittersenderscreenname');
chomp $sender;

my $twt = Net::Twitter->new(
   username    => $uid,
   password    => $user{$uid},
   clientname  => $twt_clientname,
   clienturl   => $twt_clienturl,
   useragent   => $twt_clientname,
   source   => $twt_clientname,
   clientver   => $twt_clientver,
);

my $stat = $twt->show_user($sender);
my %st = %$stat;
if ($st{'time_zone'} eq 'Tokyo' || $st{'time_zone'} eq 'Osaka') {
   my $exts = $twt->friendship_exists($uid, $sender);
   if (!$exts) {
       my $post = sprintf($msg, $sender);
       $post = decode_utf8($post);
       if ($twt->create_friend($sender)) {
           $twt->update($post);
       }
   }
}

exit;

もう少しエラー処理とかした方がいいんでしょうけどね。ご愛嬌です。

mecabの辞書にuni-dicを使用してみた。

先日の記事「MySQLによる日本語全文検索」で紹介した日本語全文検索システムではmecabを分かち書きに使用しているが、精度に不満があって、いろいろ調べた結果、CentOSやMacPortsでインストールされるIPA辞書は既にメンテナンスが終了しており、NAIST-DICというプロジェクトに引き継がれていることがわかった。

そこで、NAIST-DICをテストしてみたのだが、こちらもイマイチ。

さらにいろいろ調べてみたら、uni-dicという辞書があることがわかった。

幸いuni-dicはバイナリ辞書でも配布されているので、解凍すればすぐ使えるということで、手元のMacOSXにインストールしてテストしてみた結果、なかなか良好な結果が得られた。

また、配布元に書いてある「国立国語研究所で規定した「短単位」という揺れがない斉一な単位で設計されています」が気に入ったので、早速辞書をおきかえてみた。

ちなみに、今回のシステムではphpからシステムコマンドで呼び出しているので、辞書の入れ替えはコマンドのオプションを変更するのみで対応した。

mecab -d /path/to/uni-dic -Owakati

以上のように-dオプションの引数で辞書へのパスを指定すれば、設定ファイル等を変更しなくても辞書の変更が可能になる。

検索の精度に関しては、ぜひ以下のページでお試しを。

JULIA OPEN WATER MANUAL

rsyncで.svnを除外

rsyncでファイルを転送するときに、ほとんどの場合に(あくまでも私の場合!)、.svnは邪魔だということに今更気がついた。

というわけで、.bash_profileに以下の行を追加した。

alias rsync="rsync -avz --exclude=\".svn\""

これで、転送した後にfindとか使って.svnを削除したりしなくてすむ。

ちなみに、私の.bash_profileは以下のような感じ。

# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
  . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin

alias svndel="svn st | grep '^!' | sed -e 's/\![ ]*/svn del /g' | sh"
alias svnadd="svn st | grep '^?' | sed -e 's/\?[ ]*/svn add /g' | sh"
alias rsync="rsync -avz --exclude=\".svn\""
alias vi="vim"

export PATH
export LANG=ja_JP.UTF-8
PS1="[\u@\h \W]\$ "

シェルコマンドでシングルクォートに囲まれたシングルクォートのエスケープ

たとえば、シングルクォーテーションを使用したディレクトリ名を作成したい場合、以下のようにコマンドを入力することで、ディレクトリ名にシングルクォーテーションが使用できる。

$ mkdir test\'

つまり単純にシングルクォーテーションをバックスラッシュでエスケープしてやればよい。

削除したいときも同じ。

$ rm -fr test\'

ここまでは普通。

では、ディレクトリ名をシングルクオーテーションで囲みたいときはどうするのか?
通常、ターミナルでコマンド入力するときは、このようなシチュエーションはないが、PerlやPHPからsystem()などでコマンドを実行したい場合に、そうしたいときがある。

すくなくとも、このときはそう思った。(恥)

実はこの場合、バックスラッシュでエスケープしても意味が無い。

$ rm -fr 'test\''  # これではダメ

そこで、phpのescapeshellargがどうしているのかを確認した結果、いかのようにするのが正しいことがわかった。

$ rm -fr 'test'\'''

なんか腑に落ちない気もするが、実際にこれでうまくいったので。。。ちょっと不安。

Subversionでコミットした結果をtwitterに投稿する。

毎度懲りずに、マニアックなスクリプトを公開します。

最近、何かと話題になっているミニブログサービスのtwitterですが、私自身最近使い始めたところ、いろいろなオープンソースプロジェクトやメーカーが、サポートや開発の進捗状況などのアナウンスに利用していることがわかりました。

たとえば、WordPressもイベントの案内や新バージョンのリリースの発表に活用しています。

そこで、プロジェクトの情報共有等に役立つかと思いSubversionでコミットした結果をtwitterに投稿するためのスクリプトを作成しましたので公開します。

ソースをご覧いただくとわかるのですが、割と簡単なスクリプトなので、にるなり焼くなりして皆さんのお役に立てば幸いです。

投稿される内容について

本スクリプトはSubversionでコミットした際に、そのリビジョン番号およびコミットメッセージを以下のような感じでtwitterに投稿します。

[20] hogehogeを修正した

140文字を超えるメッセージの場合は137文字でカットして末尾に…を追加します。

重要な情報の漏洩等については、くれぐれもお気をつけ下さい。

ダウンロード

ダウンロードはこのリンクをクリックしてください。

インストール方法

以下で説明する方法はsubversionリポジトリにhookスクリプト(post-commit)が無い場合を想定しています。
すでにpost-commitが存在する場合は、post-commitから本スクリプトをコールするように修正してください。

1)まずはじめに、PerlのCPANモジュールの”Net::Twitter::Lite”をインストールしてください。

2)本スクリプトの21行目および22行目にtwitterのアカウント名およびパスワードを入力してください。

3)Subversionリポジトリ内にあるhooksという名前のディレクトリ内に本スクリプトを設置してパーミッションを755にしてください。(同じディレクトリ内には*.tmplという名前のファイルがいくつか存在しているはずです。)

4)Subversionリポジトリ内にあるhooksという名前のディレクトリ内にあるpost-commit.tmplをpost-commitというファイル名でコピーしてパーミッションを755にしてください。

5)post-commitに以下のソースをペースとしてください。

#!/bin/sh

REPOS="$1"
REV="$2"

/path/to/svn2twitter.pl "$REPOS" "$REV"

6)post-commitの/path/toの部分をご自身の環境にあわせて修正してください。

7)以上で設置は完了です。試しにクライアントから何かをコミットしてみてください。

免責事項

本スクリプトを使用したことによるいかなる損害にも補償いたしかねますのであらかじめご了承願います。

ライセンス

MITライセンスとします。
使用した結果のご感想やご意見をコメントくださるととてもうれしいです。

寄付について

このスクリプトは無料でご利用いただいても大歓迎ですが、寄付は大歓迎です。

ImageMagick 逆引きコマンドリファレンス

最近、ある開発案件の都合でImageMgickと格闘する時間が長い。

そんな中、この本を見つけたので読んでみた。

ImageMagickのリファレンスとしては貴重

ImageMagickを使用してみるとわかるのだが、オプションの数やバリエーションの豊富さに、ちょっとひいてしまう。

画像の処理をコマンドラインで行うという目的上、このオプションの多さは仕方のないことなのだが、意外とネット上にきちんとまとまった情報が少ない。(特に日本語で!!)

だから、何時間もかけて、ありとあらゆるパラメータの組み合わせを試したりしていたので、この本はとても役に立った。

しかーし!!

PHPのサンプルプログラムはダメです。

だけど、おや?

PHPによるサンプルプログラムがあると書いてある。

ImageMagickはPHPでは拡張モジュールなので、それに関するサンプルもあるのか?とおもって、中身をよく見るといくつか???と思う点があった。

  • 拡張モジュールに関する記述はいっさいなし
  • すべてsystem()関数で外部コマンドとしてコールしている
  • そもそも、これらの12種類のサンプルのうち11種類はGD関数でできる。

つまりですね、PHPのサンプルはほとんどいらないってことです。

逆にここのサンプルを本番で使ってはダメではないのかな?

たしかに、PHPの過去のバージョンでは、Jpegの画質が荒いなどGD関数が使えない時代もあった、だからImageMagickのお世話になったこともある。

だけど、今はそんなことは無い。

すくなくとも、ネイティブに実装されている関数を使わないでsystem()で外部コマンドを呼び出すのは極力やめたほうがいい。

せめて、「GDでも同じことができるよん、そのほうが安全だよ」ぐらいのことは書いてあるべきだと思う。

少なくともうちのスタッフがこれをやったら、説教部屋行きはまちがいない。

外部コマンドは必ず絶対パスで指定すべし!!

さらに!!
この本で紹介されているプログラムにはいくつかの懸念がある。

まず第一に、外部コマンドの呼び出しに対して、絶対パス指定をしていない。

つまり、

exec(‘convert …..’);

としている。

これは、非常に気になる。

やはり、/usr/bin/convertのように絶対パスで指定しないと環境変数PATHを見に行ってしまい、セキュリティ上の懸念が生じてしまう。

ちなみに、Perlの汚染チェックモードでこれをやったら、致命的エラーがでる。

コマンドへの引数がエスケープされていない

たとえば、以下のようなコードが普通に書かれている。

$cmd = “convert “.$effop.” image/org_”.$fname.” image/”.$fname;
exec($cmd);

つまり、コマンドへの引数にエスケープが施されていない。

これは、パッと見ればセキュリティホールを抱えているように見える。

そこで、ソースをさかのぼると、うん確かに引数に使われている変数にはユーザーからの入力や環境変数は使われていない。
だから、安全?

いやいや、そうではない。
こうやってソースをさかのぼって見ないといけない時点で、問題ありなのです。

$cmd = "convert ".escapeshellarg($effop)." image/org_".escapeshellarg($fname)." image/".escapeshellarg($fname);
exec($cmd);

とすれば一目瞭然、よけいな心配をする必要もなかったりする。

system()を使った際の万が一の時のダメージは、致命的である。

実際に、この本で紹介されているソースを見て安全であることを確信するには、わたしにはそれなりの時間が必要で、そう確信するまでにはいたらなかった。
それなら、いっそのことエスケープしてしまえというのが妥当な選択だと思う。

他にも!!

各サンプルの設計が悪いというかなんというか。。。

ほとんどのサンプルが、ひとつのPHPプログラムの中で、ImageMagickコマンドの発行、img要素(HTMLそのもの)の出力を行っている。

画像出力用のPHPとHTML出力用のPHPはわけたほうがよかったのではないかな?と思う。

実用上はそう考える方が圧倒的に多いような気がする。

というわけで

ImageMagickのコマンドリファレンスとしては、使うことも多いと思うのだが、PHPのサンプルは全く使えない。

GD関数でできることはGDでやるべきだし、外部コマンドを使いまくっている割に、そのリスクや注意点が全く書かれていない。

初心者の皆さんはこの本のPHPのサンプルは、くれぐれもあてにしないように。
少なくとも、このサンプルをあてにして買う価値はない!と思った。

LnuxコマンドでPDFからJpegに変換する(2)

前回の記事で、PDFからJpegに変換する方法を書いたのだが、いろいろなPDFを試しているうちに、問題を発見した。

方向が判別できていない?

たとえばA3横長のPDFをJpegに変換する際に、なぜか左に90度回転してしまい縦長の画像で出力されるという現象に直面した。

PDFとしては横長で表示されているにもかかわらず。。。

最初は、安易に右に90度傾けるように-rotate 90なんてパラメータを追加していたのだが、別の横長のPDFで今度は逆に右に90度傾いてしまうということに気がついた。

要は同じA3横長のPDFでもPDFによって結果が違うらしい。

問題はPDFを作る際の「印刷の向き」だった。

ImageMagickのconvertコマンドは、GhostScript経由でPDFから画像を作っているようだったので、GhostScriptの設定?と思いいろいろ調べていたのだが、結論はPDFを作る際の「印刷の向き」にあった。

印刷の向き

横長の画像を作りたいときは、上記の赤枠内で「横」を選択すれば、期待通りの方向で画像が作成された。

原因が分かれば、非常に納得のいくことだった。

ちなみにMacOSXにMacPorts経由でインストールしたconvertコマンドでは、このあたりのことは気にする必要がなく、つねに表示と同じ方向で画像が作成されたので、ある程度環境にも依存することなのかもしれない。