Hatena::ブログ(Diary)

slowbirds.d.hatena RSSフィード

10.09.2013 iPhone5sが届いたからJavascriptのベンチマーク取ってみた

[] iPhone5sが届いたからJavascriptベンチマーク取ってみた

僕のではなくて会社に届いたのでiPhone5(iOS7.0.2)と比較してみた。

使ったベンチマークWEBKITSunSpiderです。

モバイルSafari上で普通に開いて確認しました。

iPhone5s (iOS 7.0.2)

============================================
RESULTS (means and 95% confidence intervals)
--------------------------------------------
Total:                  398.8ms +/- 1.1%
--------------------------------------------

  3d:                    66.7ms +/- 0.7%
    cube:                26.3ms +/- 1.3%
    morph:               16.7ms +/- 2.1%
    raytrace:            23.7ms +/- 2.0%

  access:                36.2ms +/- 1.8%
    binary-trees:         5.3ms +/- 6.5%
    fannkuch:            14.9ms +/- 2.7%
    nbody:                9.3ms +/- 3.7%
    nsieve:               6.7ms +/- 5.2%

  bitops:                20.4ms +/- 3.0%
    3bit-bits-in-byte:    1.4ms +/- 26.4%
    bits-in-byte:         7.7ms +/- 6.3%
    bitwise-and:          4.8ms +/- 6.3%
    nsieve-bits:          6.5ms +/- 5.8%

  controlflow:            3.8ms +/- 11.9%
    recursive:            3.8ms +/- 11.9%

  crypto:                34.1ms +/- 5.3%
    aes:                 16.4ms +/- 3.0%
    md5:                  9.5ms +/- 10.8%
    sha1:                 8.2ms +/- 10.7%

  date:                  52.7ms +/- 5.5%
    format-tofte:        29.9ms +/- 2.1%
    format-xparb:        22.8ms +/- 10.4%

  math:                  32.2ms +/- 1.8%
    cordic:               9.0ms +/- 0.0%
    partial-sums:        18.3ms +/- 2.6%
    spectral-norm:        4.9ms +/- 8.3%

  regexp:                17.6ms +/- 2.1%
    dna:                 17.6ms +/- 2.1%

  string:               135.1ms +/- 1.4%
    base64:              11.6ms +/- 6.0%
    fasta:               20.4ms +/- 3.0%
    tagcloud:            27.6ms +/- 2.5%
    unpack-code:         59.1ms +/- 2.8%
    validate-input:      16.4ms +/- 5.1%

iPhone5 (iOS7.0.2)

============================================
RESULTS (means and 95% confidence intervals)
--------------------------------------------
Total:                  717.9ms +/- 1.5%
--------------------------------------------

  3d:                   114.1ms +/- 1.0%
    cube:                39.5ms +/- 1.8%
    morph:               33.4ms +/- 2.1%
    raytrace:            41.2ms +/- 1.8%

  access:                60.8ms +/- 1.3%
    binary-trees:         9.6ms +/- 5.2%
    fannkuch:            26.4ms +/- 1.4%
    nbody:               16.0ms +/- 3.0%
    nsieve:               8.8ms +/- 6.4%

  bitops:                35.8ms +/- 2.6%
    3bit-bits-in-byte:    2.8ms +/- 16.1%
    bits-in-byte:        13.3ms +/- 3.6%
    bitwise-and:          7.4ms +/- 5.0%
    nsieve-bits:         12.3ms +/- 4.8%

  controlflow:            9.2ms +/- 6.1%
    recursive:            9.2ms +/- 6.1%

  crypto:                62.3ms +/- 3.3%
    aes:                 28.9ms +/- 3.0%
    md5:                 18.6ms +/- 11.5%
    sha1:                14.8ms +/- 9.9%

  date:                  87.5ms +/- 2.0%
    format-tofte:        47.3ms +/- 2.1%
    format-xparb:        40.2ms +/- 2.9%

  math:                  71.8ms +/- 9.2%
    cordic:              14.7ms +/- 2.3%
    partial-sums:        43.5ms +/- 14.8%
    spectral-norm:       13.6ms +/- 3.7%

  regexp:                36.9ms +/- 1.9%
    dna:                 36.9ms +/- 1.9%

  string:               239.5ms +/- 1.3%
    base64:              20.9ms +/- 8.4%
    fasta:               45.5ms +/- 2.8%
    tagcloud:            47.2ms +/- 1.9%
    unpack-code:         98.0ms +/- 2.0%
    validate-input:      27.9ms +/- 2.8%

