Home > PHP

PHP Archive

CakePHP解説本を書きました

この記事の所要時間: 046

先日のPHPカンファレンス2007で安藤さんが発表されたとおりCakePHPの解説本が出版されます。

この本の執筆にcakephp.jp管理人である堂園さん、そしてPHP勉強会やカンファレンスでお馴染みの安藤さん、と共に参加させて頂きました。

原稿はほぼ書き終えた状況で、現在は10月出版を目指して諸々の作業を行っています。

これまでもCakePHPを扱った書籍はあったのですが、単独での解説本としては(世界?)初となります。基本的な使い方から実践的な内容までカバーしているので、CakePHPユーザはもちろんの事、フレームワークは初めてという方にもおすすめの一冊となっています。

現段階ではamazon等で予約はできませんが、正式な発売日など決まりましたら、またお知らせ致します。

お楽しみに!

via: CakePHP のおいしい食べ方: 間もなくCake本登場!!

PHPカンファレンス2007に参加してきました

この記事の所要時間: 258

2007/9/1に行われたPHPカンファレンス2007に参加してきました。

仕事の都合もあって木曜日から東京入りしていたので前日の会合から参加しました。また会場設営、撤去のお手伝いをする事もでき、以前参加した時に比べるとよりイベントに関われたので良かったです。

カンファレンスの講演は基調講演を除き2会場で行われたのですが、当初の予定どおりに動きました。どのセッションも面白かったのですが、特に午前中に行われた2セッション(基調講演、大規模サイトの構築・運用ノウハウ)は現在仕事で取り組んでいる内容と関連するものだったのでとても興味深かったです。

基調講演

PHP4/5/6の動向についてでした。特にPHP5.3/6については何となくは知っていたのですが、それほど熱心に追いかけていたわけでは無かったので、先のPHPが掴めたのが良かったです。

大規模サイトの構築・運用ノウハウ

ウノウ/GREEの両CTOによるサイト構築に関する具体的なノウハウを聞くことができました。こういった細かな実例を聞けることはあまり無いのですし、個人的にも現在取り組んでいる内容にリンクするものだったのでとても有用でした。

講演自体はウノウ->GREEの順で自社の事例を発表するという流れだったのですが、内容によってはいきなり相手に話しを振るシーンなどもありました。

とても興味深い内容だったので、次回(があれば)はもっと具体的な内容(開発の勘所やパフォーマンスチューニング等)も聞いてみたいです。

PHP Framework Update

Symfony / CakePHP / PieceFramework / Ethnaの各フレームワークについて
のセミナーでした。

当初予定されていたZendFrameworkはかなり期待していたので無くなってしまい残念でした。

あとCakePHPセッションでは共著者である安藤さんに、CakePHP本の告知をしていただきました。(安藤さんお疲れさまでしたー。)

PHP at Yahoo! JAPAN

Yahoo!のPHP環境についてです。PHPはかなり使っているようですが、使い方としては独自拡張を行っているようなのでいわゆる一般的な使い方とは異なるような印象を受けました。

やはりあれほどの大規模サイトになるとそうせざるを得ないのでしょうか。

実際のロジックはC/C++で組んで、それをPHPで実行させるというアプローチはとても興味深かったです。PHPでパフォーマンスを求めていった先の一つの形だと思います。

あと発表者であるogiさんはPHPとC/C++が書ける方なので、今後はPHP自体の開発を担われるのではないかと思います。(勝手に;-))

ライトニングトーク

ネタ系あり告知系ありでテンポ良く進みました。裏でやっていたビジネスセッションも気になったのですが、LTはライブ感が大事なのでこちらを選びました。(ビジネスセッションは誰かのレポートで追います。)

朝早かったのもありかなり眠気が来ていたのですが、ネタ系セッションでそれを吹き飛ばすことができました;-)

懇親会

100人以上(!)が参加という事で披露宴会場のような場所で立食形式で懇親会が行われました。それほど積極的には動かなかったのですが多くの方とお話することができました。お話させて頂いた皆さんありがとうございました。

