PHP5.5でWordPressのWP_DEBUGを有効にするとmysql_connectつかうなと怒られる

先週末にustでみたPHPカンファレンス 2013+WordCampのライトニングトークでマスク・ド・レガシー氏がWordPressのコードはレガシーでヤバイかもしれないと発表していた。これには全面的に賛同せざるを得ない。開発環境をPHP5.5にあげたらさっそく問題に遭遇した。マスクマンは無事に帰れただろうか。

なおWordPressはこれを書いている時点で最新の3.6.1。

WordPressで WP_DEBUG を有効にするともれなく以下の警告が発生する。

Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in 
wp-includes/wp-db.php on line 1142

暫定対策としてDeprecatedのエラーを非表示にする場合、 wp-includes/load.php を直接いじるか、wp_debug_modeが呼ばれた後の何かしらのアクションで error_reporting( E_ALL ^ E_DEPRECATED); 呼び出すかである。とりあえず開発環境なので wp-includes/load.php の263行あたりを修正した。

wp-includes/load.php
function wp_debug_mode() {
    if ( WP_DEBUG ) {
//      error_reporting( E_ALL );
        error_reporting( E_ALL  ^ E_DEPRECATED);

基本的にはPDOに置き換えるべきで、WordPressではデータベース処理はwp-db.phpでカプセル化されているので実装はそれほど難しくないと思うが、問題はユーザ環境によってPDOやPDO_mysqlが必ずしも有効になっているとはかぎらないところにあり、おそらく慎重にならざるを得ないだろう。WordPressのレガシコードはしばらく続く予感。

WordPress用GitHub Flavored MarkdownプラグインでFenced code blocks修正

ブログにMarkdownで書こうとした備忘録がうまく動かなくてバグに気がついたのでGitHub Flavored Markdownプラグインを修正しました。バージョン 0.3。

makotokw/wp-gfm
GitHub Flavored Markdown for WordPress
使う人がComposer使わなくてもいいようにGitリポジトリに依存ライブラリも突っ込んだのでGitでバージョンアップ(pull)するときは先にvendorディレクトリを消してください。

でFenced code blockの話。例えば以下をmarkdownショートコードで囲った場合

```ruby
# require 'redcarpet'
# markdown = Redcarpet.new("Hello World!")
# puts markdown.to_html
```

以下でちゃんとコードハイライトされていたらプラグインは正常に動いています。

# require 'redcarpet'
# markdown = Redcarpet.new("Hello World!")
# puts markdown.to_html

前のバージョンでバグってたのはMarkdownでは#は見出しに使われるので、blockの変換順序のせいでFenced code blockを判定するときにもう中身がh1タグになっていました。#だけじゃなくて他にもMarkdown記法が入っていると先に変換されていた恐れがあります。0.3からは真っ先にFenced code blockの変換を行います。

さて最初のバージョンではユーザに変換用にWebAPIを構築することを前提としていて導入しづらいのでプログラマ向けにGitHubにあげていたのですが、だいぶPHP-MarkdownでのGitHub Flavored Markdownモドキが使えるようになってきたのでもう少し導入しやすくして普通に公開してもいいのかなと思って来ました。

でもGitHubは便利だしWordPressのプラグインディレクトリにあげるのは面倒だし、GitHubからバージョン通知できたらいいなと思って調べていたらWordPress-GitHub-Plugin-Updaterを見つけたのでちょっと調べてみる予定。

ガラポンTV APIのラッパーライブラリ書いてみた

http://garapon.tv/のAPIのラッパーライブラリを書いてみたので公開。ガラポンTVはワンセグ動画だけど2週間分のテレビ番組を全部録画するレコーダー。申請するとAPIをつかって録画した番組のデータを使っていろいろ遊べる。

NHKのスペイン語講座ムーブした途端にブルーレイレコーダの外国語押し、NHK押しが始まり「おまかせ・まる録」にまったくお任せできなくなったのでこれでなんとかする。ガラポンTVで見たい番組が見つかったら次からはレコーダで予約してフルセグの画質で見るみたいなことがしたい。

ライブラリはとりあえずPHPで書いている。途中でやっぱりPHPめんどくさいってなってGuzzleライブラリに頼ってさくっと実装した。まだテストファーストでしか動かしていないので自分の好きそうな番組を探すツールはこれから作る。

Nginx + PHP-FPM 5.4.13で500エラーでWordPressが表示されなくなった

週末ブログのアクセス数が激減して、なにか検索エンジンのアルゴリズムでも変わったのか?と思ったがもともとこのサイトは特定のコンテンツにアクセスは依存していないのでいろいろ調べると時間がたつと500エラーでサイトにアクセスできなくなることに気付いた。

  • Nginxは1.2.7
  • PHP 5.4.11->5.4.13にあげたら起きるようになった。
  • nginxのエラーではrecv() failed (104: Connection reset by peer)が出力されている。任意のページで。頻繁ではない。
  • 上記のエラー以外のリクエストでもサイトには接続できない。nginxのエラーログには出力されておらず、accessログには単なる500エラー。
  • phpのエラーログは何も出力されていない。
  • php-fpmのエラーログにはchild xxxx exited with code 1 after xxx.xxx seconds from startが出力されている。頻繁ではない。
  • サイトに接続できないときに同じサーバの別ドメインのphpコードは動いていることがある。
  • php-fpmを再起動すると接続できるがしばらくするとまた問題が起きる。

頻繁ではないというのはすべてのリクエストでエラーがでているというわけでないという意味。これが一番わからない。ログを見るとは何らかの原因でphp-fpmに問題がおき、nginxから接続できなくなり、それ以降はエラーログにも残らず500エラーのレスポンスとして処理されているような状態になっている。

エラー名でググるとrequest_terminate_timeoutを設定するというのがでてくる。腑に落ちないままやってみたが効果なし。

デバッグログを出してみたやはり子プロセスが問題あったまま動き続けている?

WARNING: pid 7583, fpm_children_bury(), line 252: [pool www] child 7584 exited with code 1 after 397.948034 seconds from start
NOTICE: pid 7583, fpm_children_make(), line 421: [pool www] child 7648 started

request_terminate_timeoutの設定で回避するというのが腑に落ちないのは上記のログを見てもわかるように新しい子プロセスを起動しているのにそのあと接続できないところ。子プロセスがbusyになることが問題なのではなく、その状態になるとnginxとphp-fpmの関係が修復不能になる点。

結局PHPを5.4.11に戻した。とりあえずこれで様子を見る。ただ今回いろいろ調べて単純にphp-fpmの設定は見なおした方がいいと思った。

しかしトラブってもいいように週末パッケージを更新したのにすぐに問題がおきなくて結局日曜日の夜に気がつくとかなんの意味もない。

なんの意味もない。なんの意味もない。

小島よしお 2009年11月

月曜日はphp-fpmを定期的に再起動させるというどうしようもない回避策を行なっていました。

php-twientを使って問いかけに返答するTwitter botを50行でつくる

飲み会のネタでとあるつぶやき対して、ある返信をするくだらないTwitter botを作ったらえらく好評だった。以前作成したphp-twientというライブラリを数年ぶりに更新して作成したので似たようなbotを作るためのサンプルプログラムを紹介。

レシピ

  • PHP 5.3以上が実行できるマシン→今回は自宅サーバでコンソールから実行、時計をあわせておかないと401 Unauthorizedになる件
  • Twitterアプリケーション登録→https://dev.twitter.com/appsX26Xから作成する。投稿するのでAccess levelをRead and writeにしておく
  • bot用のTwitterアカウント
  • phpとかgitとかcomposerを使うための知識
  • やる気とbotのアイディア

php-twientのインストール

プログラムは github 上で 公開している ( php-twient )。composerパッケージとしてpushしたのでcomposerから簡単にインストールできる。プロジェクトに以下のようなcomposer.jsonを作成しておく。

{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/makotokw/php-twient"
        }
    ],
    "require": {
        "makotokw/twient": "dev-master"
    }
}
curl -s http://getcomposer.org/installer | php
php composer.phar install

