Archive for the ‘PHP’ Category

curl_multiの結果取得方法(HTTPエラーコードならびにL2〜L4でのエラー理由など)

cURLのマルチハンドルにおける、各ハンドルごとの通信内容の結果取得方法についてです。
データ本体ではなく、HTTPコードや通信時間、エラー理由(名前解決失敗やネットワーク接続失敗やconnection refusedなどといった、MACレイヤやTCP/IPレベルでの理由)の取得方法になります。

PHPの公式ドキュメントがクソわかりづらかったので、まとめました。

その1。
HTTPコードや通信時間などについては、curl_getinfo がそのまま使えます。
PHP: curl_getinfo – Manual

その2。
cURLが返すエラー理由については、curl_errorは使えません。curl_multi_infi_readの結果で取得できます。
PHP: curl_multi_info_read – Manual
参考:PHP: curl_error – Manual

なお、cURLのエラーコードについてはこちらに説明があります。
libcurl – Error Codes

curl_multiの使い方については以下のブログが詳しいのでそちらに説明を譲ります。
APIとの通信効率をよくする実装例(1) curl_multi – Yahoo! JAPAN Tech Blog

以下は実際に取得するためのサンプルコードです。
いろいろ端折ってます。

// make multi handle
$mh = curl_multi_init();

// make each handle (同時実行数分だけ行う)
$ch1 = curl_init() ;
curl_setopt( $ch1 , OPTION , VALUE ) ;         // option はここで設定
curl_multi_add_handle( $mh , $ch1 ) ;
$ch2 = curl_init() ;
curl_setopt( $ch2 , OPTION , VALUE ) ;         // option はここで設定
curl_multi_add_handle( $mh , $ch2 ) ;
$ch3 = curl_init() ;
curl_setopt( $ch3 , OPTION , VALUE ) ;         // option はここで設定
curl_multi_add_handle( $mh , $ch3 ) ;
    /* 途中省略 */
// ここまで each handle 分くりかえす
$mchary = array( $ch1 , $ch2 , $ch3 /* 中略 */ ) ;

// execute 
do {
    $mr = curl_multi_exec( $mh , $active ) ;
} while( $mr == CURLM_CALL_MULTI_PERFORM );

// get result 
$curlinfoary = array () ;
while( ( $cary = curl_multi_info_read( $mh ) ) != FALSE )   
{
    /* 1回の実行で1つのハンドル($chN)しか取得できないので注意 
     * $cary['handle'] ... curl_init で取得したハンドル 
     * $cary['result'] ... 結果コード
     */
    $curlinfoary[] = $cary ;

}

// get each handle results
foreach ( $mchary as $lch )
{
     /* content本体 */
     $content = curl_multi_getcontent($lch);
     /* info data */
     $infodata = curl_getinfo($c) ;     // curl single系のものをそのまま使えます
     /* どんな内容が得られるかは、 curl_getinfo のドキュメントを参照すべし */
     $httpconde = $infodata['http_code'] ;
     $tttime    = $infodata['total_time'] ;
     $ftime     = $infodata['filetime'] ; 
     /* 各ハンドルの result(エラーコード) 取得 */
     $rh = -1 ;
     foreach ( $curlinfoary as $cj )
     {
         if ( $cj['handle'] === $lch ) 
         {
              $rh = $cj['result'] ;
              break ; 
         }
     }
     /* $rh の値は、cURLのエラーコード(CURLE_*)になります。
        $rh に -1 が入ってるわけがないので、今回はそのまま強行します
       (念のためチェックはしたほうがいいかも?) 
      */
     curl_multi_remove_handle( $mh , $lch ) ;
}

L2〜L4で失敗した詳細の理由を欲しいとかいう場面は滅多にないと思いますが、どうしても原因がわからずに詰んでしまったときのデバッグのための手助けにはなると思います。

今日のクソコード

FizzBuzzはこれまで不要だったとしてもこれからは必要 – きしだのはてなを受けて。

