2018年 04月の投稿を表示しています

ふるさと納税を簡単に調べたまとめ

ふるさと納税って何なのか

いろいろなところでも書かれているのでざっくりまとめ。

総務省|ふるさと納税ポータルサイト

  • 原則として自己負担額の2,000円を除いた全額が控除の対象

  • ざっくりとは年収によって控除できる上限が決まっている

  • 自治体の選び方は自由

  • 5 団体以内、かつ確定申告が不要な人はワンストップ特例が利用できる(都度書類を提出するだけでいい)

  • ワンストップ特例では翌年の6月以降に支払う住民税の減額がされる

  • 確定申告をすると所得税,住民税から控除される

  • ワンストップにせよ確定申告にせよ、最低2000円の自己負担で、地域特有のお礼の品がもらえるところがある

    • その地域に納税する代わりに俺の税金ひいてくれよ
    • 地域からはうちにいれてくれてありがとうなあ活用するなあってやつ
  • いくらまでふるさと納税できるかをちゃんと計算しないと自己負担2000円から高くなる可能性がある

    • 2000 円で ○○ がもらえる!20 万納税します!!
      • 上にも書いたものと同様に年収 500 万で共働きだと 6 万くらいまでなので 20 万をふるさと納税しても 14 万くらいの自己負担になる
      • お礼の品を求めるなら、とりあえずやればいいってもんでもない
    • 実際それ 2000 円よりも高くつくよね、みたいなお礼を選ぶとオトク感
  • ところでそのお礼の品のお金どっから出てるんだ…?納税金の一部が使われる…?

    • 自治体のお金の出入りみたいなのを調べたらよさそうだけどそこまでの気持ちはなかった

どうやってふるさと納税先、お礼の品を探すか

いくつかの企業が EC サイトよろしく、一覧で眺められるようなものを作っている。

ふるさと納税サイト [ふるさとチョイス] | お礼の品掲載数No.1
ふるさと納税【さとふる】認知度・利用意向No.1ふるさと納税サイト
ふるさと納税とは? - ふるさと納税の「ふるさとぷらす」
ふるさと納税をするなら【ふるまる】 ふるさと納税サイト furumaru.jp
ANAのふるさと納税
JTBのふるさと納税ポータルサイト「ふるぽ」ポイントを特産品・旅行へ | ふるさと納税サイト [ふるぽ]
オススメのふるさと納税特集|郵便局のネットショップ
【楽天市場】ふるさと納税|納付先の自治体、寄附金の使い道が選べ、お礼の特産品や税金の控除もうれしい「ふるさと納税」

ANA とか郵便局もやってるんかってのはちょっと驚いた。

 

ちなみにこういうのもあった。

ふるさと納税管理システム LedgHOME(レジホーム)

 

自治体と企業が契約を結んで、うちのふるさと納税の宣伝や管理よろ!というのを企業が請け負っている感じっぽく、企業側はそこで利益が出ていくのかな。

 

総務省のサイトでも自治体別の一覧が見られるが、各自治体のふるさと納税のやり方ページにリンクされており、そこから進めようとすると、ふるさとチョイスだったり、さとふるだったり、契約先の管理されているページへとリンクされている。

総務省|ふるさと納税ポータルサイト|自治体をさがす

 

こういうものが欲しいんだよなあ(肉、魚、飲み物、…etc)とか、私のお金はこういう用途で使ってほしい!、といったものがあれば、ふるさとチョイス、さとふるを始めとする上で挙げたような各種サイトを眺める。

この自治体にお世話になったから応援したい!なら、総務省の一覧ページから探す。

という使い方がよさそう。

そのほか余談

1都3県、ふるさと納税で846億円減収:日本経済新聞

すごく最近の話題だけどこういう話もある。地方に出る分、都会の歳入が減ってしまって逆に困っちまうで!というもの。

 

ちなみに。例えばふるさとチョイスのランキング上位に来ている近江八幡市の決算を見ると、平成 28 年度でふるさと納税(寄附金)が 14 億あったっぽい。みんなやってるんだな、すごい。

平成28年度決算 | 近江八幡市