あとじゃんけん大会でZend社TシャツをGETしました!どこかのイベントで着ていきます。:-D

PHP5.2.4ではPHPエラーでHTTP 500を返す

  • 2007-08-26 (日)
  • PHP
この記事の所要時間: 14

via: Gregory Szorc’s blog – PHP Now Using Proper HTTP Status Codes on Error

PHP5.2.4ではPHPエラーでHTTP 500 Internal Server Errorが返ってくるようにです。

PHP5.2.4RC3で試してみると、以下のような「Fatal Error」や「Parse Error」などスクリプトの実行が停止するようなエラーではHTTP 500が返ってきました。

<?php
// Fatal Error: Call to undefined function a() 
a();
?>
<?php
// Parse error: syntax error, unexpected '}'
$f = true;
if ($f) 
  echo "a";
}
?>

例外投げてcatchしない場合も同じです。

<?php
// Fatal error: Uncaught exception 'Exception'
throw new Exception();
?>

trigger_error()でE_USER_ERRORを発生させた場合もHTTP 500が出力されます。

<?php
trigger_error(null, E_USER_ERROR);
?>

ちなみにNoticeやWarningではこれまでどおりHTTP 200が返ってきました。

mod_phpを使っている時は見ることが無かったInternal Server Errorですが、これからは目にすることがありそうです。

あとHTTP経由でPHPアプリケーションを監視している場合は、頭に入れておきましょう。

PHPカンファレンス2007に参加します

この記事の所要時間: 043

9/1に開催されるPHPカンファレンス2007に参加します。

プログラムが公開されているのですが、複数の会場で講演があるのでどれを見るかは迷いますね。

基調講演以降は下のように動く予定です。

  • 11:00 大規模サイトの構築・運用ノウハウ(仮題)
  • 13:30 PHP Framework Update
  • 15:00 PHP at Yahoo! JAPAN
  • 16:00 パネルディスカッション or ライトニングトーク

16:00からは内容がまだ不明なのでそれに応じて考えます。

あと新幹線の時間はありますが、懇親会には出るつもりです。懇親会に興味あるけど行くのに抵抗がある方はこちらのエントリをどうぞ。(もし私で良ければ声懸けてやってください。お話しましょう。)

では皆さんよろしくお願いします。;-)

CakePHP データベースを使わないアプリケーション

この記事の所要時間: 220

CakePHPはデータベースを使用することが前提となっているので、フレームワークがデータベースへの接続を自動的に行います。ただマッシュアップ系のサービスなどデータベースを全く使用しない場合はこの機能を無効にしたくなります。

そこでCakePHPアプリケーション全体でデータベースを使わない方法です。

ちなみにこの方法では[app/config/database.php]を作成する必要もありません。

1. モデルを使わない

コントローラの$usesにnull or array()を設定することによりモデルを使用しないようにできます。データベースへの接続はモデルを介して行うのでモデルを使わなければ接続処理は行われません。(セッションやキャッシュをDBに保存する場合は別ですが)

<?php
class HogeController extends AppController {
  var $uses = null;
}
?>

ただこの方法だと当然ながらモデルは使用できませんし、各コントローラに$uses=nullを設定する必要があります。

2. AppModelに$useTableを設定する

モデルの$useTableにfalseを設定することにより、そのモデルではデータベースを使用しないようにできます。これを利用して基底クラスAppModelの$useTableにfalseを設定します。

[app/app_model.php]

<?php
class AppModel extends Model {
  var $useTable = false;
}
?>

この方法ならAppModelに設定するだけで、全てのモデルでデータベースを使用しなくなります。1.よりも手間がかからないのでオススメです。

フレームワークのビューテンプレートに注意