5s早いですね。

10.01.2013 UbuntuでRedmineとGitlabをNGINXでホスティングする

[][][][][][][] UbuntuRedmineとGitlabをNGINXでホスティングする

ちなみにUbuntu12系でやってます。

今回のディレクトリとか構成とか

全て"projects”ユーザでの動作に統一する

/home/projects

以下にドキュメントルート、redmine、gitlabディレクトリを作成する

http://localhost/
    /gitlab
    /redmine

というURLで動作するようにする


NGINXのインストール

普通

sudo aptitude install nginx

Redmineインストール

注意点

Redmineのコンフィグはほぼデフォルトのままだけど

> /home/projects 以下に"redmine_attachments"というディレクトリを作っておく

で作っておいたディレクトリに添付ファイルとかを置くように設定をいじっておくとRedmineディレクトリから添付ファイル類を独立させられるのでバージョンアップ時とかバックアップが楽です。

/home/projects/redmine/config/configuration.yml

attachments_storage_path: /home/projects/redmine_attachments

Unicornで起動する設定類

redmineのconfigディレクトリUnicorn用configファイルを配置
worker_processes 2
user "projects", "projects"
working_directory "/home/projects/redmine"

listen File.expand_path("tmp/unicorn.sock", ENV['RAILS_ROOT'])
pid File.expand_path("tmp/pids/unicorn.pid", ENV['RAILS_ROOT'])

timeout 60

preload_app true

stdout_path File.expand_path("log/unicorn.stdout.log", ENV['RAILS_ROOT'])
stderr_path File.expand_path("log/unicorn.stderr.log", ENV['RAILS_ROOT'])

GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true

before_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!

  old_pid = "#{server.config[:pid]}.oldbin"
    if old_pid != server.pid
      begin
        sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
        Process.kill(sig, File.read(old_pid).to_i)
      rescue Errno::ENOENT, Errno::ESRCH
      end 
    end 

    sleep 1
  end 

after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end
/etc/init.d/redmine にサービスとしてUnicorn叩きつけるような感じのスクリプト置く
sudo vi /etc/init.d/redmine
#!/bin/sh
 
export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin
 
# move to project root directory
NAME=redmine
ENVIROMENT=production
 
# SCRIPT_DIR=`dirname $0`
# ROOT_DIR=$(cd "${SCRIPT_DIR}/../../"; pwd)
ROOT_DIR="/home/projects/redmine"
 
PID="${ROOT_DIR}/tmp/pids/unicorn.pid"
CONF="${ROOT_DIR}/config/unicorn.rb"
 
start()
{
  if [ -e $PID ]; then
    echo "$NAME already started";
    exit 1;
  fi
  echo "start $NAME";
  cd $ROOT_DIR
  bundle exec unicorn_rails -c ${CONF} -E ${ENVIROMENT} -D --path /redmine
}
 
stop()
{
  if [ ! -e $PID ]; then
    echo "$NAME not started";
    exit 1;
  fi
  echo "stop $NAME";
  kill -QUIT `cat ${PID}`
  rm -f $PID
}
 
force_stop()
{
  if [ ! -e $PID ]; then
    echo "$NAME not started";
    exit 1;
  fi
  echo "stop $NAME";
  kill -TERM `cat ${PID}`
  rm -f $PID
}
 
reload()
{
  if [ ! -e $PID ]; then
    echo "$NAME not started";
    start
    exit 0;
  fi
  echo "reload $NAME";
  kill -HUP `cat ${PID}`
}
 
restart()
{
    stop
    start
}
 
case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  force-stop)
    force_stop
    ;;
  reload)
    reload
    ;;
  restart)
    restart
    ;;
  *)
    echo "Syntax Error: release [start|stop|force-stop|reload|restart]"
    ;;
esac

サブディレクトリで運用する場合は

bundle exec unicorn_rails -c ${CONF} -E ${ENVIROMENT} -D --path /redmine

これが重要!

Gitlabのインストール

今回は最新の6.1-stableからインストールしてみました。

基本的にオフィシャルのリポジトリに入ってるインストールドキュメントの通りにやれば何の問題もありません。

https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md

ただし、オフィシャルドキュメントは

"git"ユーザとして"/home/git"以下で構築してドキュメントルートで運用する。という想定になっているのでそこだけ読み替える必要があります。

問題はドキュメントルートではなくサブディレクトリで運用する設定ですが、これも5.0の頃と違って現在のバージョンではちゃんと説明されているので迷うことはないです。