WatchDogs 2 のメインストーリーと DLC1-3 のストーリーをクリアした

これ起動時のロード画面?なんだぜ、かっこいいだろ。

前作についてのクリアしたよって話はこっち。
WatchDogs のメインストーリーをクリアしたよ | ごみばこいん Blog

公式サイトはここ。
Watch Dogs 2 - ウォッチドッグス2 | Ubisoft

前作から変わったところとか

舞台

  • 舞台はサンフランシスコ
    • サンフランシスコ~シリコンバレー、オークランド。大体そのあたり。
    • 縮尺は現実とあっていなくて(そりゃそうだ)いい感じにデフォルメされたような
    • 実在する地名も出てくる。アルカトラズとか。
  • 主人公が DedSec の一員になった
    • 主人公が属する SF の DedSec チームが主役
    • 1 人の戦いではなく、ストーリー全体を通してチームで戦っている感がある
  • ctOS のバージョンがあがって 2.0 になった
    • ctOS タワーがない
    • やっぱりスマホポチーでハックできる ctOS
  • 現実っぽさマシマシ
    • アプリを市民に入れてもらって、そのマシンパワーを使ってより高度なハックができるようになる
    • アプリでは真実を伝えることで、利用者(=フォロワー) を増やしていく
    • 3D プリンタで銃を作る
    • スマートホームとか、自動運転とか
    • nudle maps 超便利(nudle って明らかに Google オマージュでしょ)
    • ギャングとハッカー


ゲームシステム

  • "フォーカス"がなくなった
  • ガジェットが増えた
    • ドローンとジャンパー。便利すぎて 1 人でコソコソしなくていい
  • 装備スロットはスタンガン + 任意の武器 2 つ
  • 拠点(=ハッカースペース)ができた
  • ガンショップがなくなった
    • 拠点には 3D プリンターがあるので、最高にクールな銃を製造できる
  • ハッキングに回路パズルが絡むことが少なくなった
    • 要所要所で現れる
    • かつ、別画面に移行することなく、世界に回路パズルが見える
    • ガジェットやハックを駆使して解決する
  • 自撮りができる
    • 特定の目的の時に写真を使うことがある
  • 警察だけでなくギャングからも追いかけられることがある
    • 前作もあったっけ?

プレイ状況

攻略サイトなどの情報は一切なし。

最終的なプレイ時間は 33 時間でした。

メインオペレーションと、ほぼすべてのサイドオペレーション、 DLC オペレーションをクリアしました。

サイドオペレーションを完遂するという実績が解除されていなかったので取り逃したんだろうなあ。
でもサイドオペレーションがこれ以上は見当たらないのだけど、これはいったい…?
サイドオペレーションを優先してやっていたのだけど、お話が進むにつれてできなくなる、とかあったのかなあ…わからん…。

 

最終的なスキルはこんな感じ。

ドンパチ系スキルをほぼ取っていないんですが、これはプレイングがサイレント寄りだったのでーって感じですね。
前作に比べてドンパチやるか、フルサイレントでやるか、ほぼすべての場面で選択できたと思います。

 

収集品は最低限って感じで、スキルを取る上で必要なものを優先してーって感じですかね。

 

前作もそうだったんですけど、カーオンデマンド使わなくない…?
ちょっと走ったら道端に駐車しているか、道路を走ってくる車がいるので止めて乗っ取り~ってのが主な移動手段でした。

 

あとは自撮りが楽しいよってくらいなのでちょっとだけ紹介するね

これはイケイケ砂漠フェスの様子

これはシリコンバレーの大企業 nudle の社内の様子

これは警備会社で民間軍事会社のユーメナイズールーの装甲車。大切なものを運んでいるらしい。

これはサンフランシスコの光景。どこ…

W → M (いわゆる転職エントリ)

いわゆる退職エントリです W を退職しました、しばらくお休みして M に行きます。

W でやったこと

まる 4 年在籍して、おおきくまとめるとこの 3 つをやってました。

  • Web サービスの新機能開発、バグ修正、トラブル対応
  • ボヤ騒ぎのやつを拾ったら炎上したので消火
  • 採用系(ちょっと)