いずれの方法を取った場合でもデフォルトのルーティングのままで[http://example.com/]にアクセスするとデータベースに接続できないといったエラーメッセージが表示されます。

これは出力されるビューテンプレート[cake/libs/view/templates/pages/home.thtml]にてデータベース接続を行っているためです。このビューテンプレートはアプリケーション稼働時に出力することは通常無いので無視して構いません。

なおこのビューテンプレートへのルーティングをコメントアウトしてしまえば表示されなくなります。

[app/config/routes.php]

<?php
// コメントアウト
//	$Route->connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
?>

DATABASE_CONFIGを空にする方法はNG

“データベースをまったく使わない設定” フォーラム – CakePHP Users in JapanにDATABASE_CONFIGクラスを空にする方法が紹介されていますが、1.1.16.5421ではObject#cakeError()が呼ばれてしまうのでこの方法は使えません。ご注意を。

PostgreSQL8.1以降はoidに注意

この記事の所要時間: 131

PostgreSQL8.1以降はデフォルトではテーブルにoid列が存在しません。

このままだとPHP側からINSERTした後、pg_last_oid()で追加したレコードのoidを取り、それを使って追加したレコードをSELECTで読み込むという方法が使えません。

過去のアプリケーションを移行するなどしてoid列が必要な場合はテーブルを作成する際に以下のような方法を取る必要があります。

1. default_with_oidsをOnにする

postgresql.confにあるdefault_with_oidsをonに設定する(デフォルトはoff)とそれ以降に作成したテーブルにはoid列が生成されます。

default_with_oids = On

default_with_oidsはset文でも設定可能です。この場合設定したセッション内でのみ有効となります。

set default_with_oids = On;

2. CREATE TABLE文にWITH OIDSを付ける

CREATE TABLE文でテーブルを作成する際にWITH OIDSを付けておくとdefault_with_oidsの設定に関わらず、oid列が生成されます。

create table test(id int) with oids;

先日とあるアプリケーションをPostgreSQL7.2から8.1に移行した際にこの問題が発生しました。シーケンスから直前に追加された値を取得するなど他の方法もあったのですが、PHP側を変更したくなかったので、default_with_oids=onにして対応しました。

UStreamはPHPで動いてる

この記事の所要時間: 10

近頃Twitter界隈でアツいのがUStreamです。これは動画配信サービスなのですが、なかなか良くできていてWebカメラさえあれば誰でも簡単にライブ動画を配信することができます。

配信するにせよライブを見るにせよ話を聞いただけでは「何が面白いの?」なサービスなのですが、これがやってみると面白いんですね。Webカメラを使っている人がほとんどなのでPC前で作業している絵面ばかりなのですが、一緒に作業している感があって良いです。SOHOやフリーランスで仕事している人にはさぼり防止に良いかもしれません;-)

さてそんなUStreamですが、サーバ側はPHPで動いています。HTTPレスポンスヘッダを見るとPHP5.2.0を使っているようです。

Server: Apache
X-Powered-By: PHP/5.2.0

動画配信やコメント一覧表示など各所でPHPが使われている箇所が見られます。

Twitterをはじめ昨今のWeb2.0的サービスではRoRが注目されることが多いですが、PHPもまだまだ頑張っていますね。

開発・運用のノウハウが公開されるのを心待ちにしたいところです。

HTTPリスエストをそのまま返すPHPサーバ

  • 2007-07-21 (土)
  • PHP
この記事の所要時間: 124

PHPで簡易サーバを書いてみました。ブラウザから来たHTTPリクエストをそのまま返すだけのものです。実用性はあまり無いですが、ブラウザがどんなリクエストを出しているか確認するにはちょっとだけ便利かもしれません。;-)

socketsライブラリを有効にする

PHPでsocketsライブラリが有効になっている必要があります。Win環境ならPHPパッケージにDLLが用意されているので、php.ini等で下のように有効にすればokです。

# ; を外す
extension=php_sockets.dll

サーバを起動する

下のソースをローカルに保存して、phpコマンドで実行するとサーバが起動します。デフォルトではlocalhostの9000ポートでlistenします。

