※ パクレゼルヴではWeb開発エンジニアを大募集中!詳細はこちら

Archive

Archive for the ‘システム’ Category

Google Analytics を携帯から使う

2009/4/28 火曜日 21:49:21

こんばんは。
のびーにょです。

GoogleAnalyticsをケータイから使う

ためのクラス書きました。
探してもfunctionでしかなかったので。
あと、fopen()のタイプしかなかったので。
fopenだと繋がらなかった場合エラー出るんですよね。
なのでfsockopen()でソケット通信を利用。
繋がらなかったらそこで終了。
もしくはimgタグにして画像を読み込ませる感じでも利用できます。
参考にさせていただいたソースはfopenでPHP5で追加されたパラメータ使っています。
別に弊社もPHP5なんでかまわないんですけど、どうせなら4でも動くようにという感じにしておきました。
後色々機能追加しています。

使用サンプル

//クラス読み込み
include_once 'GoogleAnalytics.class.inc';
include_once 'googleAnalytics.conf.inc';
//Google AnalyticsのID
$GAM_id = GOOGLE_ANALYTICS_ID;
//サーバ名(ドメイン)
$domain = $_SERVER['SERVER_NAME'];
//セッションID 携帯では独自に引き継がないとできないので注意
$sess = session_id();
//ユーザ側で通知したい値
$md5_subid = md5('ユーザID等');
//GAMセット
$GAM = new GoogleAnalyticsMobile($GAM_id,$domain,$sess);
$res=$GAM->sendGoogle($md5_subid);
//IMGタグを利用する場合は以下も必要
//echo $res;

以上
googleAnalytics.conf.incには定数の定義があります。
内容は以下

設定例

//GoogleAnalyticsのID
define('GOOGLE_ANALYTICS_ID','UA-XXXXXXX-X');
//imgタグで画像を読ませるかどうか
define('GOOGLE_ANALYTICS_READIMG',FALSE);
//define('GOOGLE_ANALYTICS_READIMG',TRUE);
//ソケット通信で通信する場合のタイムアウトするまでの時間
define('GOOGLE_ANALYTICS_TIMEOUT',5);
//通信ポート
define('GOOGLE_ANALYTICS_SEND_PORT',80);
//GoogleAnalyticsへ通知するURL(変わったら変えてね)
define('GOOGLE_ANALYTICS_HOST','www.google-analytics.com');

設定はお好みでどうぞ。
GoogleAnalytics.class.incの中身は以下です。

GoogleAnalyticsMobile

/**
 * GoogleAnalytics を携帯でも利用するためのクラス
 * @auther のびーにょ
 */