1.gitlab/config/gitlab.yml
relative_url_root: /gitlab

の行コメントを外す

2.gitlab/config/application.rb

最下部の

config.relative_url_root = "/gitlab"

の行コメントを外す

3.gitlab/config/unicorn.rb
ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"

追記する。適当にListenの下に追加しておきました。

特筆すべきはこんくらいかと思います。

ユーザ名や置き場所をデフォルトから変えているので忘れずに

設置した/etc/init.d/gitlab の上部の設定値類をしっかり確認しておくこと


不思議な現象:sidekiqでエラー

運用し始めたらsidekiqがシェルを叩くのに毎度失敗しているようだったのでエラーを漁って見たところなぜかシェルを"git"ユーザとして叩こうとしてユーザがいないというエラーが出ている。

エラーの場所をたどると

config.gitlab_shell.ssh_user という値を参照しているようだけどそんな値は設定されていないので適当に

gitlab/config/gitlab.yml の gitlab_shell 部分に

ssh_user: projects

と一行追加してみたらエラーが消えて今のところ問題なく動いている様子。

正しい運用なのかは不明です。

NGINXの設定

ローカルサーバで外に開いてないのでセキュリティとか全然気にしておりません。

とりあえず最低限の設定のみ。

upstream gitlab {
  server unix:/home/projects/gitlab/tmp/sockets/gitlab.socket;
}
upstream redmine {
  server unix:/home/projects/redmine/tmp/unicorn.sock fail_timeout=0;
}

server {
  listen 80 default deferred;
  server_name localhost;

  location / {
    root /home/projects/public;
    index index.html;
  }

  location /gitlab {
    root /home/projects/gitlab/public;
    try_files $uri @gitlab;
  }

  location @gitlab {
    if (-f $request_filename) { break; }
    proxy_read_timeout 300;
    proxy_connect_timeout 300;
    proxy_redirect     off;

    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_set_header   Host              $http_host;
    proxy_set_header   X-Real-IP         $remote_addr;

    proxy_pass http://gitlab;
  }
  
  location /redmine {
    root /home/projects/redmine/public;
    try_files $uri @redmine;
  }
  location @redmine {
    if (-f $request_filename) { break; }
    proxy_read_timeout 300;
    proxy_connect_timeout 300;
    proxy_redirect     off;

    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_set_header   Host              $http_host;
    proxy_set_header   X-Real-IP         $remote_addr;

    proxy_pass http://redmine;
  }

  location /jenkins {
    proxy_pass http://localhost:8080/jenkins;
  }

}

あ、Jenkinsも入ってます。


最終的に/home/projects以下はこんな感じになってました

ls -la /home/projects

gitlab
gitlab-satelites
gitlab-shell
public
redmine -> /home/projects/redmine2.3/
redmine2.3
redmine_attachments
repositories

終わり。

大雑把エントリーですが誰かの参考になれば幸いです

03.23.2012

[][][] 会社の環境

現在会社(受託WEB開発会社)で開発環境とか作る業務を持っていまして

受託案件はすべて僕が作った環境上のRedmineSVNで管理しています。(本当はGit使いたいけど色々と問題が・・・)

UbuntuServer(HDD 1TB)に

# Apache

# Passenger(mod_rails)

# RubyOnRails

# MySQL

# Subversion

みたいな感じでやっていてSVNはRemine.pmを使って認証しているのでRedmineアカウントで関連付けられたリポジトリのみRW可能みたいにしています。

これまとめよう。

02.22.2012

[]history.pushStateの話

現実的に考えるとサーバサイドも必要になるので、それも含めて作成。自分のサイトに適用してみました。

http://slowbirds.org

案外コントロールが面倒でした。。。

サーバサイドも含めてのサンプルってあんまり見かけないかも・

history.pushState

基本的には履歴にページを残してURLを書き換えるだけ。

ようするに http://hogehoge.com/#!/user みたいなダサイURLにしなくても http://hogehoge.com/userURLを書き換えつつ画面遷移をしない。ということが実現できるAPI

詳しくはこちらとか history.pushState、history.replaceState - 素人がプログラミングを勉強していたブログ 参照

history.pushState自体は履歴にページ(ドメインルートから絶対パス)を記録してURLを変えてくれるだけでそれ以外は何もしてくれません。

あとは元々存在する onpopstate イベントで戻る、進む、更新のイベントを回収してページの書き換えを行います。

なぜサーバサイドが必要か