いままでだと確かにFizzBuzzが「具体的に」SIの現場でどう役に立つかっていう話をしたがるよねえと。そんなことよりも金利計算とかそういった実務直結のプログラムが書ければいいんだし(実際、汎用機で使われてる言語だとFizzBuzzの書き方を知らなくてもそういうのが書けた記憶がある)。

そしてそんなこと考えてたりしたら、きっとこういうコード書いてくる人も出てくるんだろうなあと頭に浮かんでしまい(苦笑)、実際に書いてみたら…

<?php
$i = 1 ;
while(1)
{
  echo $i ."¥n" ; $i++ ;        //  1 + 15n
  echo $i ."¥n" ; $i++ ;        //  2 + 15n
  echo "fizz¥n" ; $i++ ;        //  3 + 15n
  echo $i ."¥n" ; $i++ ;        //  4 + 15n
  echo "buzz¥n" ; $i++ ;        //  5 + 15n
  echo "fizz¥n" ; $i++ ;        //  6 + 15n
  echo $i ."¥n" ; $i++ ;        //  7 + 15n
  echo $i ."¥n" ; $i++ ;        //  8 + 15n
  echo "fizz¥n" ; $i++ ;        //  9 + 15n
  echo "buzz¥n" ; $i++ ;        // 10 + 15n
    if ( $i == 100 ) break ;    // 10 + 15*6 = 100 なのでここで終了
  echo $i ."¥n" ; $i ++ ;       // 11 + 15n
  echo "fizz¥n" ; $i ++ ;       // 12 + 15n
  echo $i ."¥n" ; $i ++ ;       // 13 + 15n
  echo $i ."¥n" ; $i ++ ;       // 14 + 15n
  echo "fizzbuzz¥n" ; $i ++ ;   // 15 + 15n
}
?>

クソというよりはSAN値直葬のほうがふさわしいか。
というか、書いてる本人が既にSAN値直葬されかけたし。(笑)
着眼点は決して悪くはないけど、そこは人間じゃなくてプログラムがやってくれるべきとこだってこと。

そして。
ここまで書いていて、このやりかたが何か似てることに気づいたのですが、
それの正体が「Excel方眼紙」だったのは、内緒にしておきます :D

そして最後に気に入ってるサイトを紹介して終了。
ウンコード・マニア

PHP:cURLのコールバック

curl_setopt()の「CURLOPT_HEADERFUNCTION」と「CURLOPT_WRITEFUNCTION」の説明がわかりづらくて、かつどこにも説明してるサイトがなかったので自分でまとめてみました。

CURLOPT_HEADERFUNCTIONは、「ヘッダが読まれたときにコールバックする関数」を指定し、
CURLOPT_WRITEFUNCTIONは、「コンテンツが読み込まれた時に逐次コールバックされる関数」を指定します。
全部読み込んで一気に処理するのではなく、戻ってきた分を逐次処理して返す場合に有効。

<?php
set_time_limit(0);

$c = curl_init() ;
curl_setopt( $c , CURLOPT_URL , $url ) ;
curl_setopt( $c , CURLOPT_USERAGENT , $useragent ) ;
curl_setopt( $c , CURLOPT_COOKIE , $cookies );

// curl_setopt( $c , CURLOPT_RETURNTRANSFER , TRUE ) ; // 不要

curl_setopt( $c , CURLOPT_HTTPHEADER , $headers ) ;
curl_setopt( $c , CURLOPT_HEADERFUNCTION , hdcallback ) ;
curl_setopt( $c , CURLOPT_WRITEFUNCTION , wrcallback ) ;

$r = curl_exec( $c ) ;
curl_close( $c );

////////////////////////////// callback function

function hdcallback( $res , $response )
{
    $h1 = explode( "\r\n" , $response ) ;

    foreach( $h1 as $hout )
    {
        if ( $hout == "Transfer-Encoding: chunked" ) continue ;
        header( $hout ) ;
    }
    return strlen( $response ) ;
}