class GoogleAnalyticsMobile {
  var $utmac;   //Google AnalyticsのID
  var $utmhn;   //ドメイン
  var $session;  //クッキー(セッション)
  var $random;  //ランダムな値
  var $utmn;    //ランダムな値(上のパラメータとは別の値が必要)
  var $today;   //日付
  var $header;  //送信するヘッダ
  var $res;      //送信後に取得した内容(おそらく必要なし)
  /**
   * CONSTRACTER
   * @param utmac GoogleAnalyticsのID
   * @param utmhn 測定するドメイン
   * @param session セッションID(一意に識別できるものなら何でも可)
   */
  function GoogleAnalyticsMobile($utmac,$utmhn,$session)
  {
    $this->utmac = $utmac;
    $this->utmhn = $utmhn;
    $this->session= $session;
    //intの最大値を超える数字を指定した乱数(float型になるのかな?)
    $this->utmn = rand(1000000000, 9999999999);
    //intの最大値までの乱数
    $this->random= rand(1000000000,2147483647);
    $this->set('today',time());
    $this->set('header');
  }
  /**
   * データ作成とデータ送信
   * @param uservar ユーザー識別子(カスタマID)
   * @param referer リファラ
   * @param utmp リクエストURI
   * @param utmdt ページタイトル
   * @param utmsr ディスプレイサイズ(横*縦)
   * @param utmfl Flashのバージョン
   * @param utmsc 色
   * @param utmcs 文字コード
   * @return IMGタグ OR なし
   */
  function sendGoogle($uservar=NULL
                      ,$referer=NULL
                      ,$utmp=NULL
                      ,$utmdt='-'
                      ,$utmsr='-'
                      ,$utmfl='-'
                      ,$utmsc='-'
                      ,$utmcs='UTF-8')
  {
    if(!$referer){
      $referer = $this->getReferer();
    }
    if(!$utmp){
      $utmp = $this->getRequestUri();
    }
    $today = $this->get('today');
    $host = GOOGLE_ANALYTICS_HOST;
    $sendPrm ='/__utm.gif?';
    $sendPrm.='utmwv=1'
      .'&utmn='.$this->utmn
      .'&utmhn='.$this->utmhn
      .'&utmcs='.$utmcs
      .'&utmsr='.$utmsr
      .'&utmsc='.$utmsc
      .'&utmul=ja'
      .'&utmje=0'
      .'&utmfl='.$utmfl
      .'&utmdt='.urlencode($utmdt)
      .'&utmhid=-'
      .'&utmr='.urlencode($referer)
      .'&utmp='.urlencode($utmp)
      .'&utmac='.$this->utmac
      .'&utmcc='
        .'__utma%3D'.$this->session.'.'
    .$this->random.'.'
    .$today.'.'
    .$today.'.'
    .$today.'.2%3B'
        .'%2B__utmb%3D'.$this->session.'%3B'
        .'%2B__utmc%3D'.$this->session.'%3B'
        .'%2B__utmz%3D'.$this->session.'.'
          .$today.'.2.2.'
          .'utmccn%3D(direct)%7Cutmcsr%3D(direct)'
            .'%7Cutmcmd%3D(none)%3B'
        .'%2B__utmv%3D'.$this->session.'.'
    .$uservar.'%3B';
    //IMGタグで設置する場合
    if(GOOGLE_ANALYTICS_READIMG){
      //EZWebの端末のキャッシュ対策に末尾にtimestampを付与(ランダム数字とクッキーがあるので不要かも)
      return '<img src = "http://' . $host.$sendPrm.'&'.$today.'" width="0" height="0"/>';
    }else{
      $header = $this->get('header');
      if(!$header){
        $header = $this->makeHeader();
      }
      $this->sendData($host,$sendPrm,$header);
      return ;
    }
  }
  /**
   * データ送信
   * @param host ホスト名 (example.com)
   * @param sendPrm リクエストパラメータ (/index.php?aaa=bbb)
   * @param header HTTPヘッダ(クエリ、Host、Content-Type、Content-Lengthは自動的に付与される)
   */
  function sendData($host,$sendPrm,$header)
  {
    $header = "GET ".str_replace(array("r","n"),"",$sendPrm)." HTTP/1.1\\r\\n".$header;
    $header .= "Host: ".$host."\\r\\n";
    $header .= "Connection: Keep-Alive\\r\\n\\r\\n";
    $errno='';
    $errstr='';
    //WEBサーバへ接続
    $fp = fsockopen($host
                    ,GOOGLE_ANALYTICS_SEND_PORT
                    ,$errno
                    ,$errstr
                    ,GOOGLE_ANALYTICS_TIMEOUT);
    //接続に失敗した時の処理
    if (!$fp){
      return FALSE;
    }
    //要求データ送信
    fputs($fp,$header."GET ".$sendPrm." HTTP/1.1\\r\\n");
    //応答データ受信
    $response = "";
    while (!feof($fp)){
      $response .= fgets($fp,4096);
    }
    $this->set('res',$response);
    //接続を終了
    fclose($fp);
  }
  /**
   * ヘッダ作成
   * @param lang 文字種別
   * @param ua ユーザエイジェント
   * @return header
   */
  function makeHeader($lang=null,$ua=null)
  {
    if($lang){
      $header = 'Accept-language: '.$lang."\\r\\n";
    }elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && $_SERVER['HTTP_ACCEPT_LANGUAGE']) {
      $header = 'Accept-language: '.$_SERVER['HTTP_ACCEPT_LANGUAGE']."\\r\\n";
    }else{
      $header = 'Accept-language: ja'."\\r\\n";
    }
    if($ua){
      $header .= 'User-Agent: '.$ua."\\r\\n";
    }elseif (isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT']) {
      $header .= 'User-Agent: '.$_SERVER['HTTP_USER_AGENT']."\\r\\n";
    }else{
      $header .= 'User-Agent: Unknown'."\\r\\n";
    }
    return $header;
  }
  /**
   * リファラーがあれば返す。
   * @return リファラー(あれば)
   */
  function getReferer()
  {
    if(isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER']){
      return $_SERVER['HTTP_REFERER']; //referer url
    }else{
      return '-';
    }
  }
  /**
   * REQUEST_URIがあれば返す
   * @param  delPrmArr 削除するパラメータ配列
   * @return リクエストURI(あれば)
   * <pre>
   * 値までわかっている場合は配列で指定
   * EX: パラメータprmで値がvalとパラメータがprm2で値が分からない場合
   * ?prm=val&prm2=xxxxx のリクエストURI
   *   => array('prm'->'val','prm2'=>'')
   * </pre>
   */
  function getRequestUri($delPrmArr = array())
  {
    if(isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] && ($_SERVER['REQUEST_URI'] != '/')){
      $backData = $_SERVER['REQUEST_URI'];
      $flg = FALSE;
      //削除する
      foreach($delPrmArr as $key => $val){
        if($val){
          $backData = str_replace($key.'='.$val,'',$backData);
        }else{
          $backData = preg_replace("/".$key."=[^&]*&/",'&',$backData);
        }
        $flg = TRUE;
      }
      if($flg){
        $backData=str_replace('&&','&',$backData);
        $backData=str_replace('?&','?',$backData);
      }
      return $backData;
    }else{
      return '';
    }
  }
  /**
   * 自由にアクセスできるGETTER/SETTER
   * @param arg アクセスしたいパラメータ名
   * @return パラメータが保持している値
   */
  function get($arg = null)
  {
    if(!$arg){
      return null;
    }
    if(isset($this->$arg)){
      return $this->$arg;
    }else{
      return null;
    }
  }
  /**
   * SETTER
   * @param arg アクセスしたいメンバ変数名
   * @param val 格納したい値
   */
  function set($arg=null,$val=null)
  {
    $this->$arg = $val;
  }
}