上記だけであればURLパラメータにあわせてコンテンツを出し分ければいいだけなのでJavascriptのみでページ作成ができます。

が、せっかくURLをしっかり書き換えているので対応していないブラウザ検索エンジンにもしっかり見せてあげたいじゃないですか!

ということでサーバサイドと.htaccessでいい感じにしてあげます。

理想はhistory.pushStateに対応したブラウザは画面遷移をなくし、対応していないブラウザは従来通り普通に画面遷移をする。といった動作(今回はそこまで詰めてないですが・・・)

今回はサーバの関係でPHPで書きました。PHP側のコードは下に記載しますが

僕の解決方法としては

ヘッダやフッタなんかも別パーツにしておいて

  • ?page= でページ名を指定。ヘッダやフッタも合体しながら出力
  • $X-PJAX=true のパラメータがある場合はヘッダやフッタなどのパーツを出さずにページ本体部分だけを出力

として、Javascriptでアクセスが来たことをGETパラメータで明示しています。

これは有名なプラグインではHTTPリクエストヘッダにX-PJAXという値を追加していたのでそのまま名前をパクりました。

これで

  • 通常のリクエスト /?page=scraps で普通のページとして表示
  • Javascriptの非同期通信でリクエスト /?page=scraps&X-PJAX=true ほしい部分だけのテンプレートファイル

と、切り分けをしてを出力できる

あとはmod_rewrite

#mod rewrite
RewriteEngine on
RewriteBase /
RewriteRule ^([a-zA-Z_0-9]+)/?$ /?page=$1

こうしてあげれば

/scraps でアクセスが来た時、Apacheに /?page=scraps でリクエストが飛ぶので

history.pushStateで発行したURLと実際にサーバが吐き出すURLの整合性が取れて無事ページが表示されるのでしたー

ハマったポイント

  • onpopstateでそのままページ書き換え処理に流してたらhistory.pushStateが動くので更に履歴を追加してしまった→更新するたびに同じURLの履歴がたくさんできていくら戻っても戻れなくなる
  • 読み込み先のコンテンツによってJavascriptを実行してあげる処理をいれたかったがあまりうまく実装できた無いと思う。多分もっとprototypeの構造理解せんといかん
  • 同期処理がうまくできていない。 callbackとかの仕組みを上手く作れてなくて泥臭い。jsDefferedみたいな構造をちゃんと使って処理を順番に流すような作りにすべき
  • 一回目書いたときはあまりに例外処理の取りこぼしが多かったので一回ひと通り作ってから全部けしてやり直した。なんとかかんとか最初よりは綺麗になったかな・・・
  • やはりJavascriptというかプログラムの仕組みをもっと1から勉強し直さないといかん。言語のリファレンスというよりは構造をどうするか?で悩む場面が多すぎて・・・

とりあえず

各種コードは汚いのですがこんな感じです。jqueryで手抜きしてます。

本体
<?php
CLASS Loader {
  public function __construct() {
  }
  public function publish() {
    $page = isset($_GET['page']) ? $_GET['page'] : "top";
    if(preg_match('/^[a-zA-Z_0-9\-]+$/',$page)) {
      if($page == "index") {
        $page = "top";
      }
    }else {
      $page = "top";
    }
    $template = $this->getTemplate($page);
    if($template == "") {
      $this->setError($template);
    }else {
      $this->setPage($page);
    }
  }
  private function isFile($file) {
    if(file_exists($file)) {
      return true;
    }else {
      return false;
    }
  }
  private function isPjax() {
    if(isset($_GET['X-PJAX'])) {
      return true;
    }else {
      return false;
    }
  }
  private function setError($code) {
    if($code == 404) {
      header("HTTP/1.0 404 Not Found");
    }
    $this->setPage(PATH_ERROR.$code.".html");
  }
  private function setPage($page) {
    if($this->isPjax()) {
      print($this->getTemplate($page));
    }else {
      print($this->makePage($page));
    }
    return;
  }
  private function makePage($page) {
    $dir = PATH_TEMPLATE;
    
    $meta_path   = $dir."meta".".html";
    $header_path = $dir."header".".html";
    $bottom_path = $dir."bottom".".html";
    
    $meta   = file_get_contents($meta_path);
    $header = file_get_contents($header_path);
    $bottom = file_get_contents($bottom_path);
    
    $content = $this->getTemplate($page);
    
    $page = $meta.$header.$content.$bottom;
    
    return $page;
  }
  private function getTemplate($page) {
    $dir = PATH_TEMPLATE;
    $template_path = $dir.$page.".html";
    if($this->isFile($template_path)) {
      $template = file_get_contents($template_path);
    }else {
      $template = "";
    }
    return $template;
  }
}
?>
index.php
<?php
require_once("./modules/Loader/configure.php");
require_once("modules/Loader/Loader.class.php");
$loader = new Loader();
echo $loader->publish();
?>