良い言い方をすると、いずれも未経験なことばかりで、おもしろ楽しくやらさせてもらいました。
圧倒的成長チャンスに超感謝!!!!!!

W でよかったところ

  • 幅広いチャレンジができる&良い経験が多かった
  • 整ってないところが全部なので、こうしたらいいんじゃないっすかって整えるところもやる
  • やっていきがあればどこまでもできる

逆に言えばやっていきがないと辛いですね、ただこなすだけだと無理だと思います、いろいろ。
まあ、ベンチャー企業あるあるじゃないっすか、やりがい搾取っていうと非常に悪いですが、ぼくは楽しかったと思います。
とはいえ、なんだかんだ 1-2 年くらいはよくわかんないが続いて、ちともったいなかったかなあと思いますが。

W でダメなところ

  • せっかくやっていきが高い人がいるのに、それをうまくコントロールできない
  • 勝手にやったらいいんじゃない俺は知らんけどってスタイルの人がいる、全般的に良くも悪くもいわゆる老害基質
  • 「人が良い」ってなんだろうね
  • よくわからんところがチラホラある

この中の大半は昔の話ですね。

一番よくわからなかったエピソードを挙げると「イチローも最初は給料が低かったんだから」で話が終わった会です。自分の中でクソ殿堂入りです。

イチローさんの年俸の正しさはわかりませんが、いくつか調べると、早々にして倍になったり桁が変わったりしたようです。
たとえば: イチローの年俸と成績推移を調査!引退時期の考察と子供が気になる!スーパープレー動画も!

イチローさんと比べて〜とかって話ではなくて、ちょっと調べたらわかりそうなことを適当に理由付けしてお話おわりって非常にアレ。
ぼくの場合は頭の速度が出ないので、その場は「確かになるほどなあ」って思ったのですが、あとでなんか違うくね?って思い調べたものの
ハイ!!!話はおわり!!!って調子だったのでもういいやって。

転職活動

気づいた方もいると思うのですが、このごみばこいんのサイトは 2017 年の 8 月ごろから更新頻度が上がっています。そういうことですね。
それとともに、自分がやってきたことをマークダウンでまとめて marked で html にして web にのっけて basic 認証を設定して、先方に送っていました。手書きつらいでしょ。
その中に盛り込むコンテンツは転職ドラフトの登録できる内容を参考にしていました。

あとは、エージェントは一度話を聞きましたが、利用していないです。
エージェント使うのもなんだかなあと思ったので(エージェントに紹介してもらう企業って自分で行こうと思えば選考を受けることもできるはず)
知人つてで、飯いこうず~~~~みたいな感じでやってました。その間 W には有給だったり半休だったりしてます。

最後のはなし

最後にひとこと終わりの挨拶おねがいします、みたいなのがあるんですが
とくに喋ることもないので、ここ最近ふわ~~って考えているところからひねり出したことを喋ってました。
ざっくりとはこうですね。

  • 正解は自分の手で作る、他人に作られるものではない
  • 自分の力で変えられるもの、変えられないものがある
  • 辛い記憶が多いけど面白かったよね
  • クソお世話になったし、クソお世話した(超重要)

これからのこと

M に入るまでしばらく間があるので、それまでにやりたかったけどできなかったことをどんどんやっていきたいなーーって思います。
エンジニアリングするとか、旅行するとか、あのゲームをクリアするとか、とかとか。

M のほうは色々が色々みたいで、ワクワク感すごくてとにかく楽しみでしかないって感じですね。
ガシガシやっていく気持ちがマッハ。

ちなみに明日から、来週も、出社しなくていい!!!!!って思ったらだいぶ気持ちが楽になった件も共有しておきます。

干し芋

お気持ちのほど、よろしくおねがいします。

http://amzn.asia/314K1cw

Google App Script を使って Bitbucket Pipelines の動いている状況を見える化した

某所で Bitbucket Cloud を使っていて、そのうえで動く CI 、 Bitbucket Pipelines を利用しています。

Bitbucket Pipelines | Atlassian

