トラックバックの送受信

2007/03/18 | XML

XMLとは関係ないけれど、ブログ関連の話題が続いたので、ついでにトラックバックも取り上げてみた。トラックバックの機能はXMLとはまったく関係ない仕組みだ。送信側は指定されたトラックバックURLに、フォームからpostする要領でデータをpostする。サーバからpostするわけだから(ブラウザがpostするのではないのだから)、少しだけ工夫が必要。postメソッドで送信するために、ローレベルにfsockopenで直接相手先ホストとお話しするか、curlを使うか、あとPEARのライブラリも使えるはずだ(工夫が必要なのはここだけ)。
受信する側はもっと簡単。フォームで内容を受け取るのと同様で、単にPOSTを受けるだけ。$_POSTでいいわけだ。

トラックバックURLはどうもPATH_INFOを使って記事番号の受け渡しをすることが一般的らしい。当然何らかの記事に対してトラックバックがおこなわれるわけだが、記事には固有の番号がついている。トラックバックが送られてきたら、どの記事に対して送られてきたのか判別する必要があり、その記事番号の受け取り方でPATH_INFOを使用する、ということだ。
例えばトラックバックを受け付けるプログラムのURLと記事番号が以下のような場合、

URL: http://hogehoge.com/trackback.php
記事番号: 24

トラックバックURLは以下のようにする、ということだ。

http://hogehoge.com/trackback.php/24

でもって、trackback.php内でPATH_INFOから記事番号を判別する。ちなみにPOSTで受け渡しされる情報は以下のとおり。

blog_name:ブログ名
title:言及記事のタイトル
url:言及記事のURL
excerpt:言及記事の概要

どこかで一度試してみるとしよう。

XML-RPCによるブログ操作のサンプル

2007/03/17 | XML

XML-RPCを使って、ブログを操作するためのPHPスクリプトをクラス化してみた。全部の機能が正しく動いているかどうかは確認していないけれど、いちおういくつかの正しく動いているので、公開してみることにする。

もし万一、業務用途で使いたい方という方がいらっしゃったら、コメント欄からでもご一報お願いできますか。

xmlrpc.incを使っているのでダウンロードしてパスの通っているところにおく必要あり(ダウンロードはここから)。

使い方:
最初にオブジェクトを生成して、prepareで接続を初期化してから使う。