function wrcallback( $res , $response )
{
        echo $response ;
        ob_flush() ;
        return strlen( $response ) ;
}

まず、PHPじゃこういう使い方しませんものね。(偏見)

《追記》CURLOPT_RETURNTRANSFERを指定すると変なことになっちゃいましたので外しました。

Zend Frameworkで何か作ってみた(参考にしたサイトメモ)

単なるリンク集です…。
初めて扱ったので、そのために参考にしたサイトを載せておきます。

●本家

●基本

●今回はすっ飛ばしたけど、読んでおいて欲しいmodelのはなし。

CakePHPでは標準ではActiveRecordを採用していると思いますが、ここがCakePHPやsymfonyで学習してきた人が一番最初に戸惑う部分ではないかと思います。

Zend Frameworkのメイン開発者の方のコメントとして、Model !== Databaseだという主張が明確に述べられています。

正直、ここ、この概念で一番はまりました。
まさに、ずっとCakePHPやsymfonyでやってきたので(^_^;
実は今回はmodelは使ってません。

●データベース

●フォーム

formに関してはsymfonyのsfFormみたいなかんじでやる方法もあるけど今回は割愛。

●レイアウトとビュー

●ルーティング

●環境依存

public/index.phpで、set_include_path()することで解決。
余談ですが、ヘテムルだと「CLIの」phpを実行するとPHP4が呼ばれるので、bin/zf.shのPHP CLIパス検索処理部分で「php」になってるところを「php5」にしてください(合計3つ)。

# find php: pear first, command -v second, straight up php lastly
if test "@php_bin@" != '@'php_bin'@'; then
    PHP_BIN="@php_bin@"
elif command -v php5 1>/dev/null 2>/dev/null; then
    PHP_BIN=`command -v php5`
else
    PHP_BIN=php5
fi

●そんでもって。

作ったのが、「●●少女まどか☆マギカ(タイトルジェネレータ)」です。
アイディアそのものは2日ほど前に漠然と浮かんでて、実装は今朝考えました。10時くらいからとりかかって、Zend Frameworkのドキュメントを読みながら15時くらいにはできあがりました。
各種サイトを読んでからというよりは、読みながら必死になって書いたほうが案外なんとかなったというか…。

今回は、魔法少女まどか☆マギカだけのになりましたが、他のも簡単に作れるように今後コードを追加する予定。
(そのときは名前変えて新しくリリースの予定)

「パーフェクトPHP」がよかった件

先日買いました。

はっきり言うと「レベルが高い」…けど「必要なことはすべて書いてある」。
でも、個人的には超がつくおすすめの1冊。
今、PHPプログラマとして働きたい人には是非読んで欲しいと思ってます。
(ちなみに私の会社では必読書にしたいと思ってます、そのくらいね)

読み方としては、「書いてあることを全部理解する」ことはしないほうがいいと思う、特に初心者は。
まず一通り目を通しておく。とにかく、どんなことが書いてあったかを頭に入れておく。
書いてある内容を理解という意味ではその次でいいと思う。

そういえば。
PHPってバージョン3の頃から触ってたなあ、と。
最初に入社した会社のときでした。10年くらい前かな?

CentOS 5.5でPHP5.2.x

yumで入れるとPHP 5.1.xになっちゃうので、symfony 1.4を動かせなかったり、いろいろ不都合が多くて、ね。

ちなみに。
さくらのVPSさくらインターネットさんの専用サーバ エントリーのOSのバージョンがまさにこれなんで、思いっきりぶちあたりました。
多分今後参照する人が増えると思うので、メモがてら。

対策としては2つあります。

(1)根性で野良ビルド

私はこれでやりました。ちなみに5.2.14。
慣れないと難しいけど最強な方法。
逆を言うと「–with-apxs2ってなにそれ?」って人は絶対にやっちゃだめ。

参考までに私のとこでの configure 設定例を載せておきます。
php -i した結果です。

コンパイル環境や開発用のライブラリをyumコマンドで前もっていれておく必要があります。

manaka(専用サーバのほう)

Configure Command => ‘./configure’ ‘–prefix=/usr’ ‘–with-apxs2’ ‘–with-mysql’ ‘–with-pdo-mysql’ ‘–enable-calendar’ ‘–with-curl’ ‘–enable-mbstring’ ‘–with-mcrypt’ ‘–with-ncurses’ ‘–with-gd’ ‘–with-t1lib’ ‘–with-zlib’ ‘–with-png-dir=/usr/lib’ ‘–with-jpeg-dir=/usr/lib’

nene(VPSのほう)

Configure Command => ‘./configure’ ‘–prefix=/usr’ ‘–with-libdir=lib64’ ‘–with-apxs2’ ‘–with-mysql’ ‘–with-pdo-mysql’ ‘–enable-calendar’ ‘–with-curl’ ‘–enable-mbstring’ ‘–with-mcrypt’ ‘–with-ncurses’ ‘–with-gd’ ‘–with-zlib’ ‘–with-png-dir=/usr/lib’ ‘–with-jpeg-dir=/usr/lib’

VPSのほうは64bitなんで、’–with-libdir=lib64’が要ります。これ注意ね。

(2)パッケージを利用する

野良ビルドがわからない人向け。(できればこのほうをおすすめしたい)
yumでリポジトリを明示的に指定してする方法です。
「centOS 5.5 PHP 5.2」で検索するとこちらの方が圧倒的に多いですね。

docomo ID の独自APIにアクセスする(1)

前回の続きです。
例によって?PHP OpenID Libraryのexampleを改造。

今回は、独自APIの1つ「ユーザ属性情報通知」(3.8)、これはiモードID(guid=ONにしたときにDCMGUIDヘッダで取得できる個体識別文字列)をとってくるAPIです。
なお、iモードIDに関しては以前書いたのでそちらを参照してください。

割と手抜きのコードですが、差分はだいたいこんなかんじ。

--- ../../../w/php-openid-2.1.3/examples/consumer/finish_auth.php       2009-04-22 03:31:20.000000000 +0900
+++ ./finish_auth.php   2010-03-10 18:29:39.000000000 +0900
@@ -38,6 +38,26 @@
             $success .= '  (XRI CanonicalID: '.$escaped_canonicalID.') ';
         }

