Home > アーカイブ > 2007-07

2007-07

UStream Viewersを増やす方法

この記事の所要時間: 13

UStreamではリアルタイムで動画を見ているユーザ数[Viewers]が表示されます。Viewersが多い動画は注目動画としてUStreamのトップページに表示されます。(はてぶと似たような仕組みですね。)

このViewesですが実は誰でも簡単に増やすことができます。

方法は簡単で、単に動画配信ページを複数のブラウザ(タブ)で開いていくだけです。これだけでViewersを増やすことができます。

普段4 Viewersとか5 Viewersな私の動画が26Viewersに。

UStreamのトップにも載りました;-)。

どうやら内部ではセッション数のみをカウントしていてクライアントIPなどは見ていないようです。ちなみにJava IRC chatでは「同一IPから開きすぎ」といった警告メッセージが表示されました。

今あなたが見ている人気動画は実は自作自演でViewersを増やしているものかもしれませんよ。:-p

なお現在はこの仕様ですが、以後どのように変わるかは分かりません。同一IPから多くのセッションを張るクライアントへは接続制限を行うなどの可能性もあります。また、無駄にサーバに負荷をかけることにもなりますのでご注意を。

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もまだまだ頑張っていますね。

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

Twitter 自分宛のメッセージを携帯で見る

この記事の所要時間: 046

今更ですが「Twitter で暇つぶし」を使って自分宛のメッセージを携帯で見る方法です。

Twitter で暇つぶし」でTwitterのユーザIDを指定すると、そのユーザが投稿したメッセージとそのユーザ宛のメッセージが表示されます。そこで自分のユーザIDを指定したURLを携帯に保存しておけば出先でも携帯で自分宛のメッセージを確認することができます。

例えばユーザIDがshin1x1なら下のように指定します。

http://twitter.1×1.jp/m/viewer/shin1x1

あとはこれを携帯でブックマークしておけばokです。

ちなみに「Twitter で暇つぶし」で見ることができるのは「Twitter検索」と同じですので、自分がaddしていない人からのメッセージも携帯で見ることができます。(逆にProtectしている人のメッセージは見れません。)

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 http://www.1x1.jp/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' => 'http://www.1x1.jp/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ベンチマーク CakePHPでモデルを使用しない

この記事の所要時間: 135

via: PHPベンチマーク: Zend Framework vs Symfony vs CakePHP vs CodeIgniter vs PHP on TRAX – 徒然なるままにBlog

エントリでPHPフレームワークのベンチが比較されており、とても興味深いものです。

ベンチ対象のソースが公開されているので、ざっと見てみると気になるところが一点ありました。

各フレームワークで行った処理はコントローラを呼び出しビューに遷移させて
“Hello World!”を表示させるだけのかなりシンプルな内容です。
DBへの接続やモデルの作成は行わず、自動レイアウト機能があるものはオフにするか全て削除しています。

PHPベンチマーク: Zend Framework vs Symfony vs CakePHP vs CodeIgniter vs PHP on TRAX – 徒然なるままにBlog

モデル作成を行わないはずなのにCakePHPだけモデルが生成されるようになっていました。

そこでモデル生成あり・なしでどの程度差が出るかを計ってみます。計測方法は[ab -c 100 -n 100]を10セットしてその平均を出しています。

モデル生成ありのソースは元エントリのまま。モデル生成なしは元エントリのhello_controller.phpを下のように変更しています。

<?php
class HelloNoModelController extends AppController{
  var $layout=null;
  var $autoLayout=false;
  var $uses = array(); // モデルを使用しない

  function hello()
  {
  }
}
?>

結果は以下のとおり。

モデル生成あり 5.145 Request/sec
モデル生成なし 6.381 Request/sec

実行環境が元エントリとは異なるので絶対的な数値にはあまり意味が無いですが、両者を比べるとモデル生成なしの方が20%ほど速いようです。

できれば元エントリの環境でモデル生成なし版によるベンチが見てみたいところです;-)。

Web API実践リファレンスブック

この記事の所要時間: 026

本屋で何気に「Web API実践リファレンスブック」をパラパラと見ていると楽天ウェブサービスのところで拙作Services_Rakutenが紹介されていました!

全然知りませんでした。こういうのって連絡とか来ないんですね。。。

# まあServices_Rakutenは放置していたのでアレですが;-)。

使っている人がいるかどうか分かりませんが、楽天ウェブサービスのAPIもかなり増えているので近日中に更新したいと思います。

CakePHP 1.1.15.5144以降はHtmlHelper#tagValueに注意

この記事の所要時間: 13

確認画面等で入力値を表示するのにHtmlHelper#tagValueを使っています。

これは内部でHTMLタグをエスケープしてくれるので重宝していたのですが、1.1.15.5144から、このメソッドにエスケープするか否かを表すフラグが追加されました。

[cake/libs/view/helpers/html.php]

<?php

   function tagValue($fieldName, $escape = false) {

?>

困った事にデフォルトではfalseになっているので、従来の[$html->tagValue(‘Foo/bar’);]のような使い方をしているとHTMLタグがそのまま出力されてしまいます。1.1.15.5144以降(昨日リリースされた1.1.16.5421も含む)でHTMLタグをエスケープするには以下のように第2引数にtrueを指定しなければなりません。

// tagValueでHTMLタグをエスケープ
<?php echo $html->tagValue('Foo/bar', true);
// もしくはh()で囲む
<?php echo h($html->tagValue('Foo/bar')) ?>;

1.1.14.4797以前から1.1.15.5144以降へバージョンアップする際はくれぐれもご用心を。

ちなみに1.2.0.5147ではtagValueがdeprecatedになっており、代わりにvalueメソッドを使うようになるようです。

CentOS yumでGPGキーエラー

  • 2007-07-03 (火)
  • unix
この記事の所要時間: 036

先日某所で借りたサーバがCentOS3だったのですが、yumでRPMを更新しようとすると以下のエラーが発生しました。

Error: You may also check that you have the correct GPG keys installed centos

GPGキーを入れろとの事なので以下のコマンドでインストールして解決。

# rpm --import http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-3

サーバ屋構築のサーバなんだから、あらかじめGPGキー入れといてくれても良いかと。

Home > アーカイブ > 2007-07

検索
フィード
メタ情報

Return to page top