CI 時間が月 500 分を超えるまでは無料で利用できるのですが(すごい)、あっちこっちのリポジトリで使い始めたり、プッシュ頻度の高いリポジトリで使ったり
あるいは コード量が多くて CI の時間がかかるようなリポジトリ、そんなところで使い始めると途端に課金額がマッハになってしまいます
(ブランチを絞る、手動だけなどしてもいいけどそういうことじゃない)

いきなり XXXXX ドルの請求です!なんて届くと超ビビってしまうので、
毎月どれくらいの請求額になりそうか、過去はどれくらいだったのかを記録して通知するものを スプレッドシート + GAS で作ってみました。

なぜスプレッドシートと GAS なのか

同某所では GSuite を利用しているので、 Google Drive を組織的に利用できます。
GAS を使うことで、定期的にプログラムを実行することができるうえ、他の外部の何か(例えばサーバなど)について管理することが不要になります。
管理コストが少なく、かつ、スプレッドシートを基軸として、今後の分析にも使えるのでは?と考えると、スプレッドシート最高だなぁという結論になりました。

何も管理する必要なくオンラインにエクセルがいて、マクロの自動実行が出来るよって考えたらやばいと思うんだけど。

Google スプレッドシート - オンラインでスプレッドシートを作成、編集できる無料サービス

Apps Script | Google Developers
Spreadsheet Service | Apps Script | Google Developers

どのように Pipelines の利用状況を集めるか

Pipelines 、というか Bitbucket にはプログラム的に操作やデータ取得を行えるよう API が提供されています。
この中に Pipelines に関するものがあるので、これを利用します。

Bitbucket API

リポジトリ一覧を取得 → 各リポジトリに対して Pipelines の利用状況を確認 というながれで情報を集めます。

リポジトリ一覧 API : Bitbucket API
Pipelines状況 API : Bitbucket API

GAS から API を利用してデータを集める

というわけで出来上がったものがこちらです。

var BitbucketApi = {};

BitbucketApi.getAuth = function () {
  var options = {};
  options.headers = {};
  options.headers.Authorization = "Basic " + "ここに base64(ID:PW) したものを入れる";
  
  return options;
};

BitbucketApi.fetch = function (api) {
  var url = "https://api.bitbucket.org" + api;
  try {
    var rawResponse = UrlFetchApp.fetch(url, BitbucketApi.getAuth());
  } catch (ex) {
    Logger.log(ex);
    return null;
  }
  return JSON.parse(rawResponse.getContentText());
};


var Util = {};

Util.each = function(ary, cb) {
  for (var i=0, m=ary.length; i<m; i++) {
    if(cb(ary[i], i) === false) break;
  }
};

Util.getSheetByName = function (name) {
  if (Util.SpreadSheet == null) {
    Util.SpreadSheet = SpreadsheetApp.getActive();
  }
  
  return Util.SpreadSheet.getSheetByName(name);
};

Util.vlookup = function (searchValue, targetValues, column) {
  var result = null;
  Util.each(targetValues, function(row) {
    if (row[0] === searchValue) {
      result = row[column];
      return false;
    }
  });
  
  return result;
};

Util.yesterDay = function(date) {
  date = date || new Date();
  date.setDate(date.getDate() - 1);
  return date;
};

Util.startDay = function(date) {
  date = date || new Date();
  var d = new Date(date);
  d.setHours(0);
  d.setMinutes(0);
  d.setSeconds(0);
  return d;
};

Util.endDay = function(date) {
  date = date || new Date();
  var d = new Date(date);
  d.setHours(23);
  d.setMinutes(59);
  d.setSeconds(59);
  return d;
};


// reposシート、リポジトリチェック済みフラグのカラム番号
var REPOSITORY_CHECK_COLUMN = 4;


// リポジトリのチェックフラグの初期化
function initPipelinesDuration() {
  var repoSheet = Util.getSheetByName("repos");
  var repoRange = repoSheet.getRange(2, REPOSITORY_CHECK_COLUMN, repoSheet.getMaxRows() - 1, 1);
  repoRange.setValue(0);
}