+       // var_dump ($response->message->getArg('http://specs.openid.net/auth/2.0','response_nonce') ) ;
+       // var_dump ( $response->message ) ;
+       $response_nonce = $response->message->getArg('http://specs.openid.net/auth/2.0','response_nonce') ;
+
+       // call docomo original API
+       $success .= "<br />response_nonce:" . $response_nonce . "<br /><br />\n" ;
+       $apicall_url  = "https://i.mydocomo.com/api/imode/g-info?" ;
+       $apicall_url .= "ver=1.0&openid=" . urlencode( $openid ) ;
+       $apicall_url .= "&nonce=" . urlencode( $response_nonce ) ;
+       $apicall_url .= "&GUID=&UA=" ;
+       $cu = curl_init() ;
+       curl_setopt( $cu , CURLOPT_URL            , $apicall_url ) ;
+       curl_setopt( $cu , CURLOPT_RETURNTRANSFER , TRUE ) ;
+       $httpresult = curl_exec( $cu ) ;
+       $aline = explode( "\n" , $httpresult ) ;
+       $success .= "API RESULT:<br />\n" ;
+       foreach( $aline as $a ) {
+               $success .= $a . "<br />\n" ;
+       }
+
         $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);

         $sreg = $sreg_resp->contents();

実行するとこんなかんじになります。
2010031011

追記:docomo IDの実装をはじめ、なぜiモードID等の取得のみこのようなことをしてるかについて興味深い考察をしてるサイトがありましたので紹介。
docomoのOpenIDの実装周りについて – 金利0無利息キャッシング – キャッシングできます – subtech

PHP OpenID Library で RP Discovery要求対応の例

具体的に言うとdocomo IDのことなんですが(笑)
とりあえずわかったことなんでメモ。