を実行するとするとvendorディレクトリにライブラリが配置される。

つぶやきに返信するbotの作成

今回はbotに対してつぶやかれた内容に返信するというものを作った。例えば定期的にmentionを拾いにいって返信する方法もあるが、userのタイムラインをウォッチするStreaming APIを見つけたのでそれを使う。

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Twient\Twitter\V1dot1 as Twitter;

try {
    $twitter = new Twitter();
    $twitter->oAuth(
        TWITTER_CUNSUMER_KEY,
        TWITTER_CUNSUMER_SECRET,
        TWITTER_BOT_TOKEN,
        TWITTER_BOT_SECRET
    );
    $twitter->streamingUser(
        array(),
        function (Twitter $twitter, $status) {
            if ($status) {
                if (isset($status['text'])) {
                    $t = $status['text'];
                    if (strpos($t, '@' . TWITTER_BOT_SCREEN_NAME) !== false) {
                        $twitter->statusesUpdate(
                            array(
                                'status'                => createResponseStatus($status),
                                'in_reply_to_status_id' => $status['id_str']
                            )
                        );
                    }
                }
            }
            return true;
        }
    );
} catch (Exception $e) {
    echo $e;
}

function createResponseStatus($status)
{
    $user = $status['user'];
    $text = $status['text'];
    $text = preg_replace('/\s?@' . TWITTER_BOT_SCREEN_NAME . '\s?/', '', $text);
    $text = preg_replace('/はありますか/', '', $text);
    $text = preg_replace('/(?|\?)\s*/', '', $text);
    return '@' . $user['screen_name'] . ' む!' . $text . '!!';
}