<?php
class HttpServer {
  private $socket = null;

  private $method = null;
  private $uri = null;
  private $headers = null;
  private $body = null;

  public function __construct($port = 9000) {
    $this->socket = socket_create_listen($port);
    if ($this->socket === false) {
      $this->error(); 
    }
  }

  public function execute() {
    while (true) {
      $client = socket_accept($this->socket);
      if ($client === false) {
        $this->error();
      }

      $request = $this->read($client);

      $header = "HTTP/1.x 200 OK\r\n";
      $header .= "Content-Type: text/html\r\n";
      $header .= "\r\n\r\n";
      socket_write($client, $header, strlen($header));

      $request = nl2br($request);
      socket_write($client, $request, strlen($request));
      socket_close($client);
    }
  }

  private function read($client) {
    $this->body = "";
    $req = "";
    while (true) {
      $buff = socket_read($client, 2048, PHP_BINARY_READ);
      $req .= $buff;

      if ($buff === false) {
        $this->error();
      } else if ($buff === "" || strlen($buff) < 2048) {
        if (preg_match("/\r\n\r\n$/", $req)) {
          $this->parseHeader($req);
        } else {
          $this->body .= $buff;
        }

        if ($this->isComplete()) {
          return $req;
        }
      }
    }
  }

  protected function parseHeader($req) {
		$lines = explode("\r\n", $req);
    $this->setRequestLine(array_shift($lines));

    foreach ($lines as $line) {
      $line = trim($line);
      if (empty($line)) {
        continue;
      }
      $this->setHeader($line);
    }
  }

	protected function setRequestLine($line) {
		list($this->method, $this->uri) = explode(" ", $line);
	}

	protected function setHeader($line) {
		$header = explode(":", $line);
		$this->headers[trim(array_shift($header))] = trim(join( ":", $header ));
	}

  protected function isComplete() {
    if ($this->method == "POST" && !empty($this->headers['Content-Length'])) {
      return strlen($this->body) == $this->headers['Content-Length'];
    }

    return true;
  }

  private function error() {
    $message = sprintf("error:%d %s", socket_last_error()
                                    , socket_strerror(socket_last_error()));
    die($message);
  }

  public function __destruct() {
    if (is_resource($this->socket)) {
      socket_close($this->socket);
    }
  }
}

$server = new HttpServer();
$server->execute();
?>

ブラウザでサーバへアクセスする