携帯電話利用する場合、素直にREQUEST_URI送ってしまうと、セッションIDも送信されてしまい、正確なページ単位の集計ができません。
さらに、DoCoMoの端末のみ”guid=on”のパラメータが付いているサイトなどもあるでしょう。
また、広告コードが付与されて送信されてくるパターンもあります。
そういう場合にはgetRequestUri()メソッドを利用してREQUEST_URIを改変してセットします。
また、sendGoogle()メソッドには上記見てもわかるように色々引数を追加できます。
それも追加してみましょう。
また、makeHeader()メソッドでHTTP_USER_AGENTやHTTP_ACCEPT_LANGUAGEも変更できます。
しない場合はコールしなくても、勝手にコールされます。

以下サンプル

//クラス読み込み
include_once 'GoogleAnalytics.class.inc';
include_once 'googleAnalytics.conf.inc';
//Google AnalyticsのID
$GAM_id = GOOGLE_ANALYTICS_ID;
//サーバ名(ドメイン)
$domain = $_SERVER['SERVER_NAME'];
//セッションID 携帯では独自に引き継がないとできないので注意
$sess = session_id();
//ユーザ側で通知したい値
$md5_subid = md5('ユーザID等');

//GAMセット
$GAM = new GoogleAnalyticsMobile($GAM_id,$domain,$sess);
//リファラ取得
$ref = $GAM->getReferer();
//REQUEST_URI取得(guid,ses_id,ad_idを削除)
$reqi = $GAM->getRequestUri(array('guid'=>'ON'
                                 ,'ses_id'=>$ses_id
                                 ,'ad_id'=>''));