OpenIDそのに関しては前書いたメモがあるんでそっち参照。
で、今回の件。docomo IDの場合、RP Discoveryするんで、TrustRoot のaddressに、Accept: application/xrds+xmlで要求かけてるんですよね。
だから、index.php で、Acceptを判定して、XRDSドキュメントを返してあげればOKです。

PHP OpenID Libraryのexampleだと、追加するコードはこうなります。

--- ../../../w/php-openid-2.1.3/examples/consumer/index.php	2009-04-22 03:31:20.000000000 +0900
+++ ./index.php	2010-03-10 10:10:52.000000000 +0900
@@ -2,6 +2,17 @@
 require_once "common.php";
 
 global $pape_policy_uris;
+
+if ( $_SERVER['HTTP_ACCEPT'] == "application/xrds+xml" ) {
+	// returns XRDS
+	header ( "Content-type: application/xrds+xml" ) ;
+	$fh = fopen( "yadis.xrdf" , "r" ) ;
+	while( !feof( $fh ) ) {
+	 	print fgets( $fh ) ;
+	}
+	fclose( $fh ) ;
+	exit() ;
+}
 ?>
 <html>
   <head><title>PHP OpenID Authentication Example</title></head>

そして、読み込んでるyadis.xrdfはこう。

<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns:openid="http://openid.net/xmlns/1.0" xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="0">
<Type>http://specs.openid.net/auth/2.0/return_to</Type>
<URI>http://example.ne.jp:80/php-openid-2.1.3/examples/consumer/finish_auth.php</URI>
</Service>
</XRD>
</xrds:XRDS>

URIには、戻り先のURIを書いてねー。

もうちょっと、まともな書き方(XRDSを返す関数での書き方)があると思いますが、それはあとで調べて書くつもり。

なお、この件は、「docomo ID 認証 インターフェース仕様書」の「3.7.2 OP実装仕様」の表3.2にありるように、

RP Discoveryに失敗した場合、RPにはエラーは返却せず、エラー画面を返却し終了します。

と、なってます。だからいきなりこんな画面が出ます。
2010031001
無事成功するとこうなります。
2010031002
docomo ID 固有の実装・機能に関しては、のちほど書きたいと思います。

参考:RP Discovery対策 – Web Life!!! – Yahoo!ブログ

debianにSymfonyいれてみる

メモ。
PEARで入れるほうが楽そーなので、そちらの方法で。

1.PEARを入れる

$ aptitude install php-pear
$ which pear
$ pear upgrade PEAR

2番目のwhichはインストールされたか確認するため。

2.PEARからSymfonyを入れる

$ pear channel-discover pear.symfony-project.com
$ pear install symfony/symfony-1.4.2
$ which symfony

参考:The symfony framework Installation | symfony | Web PHP Framework
ちなみにおすすめできない方法のようです。
望ましいやりかたは、このへんから。
Getting Started with symfony | symfony | Web PHP Framework

とりあえずインストールだけなら以上あたりから。
この続きは、またあとで。

OpenID(2.0)に関するメモ

自分用メモを兼ねて。

●本家

●わからんとか言っててもはじまらんので流れだけまとめた。(JPEGファイル、手書き)

流れの説明で、振ってある番号は本家ドキュメントの「3.Protocol Overview」のそれぞれの節番号に対応しています。
なお、2.の「Discovery Information」で使用するYadisプロトコルに関しては割愛してます。

●そもそもの基本について。

●技術的に、参考になりそうなサイトとか

●OPの公式情報とか

PHP OpenID Library って便利なものがありましてね。・・・というわけで参考にさせていただいたサイト。

html2fpdf+mbfpdfで使うためのメモ

まずは手始めに材料。

まず、html2fpdfも、mbfpdfも、fpdfの派生クラスとして定義されています。
そんなわけで、html2fpdfの元クラスを変えてあげます。
※変更前

class HTML2FPDF extends FPDF

※変更後

class HTML2FPDF extends MBFPDF