コンテンツ切り替えの本体Javascript
var SCC;
(function(){
var SlowbirdsContentsController = function(config){
  if(typeof($) == "undefined") {
    return false;
  }
  this.config = {
    domain: "slowbirds.org",
    base: "/",
    api: "/",
    script: ["scraps"],
    scriptPath: "/shared/js/SlowbirdsContentsController.",
    index: "top",
    content: "#contents"
  }
  this.init();
  this.entire = false;
}
SlowbirdsContentsController.prototype = {
  /* utility */
  init: function() {
    if(location.pathname == "/") {
      this.loaded = "top";
    }else {
      this.loaded = location.pathname.replace(/^\//,"");
    }
    this.content = $(this.config.content);
    this.events();
    this.statusMake();
    this.statusBusy();
    this.loadScript(this.loaded);
    return this;
  },
  events: function() {
    var par = this;
    $(window).bind('popstate',function(e) {
      e.preventDefault();
      par.getContent(location.pathname.replace(/^\//,""),false)
    });
    return this;
  },
  scrollto: function() {
    var target = location.hash ? location.hash:false;
    if(!target) {
      var position = 0;
    }else {
      var position = $(target).offset().top;
      position = position-50;
    }
    $($.browser.safari ? 'body' : 'html').animate({scrollTop:position}, 400, 'swing');
    return;
  },
  isScript: function(content) {
    for(var i=0;i<this.config.script.length;i++) {
      if(this.config.script[i] === content) {
        return true;
      }
    }
    return false;
  },
  unhash: function(string) {
    return string.replace(/\#[a-zA-Z_0-9]+$/,"");
  },
  addClass: function(func) {
    this.entire = func;
    this.entire.init();
  },
  scriptLoader: function(script,callback) {
    $.ajax({
      url: script,
      dataType: "script",
      
      success: function(){
        callback();
      },
      
      error: function(xhr,message) {
        alert("script:"+message);
      }
    });
    return this;
  },
  /* trigger */
  getContent: function(content,urlchange) {
    if(!content || content == "") {
      content = this.config.index;
    }
    if(urlchange) {
      history.pushState(content,content,this.config.base+content);
    }
    if(this.loaded == this.unhash(content)) {
      this.scrollto();
      return this;
    }
    this.statusBusy();
    this.loaded = this.unhash(content);
    var par = this;
    this.content.slideUp("slow",function(){
      par.removeContent();
      par.getTemplate(par.setPage,content);
    });
    return this;
  },
  /* busy */
  statusBusy: function() {
    $("#loading").fadeIn("fast");
  },
  statusAllow: function() {
    $("#loading").fadeOut("fast");
  },
  statusMake: function() {
    var html='<div id="loading"><img src="/shared/images/icon_loading.gif"></div>';
    $("body").append(html);
    $("#loading").hide();
  },
  /* finish */
  finish: function() {
    var par = this;
    this.content.slideDown("fast",function(){
      par.scrollto()
    });
    this.statusAllow();
  },
  /* scripts */
  loadScript: function(content) {
    if(this.isScript(content)) {
      this.getScript(this.fireScript,content);
    }else {
      this.finish();
    }
    return this;
  },
  getScript: function(callback,content) {
    var par = this;
    $.ajax({
      url: par.config.scriptPath+content+".js",
      dataType: "script",
      
      success: function(){
        callback(content,par);
      },
      
      error: function(xhr,message) {
        par.setError("script",message);
      }
    });
    return this;
  },
  fireScript: function(content,par) {
    par.finish();
    return this;
  },
  /* pages */
  setPage: function(data,content,par) {
    par.content.html(data);
    par.loadScript(content);
  },
  setError: function(type,message) {
    this.content.html('<p class="errorMessage">'+type+" unloaded:"+message+'</p>');
    return this;
  },
  getTemplate: function(callback,content) {
    content = this.unhash(content);
    var par = this;
    $.ajax({
      type: "GET",
      url: par.config.api,
      timeout: 30000,
      async: true,
      dataType: "html",
      data: {'page':content,'X-PJAX':true},
      
      success: function(data){
        callback(data,content,par);
      },
      
      error: function(xhr,message) {
        par.setError("template",message);
      }
    });
    return this;
  },
  /* removing */
  removeContent: function() {
    this.content.html("");
    this.removeScript();
    return this;
  },
  removeScript: function() {
    if(this.isScript(this.content)) {
      this.entire.remove();
      this.entire = null;
    }
    return this;
  }
};

})();

読み込み先のJavascript

こうしておくと自動的に実行される

initが最初に実行される

ページ切替時に removeメソッドが発動する

SCC.addClass(new (function() {
this.init= function(){
  alert("hoge");
}
this.remove = function() {
  this.init = null;
}
}));
呼び出し方はこんな感じ

コンストラクタ呼ぶとinitが実行されてとりあえず色々埋めといてくれるので

あとはナビゲーションのクリックイベントに埋めたりする

title属性の値を拾います。(別にhrefでもいいんだけどね・・・)

$(function(){
  //contents controller
  SCC = new SlowbirdsContentsController();
  //global navigation
  $("#asyncNavigation").find("a").click(function(e){
    e.preventDefault();
    SCC.getContent($(this).attr("title"),true);
  });
});

06.09.2011

[][]FQLを発行してレスポンス返すクラス

一応利用頻度の高そうなlike系もセットにしておきました。個人的に利用するために作ったのであまり深く調べてない。

ちなみに ?query=hogehoge&callbac=fuga ってするとJSONPでfuga(〜XML〜);

という感じでXMLがそのまま引数JSONPで返してくれるのでJavascriptで使うにも充分です。XMLがそのまま渡されるので面倒くさい事この上ないですね。

facebookからFQLでいろいろ取得するクラス

<?php
CLASS FB_FQL {
  public function get_fql($from,$select,$where){
    $select_e = implode(",",$select);
    $count =0;
    $where_e = "";
    foreach($where as $key => $value){
      $where_e .= "{$key} = \"{$value}\" ";
      if($count>0 && count($where)-1 > $count) {
        $where_e .= "and ";
      }
      $count++;
    }
    $query = 'select '. $select_e .' from '. $from .' where ' . $where_e;
    $query_e = rawurlencode($query);
    return($query_e);
  }
  public function get_response($query) {
    $fql = 'https://api.facebook.com/method/fql.query?query='. $query;
    $ret = file_get_contents($fql);
    return $ret;
  }
  public function get_fql_like($url) {
    $select = array("like_count","share_count","total_count");
    $where  = array("url"=>$url);
    return $this->get_response($this->get_fql("link_stat",$select,$where));
  }
  public function get_likeCount($ret) {
    if(!$xml = simplexml_load_string($ret)) {
      die("error:FQL死んだ<br/>\n<pre>".$ret."</pre>");
    }
    $count = $xml->link_stat[0]->like_count;
    return $count;
  }
  public function get_shareCount($ret) {
    if(!$xml = simplexml_load_string($ret)) {
      die("error:FQL死んだ<br/>\n<pre>".$ret."</pre>");
    }
    $count = $xml->link_stat[0]->share_count;
    return $count;
  }
  public function get_totalCount($ret) {
    if(!$xml = simplexml_load_string($ret)) {
      die("error:FQL死んだ<br/>\n<pre>".$ret."</pre>");
    }
    $count = $xml->link_stat[0]->total_count;
    return $count;
  }
  public function get_url() {
    if(isset($_GET['u'])) {
      $url = $_GET['u'];
    }else {
      die("error:param u");
    }
    return $url;
  }
}
?>

利用例

<?php
require_once("CLASS_FB_FQL.php");
$fb_fql = new FB_FQL();

$countRet = $fb_fql->get_fql_like($fb_fql->get_url());

echo '<h1><a href="'.$fb_fql->get_url().'">'.$fb_fql->get_url().'</a> is</h1>';
//like
echo 'like = <strong>';
echo $fb_fql->get_likeCount($countRet);
echo '</strong> liked on Facebook(out of facebook.com only).<br />';

//share
echo 'share = <strong>';
echo $fb_fql->get_shareCount($countRet);
echo '</strong> shared on Facebook(out of facebook.com only).<br />';

//total
echo 'total = <strong>';
echo $fb_fql->get_totalCount($countRet);
echo '</strong> liked and shared on Facebook(out of facebook.com only).<br />';

?>

ということで、サンプルドーン

http://183.181.34.34/lab/facebook/fb_fql/?u=http://slowbirds.org

僕のサイトのかわいそうさがよくわかる感じに仕上がりましたー!