以上が簡単なサンプルプログラム。「他に頼みたい仕事はありますか?」と問いかけられると「む!他に頼みたい仕事!!」と返答する。ちなみに終了のルーチンを入れていないのでkillさせないといけない。

投稿するときにin_reply_to_status_idを入れて呟いても返信にならず、一応相手のscreen_nameを内容に入れないといけない仕様らしい。

Streaming APIとclosureを使って45行ととてもシンプルなプログラムになっている。createResponseStatus関数をわけなければもっと短くできるが、これはcreateResponseStatusを置き換えるだけで流用が可能ということであえてこういうサンプルにしている。このサンプルコードはネタなので他の人から見れば意味が無いbotだが、アイディアしだいでいろいろできると思う。

TWITTER_CUNSUMER_KEY, TWITTER_CUNSUMER_SECRETはアプリケーション登録でもらえるもの。TWITTER_BOT_TOKEN, TWITTER_BOT_SECRETは以下のおまけを参照。

おまけ: OAuthTokenの取得方法

php-twientでは以下のようなプログラムをコンソールから実行して、ブラウザでピン番号を取得してまたコンソールに入力すればOAuthTokenの取得ができる。

$twitter = new Twitter();
$oauth = $twitter->oAuth(TWITTER_CUNSUMER_KEY, TWITTER_CUNSUMER_SECRET);
$requestToken = $oauth->getRequestToken();
$url = $oauth->getAuthorizeUrl($requestToken);
echo 'ブラウザでこれを開く'.$url.PHP_EOL;
echo 'Input PIN: ';
$pin = trim(fgets(STDIN,4096));
if (empty($pin)) exit;
$token = $oauth->getAccessToken($pin);
echo 'Your token is "'.$token['oauth_token'].'"'.PHP_EOL;
echo 'Your secret token is "'.$token['oauth_token_secret'].'"'.PHP_EOL;

php-twientでTwitter API v1.1に対応しました

php-twientでTwitter API v1.1に対応したものをv0.5としてリリースしました。以下からダウンロードできます。

APIは過去のバージョンと互換があり、以下のように宣言をすれば最小限の書き直しで済みます。

use Twient\Twitter;
// or use Twient\Twitter\V1dot1 as Twitter;

ただし名前空間に書きなおしており、Twitter.phpだけをrequireするだけで使えた前のバージョンとは導入方法が異なります。PSR-0に準拠しているのでautoloadで使えます。composerでインストールして/vendor/autoload.phpをrequireするのがてっとり早いです。

特徴

php-twientには以下のような特徴があります。

  • 名前空間を使っているためPHP5.3以上が必要です
  • 基本的に他にライブラリは不要でphp_curlがなくても動作します。
  • composerを使ってインストールすることもできます。
{
        "repositories": [
            {
                "type": "vcs",
                "url": "https://github.com/makotokw/php-twient"
            }
        ],
        "require": {
            "makotokw/twient": "dev-master"
        }
    }
  • コード補完が使えるようにTwient\Twitter\V1dot1クラスがあります。
  • Streaming APIに対応しています