あとの変更点は、以下に
HTML2PDF で日本語を使う為の設定 – hir_kazの日記
基本的には上記サイトに書いてあるとおりで大丈夫ですが、差異というかmbfpdf独特の注意点はこのへん。

●before

require_once(RELATIVE_PATH.'fpdf.php');

●after

require_once(RELATIVE_PATH.'mbfpdf.php');

あとは、SetFontの指定方法が変わります。

$this->SetFont( GOTHIC , '' , 11 ) ;

最後に、使うときのやりかたがかわります。
●japanese.phpの場合

$pdf->AddSJISFont();

●mbfphpの場合

$pdf->AddMBFont( GOTHIC , 'SJIS' ) ;

あと注意点としては、align=”left”以外の位置指定がおかしくなるところでしょうか。
若干どころかかなり右寄りになります。
どうやら、簡単には解決しそうにない問題です。
参考:HTML2FPDF同梱のFPDF – 日々是雑記

mb_convert_kanaチートシート

引数の文字関係がマニュアルとかだとわかりづらいんで作った。
参考:カナを(“全角かな”、”半角かな”等に)変換する


対象文字種類全角→半角半角→全角備考
英字rR
数字nN
英数字aA※1
スペースsS
カタカナkK※2
ひらがなhH※2 ※3


対象文字種類カタ→ひらひら→カタ備考
全角ひらがな・カタカナcC

※1:U+0022, U+0027, U+005C, U+007Eを除く U+0021 – U+007E の範囲
※2:全角変換時に「V」を併用すると、濁点・半濁点つき文字を1文字にします。例)「ガ」→「ガ」
※3:全角ひらがな←→半角カタカナの変換となります。

XML-RPCを利用したWordPressの記事登録

tweetAngelで先日実装したので、それについてのちょっとした説明です。

・・・えっと。やりかたについては以下のサイトに載ってるとおりです!

更新して、そのpermanent linkを取得する処理を行います。
注意したいところは、引数はすべて XML_RPC_Value クラスを経由して与えること。
(more…)

コメントつきRTが非常に鬱陶しくてしかたがないのでいい表示方法を考えてみた。

未だ公開する気のない(主に実装がきたないのと、動きがおかしいのと、OAuth使ってないので怖いのというのが理由で・・・)ぷちつい(petitTwitter)でのはなし。
コメントつきRTが鬱陶しくてうざいので、きれいに表示する方法を考えてみました。

      // コメントつきRT(QT)対応
      if ( mb_ereg( "RT @" , $text ) && ( ! mb_ereg( "^RT @" , $text ) ) ) {
        mb_ereg( "(RT @.+)$" , $text , $gettext0 ) ;
        $srctext = $gettext0[1] ;
        $gettext1 = explode( "RT @" , $text , 2 ) ;
        $desttext = $gettext1[0] ;
        $desttext .= "<blockquote>" ;
        $destexp = explode( "RT @" , $srctext ) ;
        foreach( $destexp as $destout ) {
          if ( $destout == "" ) continue ;
          $desttext .= "<font color=\"#40c040\">↑</font><font color=\"#808080\">RT @" . $destout . "</font><br />" ;
        }
        $desttext .= "</blockquote>" ;
        $text = $desttext ;
      }
      $text = "<font size=\"2\">" . $text . "</font>" ;

ただこれは、『RT @〜』って表記にしか対応してません。
よく代替案としてあげられてる『QT @〜』にはまだ対応しておらず。
explodeでは正規表現使えないので、preg_split するしかないですね。

preg_split( "/[RQ]T @/" , $srctext ) ;

みたいなかんじなるのかなー。

こんなかんじになります。

タイムラインで。勝手に使ってごめんなさい><
2009120101
自分自身のpostはこんなかんじ。
2009120102

ケータイアクセスのみに限る方法(IPアドレスで見る方法)

手っ取り早く?、IPアドレスを32bit intにして比較する、って方法にしました。
長くなるんでー。
(more…)