// pipelinesシートの初期化
function resetPipelinesDuration() {
  Logger.log('WARNING!! Reset record durations!!  exit script.');
  return; // warning, danger.
  
  var sheet = Util.getSheetByName("pipelines");
  if (sheet.getMaxRows() > 1) {
    sheet.deleteRows(2, sheet.getMaxRows() - 1);
  }
  sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns()).setValue("");
  sheet.getRange(1, 1, 1, 4).setValues([["repo", "build_number", "build_seconds", "completed"]]);
}


// リポジトリ一覧の同期
function syncRepositories() {
  // reposシートをまっさらにする
  var sheet = Util.getSheetByName("repos");
  if (sheet.getMaxRows() > 1) {
    sheet.deleteRows(2, sheet.getMaxRows() - 1);
  }
  sheet.getRange(1, 1, 1, 4).setValues([["repo", "url", "project", "status"]]);

  // BitbucketAPIを使ってリポジトリ名一覧をシートに移す
  var page = 1;
  while (true) {
    var repositories = BitbucketApi.fetch("/2.0/repositories/チーム名/?sort=-created_on&pagelen=100&page=" + page);
    if (repositories == null || repositories.values == null || repositories.values.length == 0) {
      break;
    }
    Util.each(repositories.values, function(repo) {
      sheet.appendRow([
        repo.slug,
        repo.links.html.href,
        repo.project.name
      ]);
    });
        
    page++;
  }  
}

// pipelinesシートの更新
function syncPipelinesDuration() {
  var sheet =  Util.getSheetByName("pipelines");
  var pipelineSheetRows = sheet.getLastRow();
  var todayStart = Util.startDay(Util.yesterDay());
  var todayEnd = Util.endDay(Util.yesterDay());
  
  var repoSheet = Util.getSheetByName("repos");
  var repos = repoSheet.getSheetValues(2, 1, repoSheet.getMaxRows(), 3);
  
  // 既にシート上にそのビルドの記録があるかチェック
  var isExistsBuild = function(repo, num) {
    var find = false;
    Util.each(sheet.getSheetValues(2, 1, sheet.getLastRow(), 2), function(row) {
      if (row[0] + row[1] === repo + num) {
        find = true;
        return false;
      }
    });
    
    return find;
  };
  
  Util.each(repos, function(repo, rowIndex) {
    if (repo[0] == "" || repo[2] == "1") {
      Logger.log(repo[0] + " : skip");
      return;
    }
    Logger.log(repo[0] + " : sync");
      
    repoSheet.getRange(rowIndex + 1, REPOSITORY_CHECK_COLUMN).setValue(1);
    
    var page = 1;
    while(true) {
      Logger.log(repo[0] + " : sync : page " + page);
      var response = BitbucketApi.fetch("/2.0/repositories/チーム名/" + repo[0] + "/pipelines/?sort=-created_on&pagelen=100&page=" + page);
      if (response == null || response.values.length == 0) {
        break;
      }
      var breakFlag = false;
      
      Util.each(response.values, function(item) {
        // 完了していないビルドはスキップ
        if (item.completed_on == undefined) {
          return;
        }
        var formattedCompletedOn = item.completed_on.replace(/T.+Z/, "");
        var completedTime = new Date(formattedCompletedOn);
        if (completedTime.getTime() > todayEnd.getTime()) {
          return;
        }
        if (completedTime.getTime() < todayStart.getTime()) {
          breakFlag = true;
          return false;
        }
        
        if (isExistsBuild(item.repository.name, item.build_number)) {
          return;
        }
        
        sheet.appendRow([
          item.repository.name,
          item.build_number,
          item.build_seconds_used,
          completedTime.toLocaleDateString(),
          '=MONTH(D' + pipelineSheetRows + ')=month(now())',
        ]);
        pipelineSheetRows++;;
      });
      
      if (breakFlag) {
        break;
      }
      
      page++;
    }
  });
};

認証周りだけちょっとアレなので、なんとかしたいなあとか思いつつも直打ち。