あとはブラウザで[http://localhost:9000/]にアクセスすればリクエストの内容がそのまま表示されます。適当なフォームをHTMLで作ってPOSTするとその内容もそのまま表示されます。

次はHTTPサーバ?

新大阪<->品川の新幹線で時間があったのでちゃちゃっと書いてみました。本当はもうちょっとちゃんとしたHTTPサーバもどきにしようと考えていたのですが、これはこれで面白かったのでアップしときます。。。

あとコードを書く際はZendFramework付属のhttp_serverを参考にしました。ZFはコードが読みやすいので結構好印象です。PHP5フレームワーク・ライブラリとして今後が楽しみですね。

携帯サイトに楽天ダイナミックアドを設置する

この記事の所要時間: 14

先日リリースされた楽天ダイナミックアドですが、携帯サイト用の商品表示パーツがありません。

そこで楽天ウェブサービスを使って携帯サイト用の商品表示をPHP5で作ってみました。

今回はServices_Rakutenを使わず、SimpleXMLで実装しています。

<?php
$params = array();
$params&#91;'developerId'&#93; = DEV_ID;
$params&#91;'affiliateId'&#93; = AFF_ID;
$params&#91;'url'&#93; = 'http://example.com' . $_SERVER&#91;'REQUEST_URI'&#93;;
$params&#91;'carrier'&#93; = '1'; // 携帯

$query = "";
foreach ($params as $k => $v) {
if ($query) $query .= '&';
  $query .= $k . '=' . urlencode($v);
}
$url = sprintf("http://dynamic.rakuten.co.jp/rcm/1.0/i/rest?%s", $query);
$xml = simplexml_load_file($url);
?>

あとは$xmlの値を使ってHTMLを組み立てればokです。(取得した商品情報はUTF-8なので、必要があればSJISに変換します。)

試しに昨日からTwitterで暇つぶしに設置しています。携帯サイトは画面サイズが小さいので商品を一つだけ表示しています。また表示する商品も商品名が一番短いものを選択しています。

効果の程は分かりませんが、APIを使ってコンテンツにマッチした商品情報を簡単に取得できるのは中々面白いですね。

一つ気になったのはクローラーのアクセス頻度です。クローラーが来ることについては楽天ウェブサービスにも明記されていますので特に問題ありません。

ただ短時間に一気にアクセスしに来る事があるようで、今日の昼頃は100秒間に2000件近くのアクセスが来たこともありました。今後改良されていくかもしれませんが、動的サイトに設置される方は念のためご注意を。

楽天API PEAR::Services_Rakuten-0.2.0リリース

  • 2007-07-17 (火)
  • PHP
この記事の所要時間: 34

楽天ウェブサービスをPHPで利用するPEARライブラリ「PEAR::Services_Rakuten-0.2.0」をリリースしました。今回は未対応だったAPIの対応のほか、リファクタリングを行い、今後拡張しやすい形にソースコードを変更しました。

主な変更点は以下です。

  • 2007/07/17現在の全APIに対応(楽天ダイナミックアドAPI含む)
  • ソースコードリファクタリング
  • リファクタリングに伴い利用方法を変更(後述)

インストール・アンインストール

インストール方法は以下です。

$ pear install --alldeps /blog/download/Services_Rakuten-0.2.0.tgz

インストール時に「Failed to download pear/XML_Serializer within preferred state “stable”」といったエラーが発生した場合は以下のコマンドでXML_Serializerをインストール後、Services_Rakutenをインストールして下さい。

$ pear install --alldeps XML_Serializer-beta

アンインストール方法は以下です。

$ pear uninstall __uri/Services_Rakuten

Services_Rakutenを使う[0.2.0用]

0.2.0では以下のコードのようにServices_Rakuten::factory()にて、利用するAPIコード・デベロッパーID・アフィリエイトIDを指定します。このメソッドは各APIを実行するインスタンス(apiインスタンス)を返します。

apiインスタンスのexecuteメソッド実行する楽天ウェブサービスへリクエストが送信されます。executeメソッドは連想配列を引数として受け取るので各種パラメータを設定します。なおexecuteメソッドでは文字コードの変換は行いませんので、keywordなど日本語を渡す場合はUTF-8で指定して下さい。

取得した結果は、apiインスタンスのgetResultDataメソッドにて取得できます。

<楽天市場系API>

<?php
require_once('Services/Rakuten.php');
define('DEV_ID', 'xxxx');
define('AFF_ID', 'xxxx');

// 楽天商品検索
$api = Services_Rakuten::factory('ItemSearch', DEV_ID, AFF_ID);
$api->execute(array('keyword' => '大福'));
var_dump($api->getLastUrl());
var_dump($api->getResultData());

// 楽天商品ジャンル検索
$api = Services_Rakuten::factory('GenreSearch', DEV_ID, AFF_ID);
$api->execute();
var_dump($api->getLastUrl());
var_dump($api->getResultData());

// 楽天商品コード検索
$api = Services_Rakuten::factory('ItemCodeSearch', DEV_ID, AFF_ID);
$api->execute(array('itemCode' => 'book:11907840'));
var_dump($api->getLastUrl());
var_dump($api->getResultData());

// 楽天カタログ検索
$api = Services_Rakuten::factory('CatalogSearch', DEV_ID, AFF_ID);
$api->execute(array('keyword' => 'ワンセグ'));
var_dump($api->getLastUrl());
var_dump($api->getResultData());
?>

<楽天ブックス系API>

<?php
require_once('Services/Rakuten.php');
define('DEV_ID', 'xxxx');
define('AFF_ID', 'xxxx');

// 楽天書籍検索
$api = Services_Rakuten::factory('BookSearch', DEV_ID, AFF_ID);
$api->execute(array('keyword' => 'ブログ'));
var_dump($api->getLastUrl());
var_dump($api->getResultData());

// 楽天CD検索
$api = Services_Rakuten::factory('CDSearch', DEV_ID, AFF_ID);
$api->execute(array('keyword' => '氷室'));
var_dump($api->getLastUrl());
var_dump($api->getResultData());

// 楽天DVD検索
$api = Services_Rakuten::factory('DVDSearch', DEV_ID, AFF_ID);
$api->execute(array('keyword' => '氷室'));
var_dump($api->getLastUrl());
var_dump($api->getResultData());
?>

<楽天トラベル系API>

<?php
require_once('Services/Rakuten.php');
define('DEV_ID', 'xxxx');
define('AFF_ID', 'xxxx');

// 楽天トラベル施設検索
$api = Services_Rakuten::factory('SimpleHotelSearch', DEV_ID, AFF_ID);
$api->execute(array('largeClassCode' => 'japan', 'middleClassCode' => 'kanagawa', 'smallClassCode' => 'hakone'));
var_dump($api->getLastUrl());
var_dump($api->getResultData());

// 楽天トラベル施設情報
$api = Services_Rakuten::factory('HotelDetailSearch', DEV_ID, AFF_ID);
$api->execute(array('hotelNo' => '65638'));
var_dump($api->getLastUrl());
var_dump($api->getResultData());

// 楽天トラベル空室検索
$api = Services_Rakuten::factory('VacantHotelSearch', DEV_ID, AFF_ID);
$api->execute(array('largeClassCode' => 'japan', 'middleClassCode' => 'kanagawa', 'smallClassCode' => 'hakone'
                  , 'checkinDate' => '2007-07-18', 'checkoutDate' => '2007-07-20'));
var_dump($api->getLastUrl());
var_dump($api->getResultData());

// 楽天トラベル地区コード
$api = Services_Rakuten::factory('GetAreaClass', DEV_ID, AFF_ID);
$api->execute();
var_dump($api->getLastUrl());
var_dump($api->getResultData());

// 楽天トラベルキーワード検索
$api = Services_Rakuten::factory('KeywordHotelSearch', DEV_ID, AFF_ID);
$api->execute(array('keyword' => '伊豆'));
var_dump($api->getLastUrl());
var_dump($api->getResultData());
?>

<その他API>

<?php
require_once('Services/Rakuten.php');
define('DEV_ID', 'xxxx');
define('AFF_ID', 'xxxx');

// 楽天ダイナミックアド
$api = Services_Rakuten::factory('DynamicAd', DEV_ID, AFF_ID);
$api->execute(array('url' => '/blog/'));
var_dump($api->getResultStatus());
var_dump($api->getResultStatusMessage());
var_dump($api->getLastUrl());
var_dump($api->getResultData());
?>

Services_Rakutenを使う[0.1.0用]

従来からあったdoItemSearch/doGenreSearch/doItemCodeSearch/doBookSearchメソッドはdeprecated(非推奨)となっています。0.2.0では動作しますが、今後のリリースでは廃止される可能性があります。

今後開発される際はServices_Rakuten::factoryによる利用方法で実装して下さい。

リファクタリングの効果

ちょうどリリース作業を行っている最中に楽天ダイナミックアドAPIが発表されました。「げげっ」と思いつつも、どうせなら今回のリリースに含めたいと思い作業を開始したところ、API拡張用にリファクタリングを行っていたため短時間で機能追加を行うことができました。

早速リファクタリングの効果が発揮され、我ながら嬉しかったです;-)。

ホーム > PHP

検索
フィード
メタ情報

Return to page top