//ページタイトルを設定
$title = 'タイトル';
//画面幅(横*縦)
$bSize = '240*320';
//Flashのバージョン
$flash = '-';
//表示可能色数
$color=$LIB_ARR_MP['color'];
//送信データ生成&送信
$res=$GAM->sendGoogle($md5_subid
                        ,$ref
                        ,$reqi
                        ,$title
                        ,$bSize
                        ,$flash
                        ,$color);
//IMGタグを利用する場合は以下も必要
//echo $res;

これでOK
サイトの共通ファイルや、コントローラ、もしくは共通ヘッダ、共通フッタ等にセットすれば、GoogleAnalyticsがケータイでも利用できます。
ただ、注意点としては画像読み込みで利用される場合(GOOGLE_ANALYTICS_READIMG定数がTRUE)、
画像をフッターに表示させようとすると、ページ全体を読み込むまで通知されない可能性があるので、
極力ヘッダに記述した方がいいと思います。

ですが、HTMLが開始される前に表示するのは厳禁ですよ!

ただ、こちらも難点としてはDoCoMoの端末は画像が表示されるまで次の行を表示しようとしないのです。(データは取得している模様)
あまりにGoogleへの通知に時間がかかる場合等はユーザが戻る操作等を行う可能性があります。

ソケットで通知する際は定数にて接続を切る時間が設定できるので(GOOGLE_ANALYTICS_TIMEOUT定数)、ユーザーがせっかちな場合等はここを1秒等に設定すればましかもしれません。

のびーにょ PHP, システム, 携帯電話

キーパーソン

2008/10/31 金曜日 20:59:20

お疲れ様です

のびーにょです。

プログラムってちょっと書けるようになると結構いろいろ出来るような気になりますよね。
でも、既存のソースの修正であったり追加であったりなんかだと、追加の方がはるかに楽だったりしますよね。
修正の場合は何処がどのようになっているかをある程度把握しなければなりません。
追加でもそうですけど、データ追う量が全然違いますよね。

と、いうかですね。
修正する場合はその周りのソースを8割ぐらい理解した方がいいと思う訳ですよ。
そうすれば次の修正や追加の時に作業が軽減できますし、そのソース(システム)に精通した人になれます。

ま、何しててもそうだと思いますけど、いかに自分のポジションを良い状態に持っていけるかってのは会社、社会で働いて生活していく上では結構重要なことだと思います。

別に出世がどうとかじゃなくて、少しでも働きやすい、生活しやすい、”楽しい”状態に持って行くにはそういう重要な人(キーパーソン)になるのがお勧めです。

で、プログラマなら重要なシステムに精通している、管轄しているって言うだけでも結構そういう位置に行きやすいですよね。
たとえば、『あのシステムと●●社のシステム繋ぎこみして新たな事業展開をしたい』ってなったとするじゃないですか。
で、そのシステムに一番詳しい人に(別にリーダーじゃなくてもいいわけですよ)聞きに来たり、その人呼んで会議したりしますよね。
だから機能を限定して、この部分ならこの人が一番詳しい みたいな人になるのから始めてみるのがいいんじゃないかなぁと思います。
そのポジションで学べることもたくさんあります。

でも、さっさと全体を見た方がいいと思います。個人的にですけどね。
ポジションが人を育てる 
って言うのは間違ってないと思っていますので。

のびーにょ その他, システム

自社で使っているフレームワークの便利な機能

2008/9/30 火曜日 21:11:36

お疲れ様です。

のびーにょです。

表題の件
簡単に説明すると社内PCからのアクセス時とモバイル端末からのアクセス時で表示が変わります
え?普通ですって?