/** @var Twient\Twitter\V1dot1 $twitter */
$twitter->streamingStatusesFilter(
    array('track' => 'Sushi,Japan'),
    function ($twitter, $status) {
        static $count = 0;
        echo $status['user']['name'] . ':' . $status['text'] . PHP_EOL;
        return ($count++ < 5);
    }
);

不向き

このライブラリは以下に該当する人には向いていません。

  • PHP5.3未満の環境
  • composer?autoload?って何?つおい?

余談

もともと既存のライブラリがすべからくcurlをつかっていたため泣く泣くfile_get_contentで頑張るライブラリを書き始めたのが最初のバージョン。PSR-2や名前空間で書きなおしたり、composerに対応してみたり、そんなアグレッシブに責められるならcurlくらいセットアップできるだろ?というツッコミを受けそうだ。昨今だとVPSも流行ってきたのであまりpoorな環境に想定しなくてもいいのかなと考えがシフトしてきている。

ライブラリの設計思想としてもともとcall('/statuses/update', $params)という一つのメソッドでAPIを呼び出せるようにしていたんだけど、それを実現するためには内部でGETなのかPOSTなのかを知っていないといけなく、全てのAPIをサポートできていなかった。書き直している時にpost('/statuses/update', $params)みたいに汎用的にした方がいいのかな?とか思ったけどやっぱり使う側にたってみると間違えそうだからやりたくなかった。それを言い出すとパラメータも連想配列で渡すのではなくメソッドの引数として定義したいくらいだけど引数はいつ変わるかわからないしその度にアップデートするのもしんどい。マジックメソッドで対応することも考えたけど使う側がわかりにくすぎる。