<?php
  /*
  | Copyright (c) 2007 kaorun. All rights reserved.
  | http://php.tekmemo.com
  */
  
  require_once(‘xmlrpc.inc’);
  $GLOBALS[‘xmlrpc_internalencoding’]=’UTF-8′;
  class xmlrpc_weblog
  {
    /**
     * XML-RPC client object
     *
     * @var object
     * @access public
     */
    var $client;
    
    /**
     * Target path for XML-RPC connections
     *
     * @var string
     * @access public
     */
    var $apipath;
    
    /**
     * Target host for XML-RPC connections
     *
     * @var string
     * @access public
     */
    var $apihost;
    
    /**
     * Target port for XML-RPC connections
     *
     * @var integer
     * @access public
     */
    var $apiport;
    
    /**
     * Application key for XML-RPC connections (usually ignored)
     *
     * @var integer
     * @access public
     */
    var $appkey;
    
    /**
     * Blog ID for the target
     *
     * @var integer
     * @access public
     */
    var $blogid;
    
    /**
     * User ID for login
     *
     * @var string
     * @access public
     */
    var $userid;
    
    /**
     * Password for login
     *
     * @var string
     * @access public
     */
    var $password;
    
    /**
     * Prepare for the connection to XML-RPC client.
     *
     * @param string $path
     * @param string $host
     * @param integer $port
     * @param integer $appkey
     * @param integer $blogid
     * @param string $userid
     * @param string $password
     * @return boolean
     * @access public
     */
    function prepare($path,$host,$port,$appkey,$blogid,$userid,$password)
    {
      $this->client  =new xmlrpc_client($path,$host,$port);
      $this->appkey  =new xmlrpcval($appkey,’int’);
      $this->blogid  =new xmlrpcval($blogid,’int’);
      $this->userid  =new xmlrpcval($userid,’string’);
      $this->password  =new xmlrpcval($password,’string’);
      return true;
    }
    
    /**
     * Send XML-RPC message as "blogger.getUserInfo".
     *
     * @return mixed (successed:user information array, failed:false)
     * @access public
     */
    function blogger_getuserinfo()
    {
      $msg=new xmlrpcmsg(‘blogger.getUserInfo’,
          array(
            $this->appkey,
            $this->userid,
            $this->password
          )
        );
      $result=$this->client->send($msg);
      if(!$result){
        return false;
      }elseif($result->faultCode()){
        return false;
      }else{
        $tmpObj=$result->value();
        return $tmpObj->scalarval();
      }
    }
    
    /**
     * Send XML-RPC message as "blogger.getUsersBlogs".
     *
     * @return mixed (successed:user blog array, failed:false)
     * @access public
     */
    function blogger_getusersblogs()
    {
      $msg=new xmlrpcmsg(‘blogger.getUsersBlogs’,
          array(
            $this->appkey,
            $this->userid,
            $this->password
          )
        );
      $result=$this->client->send($msg);
&nbs
p;     if(!$result){
        return false;
      }elseif($result->faultCode()){
        return false;
      }else{
        $tmpObj=$result->value();
        return $tmpObj->scalarval();
      }
    }
    
    /**
     * Send XML-RPC message as "blogger.newPost".
     *
     * @param string $content, blog body
     * @param boolean $publish, re-build blog instantly or not
     * @return mixed (successed:posted article number, failed:false)
     * @access public
     */
    function blogger_newpost($content,$publish)
    {
      $msg=new xmlrpcmsg(‘blogger.newPost’,
          array(
            $this->appkey,
            $this->blogid,
            $this->userid,
            $this->password,
            new xmlrpcval($content,’string’),
            new xmlrpcval($publish,’boolean’)
          )
        );
      $result=$this->client->send($msg);
      if(!$result){
        return false;
      }elseif($result->faultCode()){
        return false;
      }else{
        $tmpObj=$result->value();
        return $tmpObj->scalarval();
      }
    }
    
    /**
     * Send XML-RPC message as "metaWeblog.newPost".
     *
     * @param array $article, blog title, body, and so on
     * @param boolean $publish, re-build blog instantly or not
     * @return mixed (successed:posted article number, failed:false)
     * @access public
     */
    function metaweblog_newpost($article,$publish)
    {
      $struct=array();
      foreach($article as $tmpKey => $tmpValue){
        $struct[$tmpKey]=new xmlrpcval($tmpValue,’string’);
      }
      $msg=new xmlrpcmsg(‘metaWeblog.newPost’,
          array(
            $this->blogid,
            $this->userid,
            $this->password,
            new xmlrpcval($struct,’struct’),
            new xmlrpcval($publish,’boolean’)
          )
        );
      $result=$this->client->send($msg);
      if(!$result){
        return false;
      }elseif($result->faultCode()){
        return false;
      }else{
        $tmpObj=$result->value();
        return $tmpObj->scalarval();
      }
    }
    
    /**
     * Send XML-RPC message as "metaWeblog.editPost".
     *
     * @param integer $postid, target article id
     * @param array $article, blog title, body, and so on
     * @param boolean $publish, re-build blog instantly or not
     * @return boolean
     * @access public
     */
    function metaweblog_editpost($postid,$article,$publish)
    {
      $struct=array();
      foreach($article as $tmpKey => $tmpValue){
        $struct[$tmpKey]=new xmlrpcval($tmpValue,’string’);
      }
      $msg=new xmlrpcmsg(‘metaWeblog.editPost’,
          array(
            new xmlrpcval($postid,’int’),
            $this->userid,
            $this->password,
            new xmlrpcval($struct,’struct’),
            new xmlrpcval($publish,’boolean’)
          )
        );
      $result=$this->client->send($msg);
      if(!$result){
        return false;
      }elseif($result->faultCode()){
        return false;
      }else{
        $tmpObj=$result->value();
        return $tmpObj->scalarval();
      }
    }
    
    /**
     * Send XML-RPC message as "metaWeblog.setPostCategories".
     *
     * @param integer $postid, target article id
   &n
bsp; * @param array $categories, array(array(int $category_id => bool $isPrimary),array(—),…)
     * @return boolean
     * @access public
     */
    function mt_setpostcategories($postid,$categories)
    {
      $myArray=array();
      foreach($categories as $tmpKey => $tmpValue){
        $tmpArray=array();
        $tmpArray[‘categoryId’]  =new xmlrpcval($tmpKey,’string’);
        $tmpArray[‘isPrimary’]  =new xmlrpcval($tmpValue,’boolean’);
        $myArray[]=new xmlrpcval($tmpArray,’struct’);
      }
      
      $msg=new xmlrpcmsg(‘mt.setPostCategories’,
          array(
            new xmlrpcval($postid,’int’),
            $this->userid,
            $this->password,
            new xmlrpcval($myArray,’array’)
          )
        );
      $result=$this->client->send($msg);
      if(!$result){
        return false;
      }elseif($result->faultCode()){
        return false;
      }else{
        $tmpObj=$result->value();
        return $tmpObj->scalarval();
      }
    }
    
    /**
     * Send XML-RPC message as "mt.publishPost".
     *
     * @param integer $postid, target article id
     * @return boolean
     * @access public
     */
    function mt_publishpost($postid)
    {
      $msg=new xmlrpcmsg(‘mt.setPostCategories’,
          array(
            new xmlrpcval($postid,’int’),
            $this->userid,
            $this->password
          )
        );
      $result=$this->client->send($msg);
      if(!$result){
        return false;
      }elseif($result->faultCode()){
        return false;
      }else{
        $tmpObj=$result->value();
        return $tmpObj->scalarval();
      }
    }
    
    /**
     * Send XML-RPC message as "mt.supportedMethod".
     *
     * @return mixed (successed:method array, failed:false)
     * @access public
     */
    function mt_supportedmethods()
    {
      $struct=array();
      foreach($article as $tmpKey => $tmpValue){
        $struct[$tmpKey]=new xmlrpcval($tmpValue,’string’);
      }
      $msg=new xmlrpcmsg(‘mt.supportedMethods’);
      $result=$this->client->send($msg);
      if(!$result){
        return false;
      }elseif($result->faultCode()){
        return false;
      }else{
        $tmpObj=$result->value();
        return $tmpObj->scalarval();
      }
    }
  }