実装としては次の 2 つの処理に分かれます。

  • リポジトリ一覧の同期

    • 1 日 1 回 午前中 に実行
  • Pipelines 状況の取得

    • 1 日 1 回 お昼ごろ に実行
    • 1 日 1 回 おやつごろ に実行
    • 1 日 1 回 夕方ごろ に実行

リアルタイムに動かすことが難しいので、一日一回動かして、昨日分のデータを取得するようにしています。
Pipelies は 1 度に取りきることが難しいので、何回か動かします。進捗はシートに記録されるので、再度取得することはありません。

あとは、これだけだとシートがなくて動かないので、空っぽのシートを用意したら使えるはず。

  • pipelines
  • repos

収集してからのこと、集計と通知

実行するとこんな感じに Pipelines のデータが収集できるので、ここからスプレッドシートの機能で集計していきます。

ピボットテーブルを使って、当月の、各リポジトリの、実行時間(秒)を合計します。

こんな具合で設定しています。

ここから更に、少々強引な関数を組んで、このデータを参照して日毎の実行時間(分)を算出し、月単位でどれくらいの実行時間になりそうかをざっくり予測できます。

ちなみに使った関数はこんなものです。

データの参照 ( B1 )
=index(aggregate!A1:AA20)


ビルド数の抽出 ( C24 ~ xx26 )
=COUNTIF(pipelines!$D:$D,C1)


総計の抽出行 ( C25 ~ xx26 )
=if(C1="", 0/0, if(C1<>"総計", INDEX(C1:C20, LOOKUP("総計",$B1:$B20,$A$1:$A$20), 1), 0/0))


総計の分 ( C26 ~ xx26 )
=iferror(C25/60, "")


今月予測(ビルド分) ( C27 )
=average(C26:AB26)*22


今月予測(課金額) ( C28 )
=round(((C27-500)/1000)/10*10)*10

で、このときの C28 を UrlFetch で Slack に送るよう、トリガーで日々実行しています。

var SlackApi = {};
SlackApi.config = {};
SlackApi.config.webhook = "";
SlackApi.config.testMode = false;

SlackApi.sendMessage = function (text) {
  var payload = {
    text: text
  };
  
  var options = {
    method: 'post',
    payload: JSON.stringify(payload),
  };
  
  if (SlackApi.config.testMode) {
    Logger.log(SlackApi.config.webhook);
    Logger.log(options);
  } else {
    return UrlFetchApp.fetch(SlackApi.config.webhook, options);
  }
};


function sendSlack() {
  SlackApi.config.webhook = "https://hooks.slack.com/services/....";

  var sheet = Util.getSheetByName("aggregate");
  var usages = sheet.getSheetValues(2, 1, sheet.getLastRow(), sheet.getLastColumn());

  var resultMessage = [];
  resultMessage.push("> *Pipelines利用状況* (" + (new Date()) + ")");
  resultMessage.push("```");
  Util.each(usages, function(usage) {
    if (usage[0] == "") return;
    resultMessage.push(usage[0] + ": " + usage[sheet.getLastColumn() - 1] + "sec");
  });
  resultMessage.push("");
  
  try {
    var paceSheet = Util.getSheetByName("課金ペースをみたい");
    var paces = paceSheet.getSheetValues(27, 2, 2, 2);
    resultMessage.push(paces[0][0] + ": " + paces[0][1]);
    resultMessage.push(paces[1][0] + ": " + paces[1][1]);
    resultMessage.push("");
  } catch (ex) {
    Logger.log(ex);
  }

  resultMessage.push("詳細はここ> https://docs.google.com/spreadsheets/....");
  resultMessage.push("```");
  
  SlackApi.sendMessage(resultMessage.join("\n"));
}

これでいきなり多額の請求が来ることに怯えることが少なくなりました。遅くても 1 日で oops! なことに気づけるはずです。

モダンな xhprof = tideways

PHP で書かれた、パフォーマンスがクソほど悪い web サイトの様子を見ることになったので、どこに時間使っとんねん!を調べようと思い xhprof 入れるぞ!!と準備しようとしたら PHP7 以降はサポートしないらしい。
FB 社が HHVM に移行したから扱わなくなったのかな?わからん。