まぁ普通です
以下画像を見てください。
lab1.jpg
こんな感じでPCでデバッグできるようになっています。

最近はめっきり減りましたが昔はHTMLの表示部分も意識して作っていたので、フレームワークの記述ルールに従えば
一つのソースでHTMLとXHTMLと表示が変わっていました。(サンプルの画像のソースはXHTMLのみの記述)

モバイルからアクセスした場合はきちんとモバイルのみが出力されてるわけです。

んで、右側

こっちはデバッグ領域
直接画面に出力するわけじゃないので実運用中でもある程度のデバッグが可能です。

設定ファイルを書き換えればデバッグ内容をログに出力することもでき、携帯からアクセスしてもデバッグできます。

絵文字もフレームワーク規定の書き方をすれば当然3キャリアごとの出力です。

こんなの使ってても、結局最後は端末でデバッグしますが、開発中のデバッグはかなり楽なので結構いい感じです。

のびーにょ PHP, キャリア, システム, 携帯電話

新規開発と保守

2008/8/30 土曜日 0:12:53

ぞっす!
のびーにょです

タイトルについてです。

個人的には新規開発より保守の方が長くやっています。

あたりまえですね。

新規で2,3か月かけて作ったものをさらに改良して1年、2年と運用していくわけですから。

で、僕は初めて仕事したときも当然保守から入りました。

いろいろなサイトをやりました。

サービス開始から5年も6年もたっているサイトや、オープンしたてのサイト等など

担当(修正、保守)したサイトの数は20や30ではありません。(微々たる修正も含めて、ですけど・・・)

色々なサイトのソースを見て思ったのが、最初は
『これはないわ・・・』
って思いました。

今は少し変わって、
『何でこんな状態になるんだろう?』
です。

そもそも想定していたものとは違う形でサイトが発展していくと、初期の設計から逸脱した物になっていくものです。
それは往々にしてありえることなので問題ない(しかたない)事だと思います。
昔はこのあたりを見て『これはないわ・・・』って思ってたわけです。

何かしら理由があってそうせざるを得なかった場合というのは、長く運営していればいるほどありえる話です。

そして今思うこと
『何でこんな状態になるんだろう?』
です。

そもそも想定していた物と違ったり、システムの根幹にかかわるような変更が入った段階で作り直すべきだと僕は思います。

WEBの世界は流れが非常に速いと思いますし、流行ったり廃れたりするのも非常に頻繁に起こることだと思います。

ならば短い期間で(1年なら1年、2年なら2年)でフルリニューアルを行うのも一つの手かな、と思います。

設計が古臭かったり、新しい技術を使えばもっと色々なことができたり、楽にできたり、代替案があったり・・・
色々あると思います。
エンジニアとしては新しい技術等は色々試してみたかったりするものですしね。

ただ、長く使っていればそれだけソースは枯れてきますし、ノウハウもたまってきます。(バッドノウハウがほとんどでしょうが・・・)

初期のころの想定通りにサイトが発展していけば、逆にリニューアルする必要はないのかも知れません。(デザイン的な物は別として)

ただ、いちエンジニアとしての意見だと、『自分で設計してみたい。』と誰もが思うでしょう。

そして、そのチャンスに巡り合えた時、『保守しやすいように作ろう』と思う人が多いと思います。
(特に保守をやってから新規開発を初めてやる場合)

ただ、与えられた環境や状況により、それは叶わないこともあるでしょう。

でも、なるべくがんばってください。

あなたが2,3ヵ月で作り上げた物を、誰かは1年、2年と保守をするのです。

あなたのかけた時間以上を誰かはそのサイト(システム)に費やすのです。

使いやすい。
わかりやすい。
親切な設計だ。

そう言ってもらえるようなシステムを僕は組んでいきたいと思います。

ま、どう組んだってわかりやすさや使いやすさは人それぞれなんですけどね。

のびーにょ システム