?>

コードを自動生成するスクリプト

2007/03/16 | cakePHP

cakePHPはコントローラーで$scaffold変数をセットすることで、基本的なツール(追加、編集、削除、閲覧、一覧)の機能を全て提供してくれる。しかし、さすがにそれをそのまま納品するわけにはいかない。もう一手間かけてやる必要がある。
cakePHPには上記で提供されるスクリプトのスケルトンをファイルに書き出してくれる機能がある。それがbake.phpだ。bake.phpで書き出したファイルにはすでに基本的な機能は全て実装されているので、書き出されたファイルを編集するだけで、相当レベルのウェブアプリケーションになる。ちなみにbake.phpは以下のディレクトリに存在する。

(cakePHPのインストールディレクトリ)/cake/scripts/bake.php

上記ファイルをPHP実行ファイルの引数として実行すればよい。

これだけでも高機能だが、さらにこのbake.phpを拡張して、より便利にした
ツールがdecorate.phpだ。こちらはbake.phpの機能に、一覧表示の際のページング、ソート、検索機能を追加したものだ。これでもう「お腹いっぱい」なくらいすばらしいツールだ。

ダウンロードはこちら

使い方は、ダウンロードしたファイルを解凍し、できたファイル(decorate.php)をbake.phpと同じディレクトリに配置し、bake.phpと同様の呼び出し方で、引数をdecorate.phpとするだけ。

そして、さらに便利なツールがある。decorate.phpを拡張したmasterbake.phpだ。こちらはまだ使ったことがないんだけど、なんとテーブルの自動生成までやってくれる。

ダウンロードはこちら。

世の中には賢人がいるものだ。

デフォルトのコントローラーとアクションを設定

2007/03/15 | cakePHP