調べたら PHP7 対応の更新を独自に追加した xhprof と、 tideways という xhprof 互換のプロファイリングツールがあるらしいので tideways を使ってみる話。

XHProf fork with PHP 7.0, 7.1 and 7.2 support | Tideways

tideways エクステンションの準備

ここからエクステンションがダウンロードできる。

tideways/php-xhprof-extension: Modern XHProf compatible PHP Profiler for PHP 7

ちなみにここでタイトル回収なんですけど、こう説明書きされている。

Modern XHProf compatible PHP Profiler for PHP 7 https://tideways.io

 

Windowsの場合はAppVeyorのアーティファクトからになる。Github からもリンクがあるけど、ここ。

php-profiler-extension master.251 - AppVeyor

ここから自分の PHP バージョンを選んで、CI 結果の中から Artifacts があるので、そこに dll が入っている zip がある。

 

あとは環境を問わず、エクステンションを既存の別のエクステンションと同じディレクトリに入れて php.ini で extension=... を記載を追加して読み込めば良い。

こいつは Windows でやってる。

php -m すると今読み込んでいるエクステンションが確認できるので、このときエラーが出ずに tideways の記載が見えれば準備 OK

注意事項として tideways のツール群を使わない(xhprof相当のことだけローカルでやりたい。tidewaysのインフラを使わない)なら
公式サイトでアナウンスされている yum や apt-get を使ったインストールはしないこと。
そっちだとうまくできないっぽい。

Mission control center for PHP application performance | Tideways

ただ tideways のツール群にのっかると、プロファイリングやアラート通知が出来るよ!と書いてあって非常に便利そう。
ツール群を入れたら自動でプロファイリングして集計して可視化までしてくれる。超便利やん…

tideways の xhprof 互換エクステンションを使ってプロファイリングの実施

Github にも書いてあるが、ざっくりとはこういう形。

tideways_xhprof_enable();

start_my_application();

$data = tideways_xhprof_disable();
file_put_contents(__DIR__ . "/1.xhprof", serialize($data));

これで記載したファイルと同じディレクトリに 1.xhprof というファイルが出来上がる。

Laravel や CakePHP なら一番最初に呼ばれる index.php をまるっと囲ってしまうと何も考えずに「どこのページが重たいかな~~フッフ~ン」と楽できる。
楽できる一方で、全ての処理について出てしまうので「特定のページが重いんだよなあ~~」みたいなときには使いにくい。
そのときは個別にやっていくと良さげ。

プロファイリング結果の確認

出力された結果は xhprof と互換性があるので、xhprof を可視化するツールでそのまま見れる。
例えば xhgui がいろいろできて便利そうに見える。

perftools/xhgui: A graphical interface for XHProf data built on MongoDB

いろいろ必要なのがあるなあとか、変なところで詰まるのもなあ、と思い、今回はとりあえず結果を見たかっただけなので xhprof に付属する xhprof_html を使った。
xhgui の話はまた今度にする。

 

ただ、xhprof_html は xhprof の設定が前提となるので、それそのままだと動かない(主にディレクトリ周り)ので、そのあたりを少しいじった。こちらを使いたい方はどうぞ。

sters/xhprof-html: xhprof (or tideways) visualize html tool from xhprof repo.

これを使うとこんな感じの Web ページが見えるようになるので、これでおしまい。

この画面から、どのメソッドが何回呼ばれてどれくらい時間がかかっているかという、PHP アプリケーションのボトルネックが丸見えになってくるので、パフォーマンスチューニングドンドンやっていける。

余談) PHP7 対応された xhprof

これは Travis でぐるぐる CI も回してる。 PHP 7.0 と 7.1 で CI されてるので、そこは確実に使えるっぽい。

yaoguais/phpng-xhprof: upgrade xhprof extension to PHP7

使い方は README にも書いてあるとおりで xhprof とまったく同じでいい。エクステンション入れて xhprof_enable() したら良い。
結果は xhprof_disable() すると受け取れるので、それを serialize() して保存すると各種ツールで見れる。