なんでオーバーロードがねーんだよ
マジックメソッド便利だけど可読性がねーよ
なんで俺はPHP書いてんだよ(ぇ

とかいう葛藤もあってrubyでNokogiriとActiveRecord(camelizeが使いたかった)を使ってTwitterのAPIページをスクレイピングして各APIのGET/POSTを判定した上にAPIごとのPHPのメソッドを出力するスクリプトを書くという事件?が起きた。rubyのおかげ(?)でようやくphp-twientはまともに使えるバージョンになったと思う。

PHP5.4のアップデートの手順をミスって酷い目にあった

手元の開発環境ではPHP5.4を使っていたが、PHP5.4でパフォーマンスが向上しているからWordPressにも恩恵あるかなと思ってサーバの方も更新してみたら酷い目にあった。

DebianでDotdebを利用していたのでInstructionsを見てPHP54のソースを追加するだけだったのに、DotdebでNginxやMySQL入れていることをすっかり忘れていてPHP54のソースに置き換えてしまった。それでパッケージを更新したら見事にNginxとMySQLが削除されてしまい、サイトにアクセスができない状態になった。

サービスが消えているからログが出力されるわけもないし、NginxがPATHから見つからないので気がついた。それでNginxtとMySQLも入れなおしたんだけどそれでもいろいろハマった。

nginx-extras -> nginx-passenger

Nginxを入れなおしても起動ができない。なぜかpassenger向けのディレクティブ知らんといわれる。passengerはRedmineを動かすために入れていた。

nginx: [emerg] unknown directive "passenger_root" in /etc/nginx/nginx.conf:67
nginx: configuration file /etc/nginx/nginx.conf test failed

パッケージを検索してみるとnginx-passengerというのが追加されていた。どうやらpassenger向けにビルドしたものはnginx-extrasからnginx-passengerに変わったようだ。nginx-passengerに入れ直して起動はできた。

nginxからphp-fpmに接続できず502 Bad Gateway

php-fpmではhttp://127.0.0.1:9000でリスニングしていたつもりだったんだけど設定(/etc/php5/fpm/pool.d/www.conf)を見ていくとunix socket(listen = /var/run/php5-fpm.sock)になっていたので

server unix:/var/run/php5-fpm.sock;

にnginxの設定を変更。php-fpmの設定変えた記憶がないんだけど、デフォルトの設定かディストリビューションの設定ファイル変わったんだろうか。

MySQL 5.5が動かない

MySQLも入れなおしたら5.5になっていてmy.cnfがおかしいとか言われて起動しない。

130127 0:18:13 [ERROR] An old style --language value with language specific part detected: /usr/share/mysql/english/
130127 0:18:13 [ERROR] Use --lc-messages-dir without language specific part instead.

my.cnf.dpkg-distと比較して

language= /usr/share/mysql/englishをlc-messages-dir= /usr/share/mysqlに変更した。

起動しようとするとChecking for tables which need an upgradeとか怒られるので

sudo mysqlcheck --check-upgrade --all-databases --auto-repair -u root -p

を実行して復旧。

PukiWikiが動かない

nginx+php-fpm-mysqlの設定をなんとかしてようやくブログが閲覧できたけど、今度はWordPressブログで使っているPukiWiki for WordPressプラグインが動かない。どうやらPukiWikiにPHP5.4との互換性問題があるようだ。

とりあえずhex2bin関数をif (! function_exists(‘hex2bin’)) {で囲った。

おわりに

今のところは無事動いているようだけど、作業を週末にやってよかった。

CakePHPでつくられたプロジェクト管理システムchawを試してみる

lithiumのサイトで使われているchawが気になったので試しにいれてみた。

opensourceだけど自前でのホスティング方法とかあんま書いてない、gitでインストールするようだ。git+sshの使い方が分かる人じゃないと無理。git+sshやapache+PHP5+MySQLが使える前提ですすめる。

ユーザ登録する

gitでのアクセス権を得るために、まずhttp://thechaw.com/にてユーザ登録する。

アカウント設定で、sshの公開鍵を貼り付ける。
参考: http://thechaw.com/wiki/guides/setup

インストール

実はgitでchawを取得するとINSTALL.txtがトップディレクトリにある。ただ今回はそのやり方には 従わぬ 。なぜかというとrewriteしているとは言えドキュメントルートの下に全部のソースファイルを置くやり方があまり好きではない。もともとサブディレクトリで運用したかったのでドキュメントルート外の適当な位置においてAliasをつける。

/usr/share/におく例ですすめる。

cd /usr/share/
git clone git://github.com/cakephp/cakephp1x.git
cd cakephp1x
git checkout -b 1.3 origin/1.3
git clone git@thechaw.com:chaw.git

これでCakePHP 1.3の構成にcakephp1x/appとは別にアプリがcakephp1x/chawに入った状態になる。

設定変更

debug=0にしとく。

Configure::write('debug', 0);

データベースの作成

rootは使いたくないので事前にユーザとアクセス権を与えておく。

CREATE DATABASE chaw DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
GRANT ALL PRIVILEGES ON `chaw`.* TO chaw@localhost IDENTIFIED BY 'chaw';
FLUSH PRIVILEGES;

データベース設定とテーブル作成

ユーザ、パスワード、データベースにchawをつかっている。

cd /usr/share/cakephp1x/chaw
../cake/console/cake -app chaw bake db_config
Name:
[default] >
Driver: (db2/firebird/mssql/mysql/mysqli/odbc/oracle/postgres/sqlite/sybase)
[mysql] >
Persistent Connection? (y/n)
[n] >
Database Host:
[localhost] >
Port?
[n] >
User:
[root] >chaw
Password:
>chaw
Database Name:
[cake] > chaw
Table Prefix?
[n] >
Table encoding?
[n] >
Look okay? (y/n)
[y] >
Do you wish to add another database configuration?
[n] >
File `/path/to/chaw/config/database.php` exists, overwrite? (y/n/q)
[n] > y
Wrote `/path/to/chaw/config/database.php`

スキーマ(テーブル)の作成

../cake/console/cake  -app chaw schema create
Are you sure you want to drop the table(s)? (y/n)
[n] >
Are you sure you want to create the table(s)? (y/n)
[y] >

apacheの設定

apacheのconfに以下を入れておく

Alias /chaw /usr/share/cakephp1x/chaw/webroot
<Directory /usr/share/cakephp1x/>
  AllowOverride All
  Options FollowSymLinks MultiViews ExecCGI
  Order allow,deny
  Allow from all
</Directory>

も必要。

サブディレクトリで運用するのでRewriteBaseを入れておく。

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /chaw
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>
cd /usr/share/cakephp1x/chaw
sudo chown -R www-data:www-data tmp content
sudo /etc/init.d/apache2 restart

動かしてみて

これでhttp://servername/chawでアクセスできるわけだが、実際使ってみるといまいち設定の方法がわからない。たとえば誰でもユーザ登録できるようになっているのを止めたいと思ってもできるのかできないかもわからなかった。デザインはクールだし、forkなどgitらしいオープンソースの公開方法はできそうだけどそこに期待するならgithubでもよいわけだし、プロジェクト管理の機能性としてはredmineの方に分がありそう。今のところ自分には使いこなせないっぽい。

php-twient 0.2リリース – TwitterのStreaming APIに対応

今まで横目でちらっと見てるだけだったTwitterのStreaming APIですが、今年に入って正式リリースになったのでphp-twientで対応してみました。

サンプルコードは以下のような感じ

<?php
require_once 'Twitter.php';

$user = 'your account';
$pass = 'your password';

$twitter = new Twitter();
$twitter->basicAuth($user, $pass);
$twitter->streaming('statuses/filter',array('track'=>'Sushi,Japan'),'_callback');
function _callback($status) {
    static $count = 0;
    echo $status['user']['name'].':'.$status['text'] . PHP_EOL;
    return ($count++<5);
}
?>

Twitter Streaming API by php-twient 0.2

PHPでStreamingする場合、数行でかけるのでライブラリは必要なさそうな気がしますが。

OAuth対応、cURLなしで動くphp twitter library, php-twientを公開

今年最初のリリースはphpクラスライブラリ。つっても去年つくったのをリリース用に整形しただけなんだけど・・・

特徴

  • PHP5で動く、PHP4は非サポート(動くかもしれないが基本的に対応する予定なし、むしろPHP5.3以降必須にしたいくらい)
  • cURLが無くても動く。あったらcURLを使う。
  • Basic認証とOAuthに対応(Andy Smith 氏のbasic php libraryを利用)
  • twitter APIの各メソッドは一つのcallメソッドで対応、twittterのメソッド名とarrayパラメータを渡して実行する
  • エラーは例外を発行

予定

  • まだ自分が使うメソッドしか用意してないのでゆくゆく他のメソッドに対応する
  • OAuthの部分がPHP5.3以降だとPHP Deprecatedなので修正する

余談

むかしPEARのService_Twitterとかつかってちょっと遊んだことがあったけど、なんで今更こんな後発のライブラリをつくったのか。

twitter-botをつくる機会があったんだけどPEARは使いたくなくて、http://apiwiki.twitter.com/Libraries#PHPあたりを見て試してたんだけど、なんと動かすマシンでphp-curlが入ってない。そして使いたいやつに限ってcURLが使われてる。好きにいじっていいマシンじゃないし、別にbasic認証ごときfile_get_contentsでいけるやろ。単にrequestをpostするだけやしすぐできるわ。とタカを括って書き始めてすぐできたんだけどクライアント名を表示するにはOAuthに対応しないといけないという現状を認識。

ここで使えそうなライブラリをまた探すのか、自分で書くのかいろいろ葛藤したあげく最終的に下記あたりの理由で作ることにした。

  • やっぱりみんなcURLつかってる
  • 最近Retweetなメソッドが増えるなどtwitter-apiが更新されており、追いついてないライブラリがちらほらある
  • 最近増えたtwitter-apiやOAuthをちょこっと触っておくのも良いだろう

cURLを使う、つかわない。Basic認証とOAuthでrequestクラスとauthクラスを抽象化することにした。個人的にどうもauthクラスがrequestを投げる設計が気持ちが悪くて、requestクラスにauthオブジェクトをbindするような感じにしたかったんだけど、実際書いてみたところOAuthの仕組み上、そう書きたくなることがわかった。そのためOAuthのところだけ相互参照で駄目なつくりになっている・・・ちゃんとやるならOAuthのところを2階層にわけなくちゃいけないんだけどあんまり冗長にしたくないので駄目な設計とわかりつつ妥協をしている。