cakePHPで引数なしで起動された場合に動作するコントローラー&アクションの組み合わせを設定したい場合がある(「http://○○/index.php」といった具合に)。この設定はcakePHPでは「ルーティング」という。マニュアルでは「最低限のmod_rewriteのようなもの」と書いてある。

この場合cakePHPでは以下のファイルで設定をおこなう。
(インストールディレクトリ)/app/config/routes.php

ここで、デフォルトの設定で以下の記述がある。
$Route->connect(‘/’, array(‘controller’ => ‘pages’, ‘action’ => ‘display’, ‘home’));

これは’/’でアクセスした(フロントエンドのindex.phpにアクセスした)場合に、コントローラー「pages」、アクション「display」、第一パラメータ「home」を呼び出しなさい、ということらしい。これを自分の好きなものに変更すればよい。
例えば、引数なしでアクセスした場合にコントローラー「notes」アクション「memo」を起動したい、と思ったら、上記の行をコメントアウトして以下のように記述すればよい。

$Route->connect(‘/’, array(‘controller’ => ‘notes’, ‘action’ => ‘memo’));

これだけでOK。

文字化けしないはずの仕組みで文字化け

メールフォーム作成の仕事で文字化け問題に直面した(文字化けなんてすごく久しぶり)。メールフォーム自体はすでにmojavi2で作成したベースがあるので、何のことはない。「超簡単」のはずだった。しかし文字化けが発生してしまった・・・。ちなみに今回の環境はスクリプトはEUC-JP、HTMLはSJIS。

最初は何が問題なのかわからなかった。だって文字化けしたことがなかったから。たいていのサーバでPHPのマルチバイト関連は何も設定されていないから、問題なかったのかもしれない。今回はサーバ側で既定の設定がしてあったことが原因のようだ。

問題解決のお決まりの手法だが、まず表示されたソースの文字コードをEUC-JPやUTF-8にしてみた。しかし、それでもうまく表示してくれない。どの文字コードにしてもうまく表示されないということは、間違った文字コード変換をしているか、もしくは元々のテンプレートが指定された文字コードでないか、どちらかだ。テンプレートをダウンロードしてみたがどうやら設定どおりになっている。ということは間違った文字コード変換をしているようだ。

間違った文字コード変換にも2種類ある。元の文字コードや変換先の文字コードを設定とは異なるコードに変換してしまうことと、文字コード変換を多重にしてしまう(1回でいいのに)ことだ。

このプログラムは他のサーバで正常に動いているところを見ると、間違った文字コード変換をしているとは考えにくい。ということは余計な文字コード変換をしてしまっていると考えた。

このプログラムでは、マルチバイトがらみの設定の影響を避けるため、プログラムの先頭で以下のように記述している。

mb_language(‘Japanese’);
mb_internal_encoding(‘EUC-JP’);
mb_http_input(‘pass’);
mb_http_output(‘SJIS’);
ob_start(‘mb_output_handler’);

まず原因は「ob_start(‘mb_output_handler’);」の記述だった。

実はこのサーバ、phpinfoで情報を見ることが出来ない(制限がかけてあるのだ)。いちおうお仕事の発注元経由で問い合わせをかけることは出来るが、それにしても時間がかかる。phpinfoを見ることが出来ないのは、問題解決をむずかしくする要因だった。ではなぜ上記の記述がNGなのか。

多分php.iniで「mb_output_handler」が設定されていて、そこで正しく変換しているのに、再度上記の記述で変換をかけたために文字化けしていたと思われる。
ということで、問題の行をコメントアウトして正しく表示されることを確認した。

しかし問題その2。入力した文字が確認画面で文字化けして表示される。もしやと思いencoding_translation(自動文字変換)が有効になっていないかチェックした。案の定、上記設定が有効になっていた。
プログラムとしては、SJISのHTMLから飛んでくるデータはプログラム内部で内部円コーディングに変換するように組んであるのだが、これが余計。自動変換されたものを再度変換しているのだ。で、その行をコメントアウト。

これで正しく表示されるようになった。
PHPプログラムの文字化け解決の教科書みたいな事例でした。


守谷市(まちの情報ポータル) 無料アンケートレンタルjpForm.net