孤独プログラマー譚

孤独死が近い。

Windows+PHP+Vdebug ブレイクポイントで止まらない

Vimを使う者として、IDE統合開発環境)に負けたくないという気持ちはある。

だが、さすがにログや画面にvar_dump()し続けることに疲れてきてしまった。

ということで、vdebug(ステップ実行できるVimプラグイン)を使うことにした。

環境:
Windows10
Docker
PHP7
Xdebug
Gvim(Kaoriya)

・vdebugの設定
let g:vdebug_options['path_maps'] = {"/var/www/html": "C:/Users/me/my_project/html"}

バックスラッシュで「C:\Users\me\my_project\html」と指定すると、動かない。
「C:/Users/me/my_project/html」通常スラッシュにすると、動く。

しかし、なぜかブレイクポイントで止まらない。ブレイクポイントを設定しても無視される。なぜ?

PHPStormだと、正常にブレイクポイントで止まる。でもvdebugだと止まらない。このままだと…IDEに負けてしまう!?

Xdebugのログを比べてみた。

(一部略)
・PHPStorm
breakpoint_set -f file:///var/www/html/index.php


・vdebug
breakpoint_set -f "file:///C:\Users\me\my_project\html\index.php"

ファイルの指定部分がローカルになっている…?

仕方なく、プラグインのソースを追ってみる。
うっ、Pythonで書かれてる…勉強せねば…。

・変数
remote : file:///var/www/html
local : C:/Users/me/my_project/html
filename : C:\Users\me\my_project\html\index.php


filename = filename.replace(local, remote)

どうも、/ と \ の違いが原因で、置換が上手くいってないっぽい。

/vdebug/python3/vdebug/util.py


def _create_remote(f):
 ret = f
 ret = ret.replace('\\', '/') ← 追加

これで、正常に置換できて、ブレイクポイントでも止まるようになった。

今日一日が無駄にならず済みました。

これからは「Vimでもステップ実行できる」と胸張って言えるぞ~。

jQueryでMVVM

案件によっては、自由にJSフレームワークを使えないことがあるかもしれない。
既存システムがjQueryだけで作られているので、それを踏襲する必要があるかもしれない。

そんな時、jQueryでMVVM…双方向データバインディングが出来ると、作業が楽になる。

以下のページで、超便利なラッパークラスが紹介されてた!

var Observable = (function () {
    function Observable(obj) {
        if (obj == null) return;
        for (var p in obj) {
            Object.defineProperty(this, p, createDescriptor(p, obj[p]));
        }
    }
    function createDescriptor(key, value0) {
        var value = value0;
        return {
            get: function () {
                return value;
            },
            set: function (v) {
                if (value === v) return;
                value = v;
                $(this).trigger("property_is_changed", [key, v]);
            },
            enumerable: true,
            configurable: true
        };
    }
    return Observable;
})();

連想配列JavaScriptで言うオブジェクト)でViewModelを作り、それを上記クラスでラッピングする。

<ul>
  <li><input data-model-key="num1"></li>
  <li><input data-model-key="num2"></li>
  <li><span data-model-key="total"></span></li>
  <li><span data-model-key="total2"></span></li>
</ul>
var model = (function(){
  var reload_view_is_finished;

  var model = {
    num1: 0,
    num2: 0,
    total: 0,
    reload_model: function(){
      reload_view_is_finished = false;

      this.total = parseInt(this.num1) + parseInt(this.num2);
    },
    reload_view: function(){
      if (reload_view_is_finished === true) { return; }
      reload_view_is_finished = true;

      $('[data-model-key="num1"').val(this.num1);
      $('[data-model-key="num2"').val(this.num2);
      $('[data-model-key="total"').text(this.total);
    },
  };
  return model;
}());

var observable_model = new Observable(model);
$(observable_model).on('property_is_changed', function(){
  this.reload_model();
  this.reload_view();
});

observable_model のプロパティに値の変更があれば、property_is_changed イベントが発火される。
その結果、reload_model() と reload_view() が実行される。

$(function(){
  // 初期表示
  $(observable_model).trigger('property_is_changed');

  $('input').on('change', function(){
    var key = $(this).attr('data-model-key');
    var value = $(this).val();
    observable_model[key] = value;
  });
});

フィールドに入力があれば、自動で ViewModel に反映される。
反映された ViewMoel から property_is_changed イベントが発火されるので、reload_view() で画面に反映される。

これで、開発がだいぶ楽になってくれるはず…。

PHP 引数の多いメソッド対策

以下のように、同じような引数が羅列されているソースコードをよく見る。

<?php
$this->getMemberAddress('秋元真夏', '乃木坂46', 'ソニーミュージック');
$this->getMemberAddress('白石麻衣', '乃木坂46', 'ソニーミュージック');
$this->getMemberAddress('山下美月', '乃木坂46', 'ソニーミュージック');

乃木坂46ソニーミュージック
の部分が重複しており、大変見苦しい。

そういう時は、カリー化して、引数の数を減らしてやる。

<?php
$curryingFunc = function($group, $company) {
    return function($name) use($group, $company) {
        return $this->getMemberAddress($name, $group, $company);
    };
}

$getMemberAddressByName = $curryingFunc('乃木坂46', 'ソニーミュージック');

echo $getMemberAddressByName('秋元真夏'); // 東京都…
echo $getMemberAddressByName('白石麻衣'); // 東京都…
echo $getMemberAddressByName('山下美月'); // 東京都…

重複部分が無くなった。

以下のように、部分適用としても良い。

<?php
$getMemberAddressByName = function($name) {
    return $this->getMemberAddress($name, '乃木坂46', 'ソニーミュージック');
};
echo $getMemberAddressByName('秋元真夏'); // 東京都…
echo $getMemberAddressByName('白石麻衣'); // 東京都…
echo $getMemberAddressByName('山下美月'); // 東京都…

引数が10個くらいあるメソッドを見てると、気が滅入ってくるよ。

PHP ユーザー定義関数に永続変数を持たせる

やりたいことは、JavaScriptで言うクロージャを作ること。
関数外のスコープにある変数を、関数内で保持していく。

useを使えば出来ると思ったら出来なかった。
useを使えるのは無名関数のみだったのか…。

<?php
(function() {
    $cnt = 1;
    function increment() use(&$cnt) { // syntax error, unexpected 'use'
        echo $cnt;
        $cnt++;
    }
})();


正解は、静的変数を使うこと。

<?php
function increment() {
    static $cnt = 1;
    echo $cnt;
    $cnt++;
}
increment(); // 1
increment(); // 2
increment(); // 3
var_dump($cnt); // null

無名関数 + use(参照渡し)
= 関数定義 + 静的変数
JavaScriptクロージャ

お粗末さまでした。

if文を考える

以下のような if 文をよく見かける。

if has_white_skin && is_good_looking
  p 'まいやん' # 色白で美人なのが、まいやん
elsif has_white_skin && is_cute
  p 'さゆにゃん' # 色白で可愛いのが、さゆにゃん
end

ありがちだが、良くない。以下はもっと良くない。

if has_white_skin
  if is_good_looking
    p 'まいやん' # 色白で美人なのが、まいやん
  elsif is_cute
    p 'さゆにゃん' # 色白で可愛いのが、さゆにゃん
  end
end

今後、スパゲティコードとして成長していくことが目に浮かぶ。


まず大切なのは、この処理には「白石麻衣」の場合と「井上小百合」の場合、その2通りの状態があること。

白石麻衣は「色白で美人」である。だが、どのような条件で「白石麻衣」と判断するか…それはどうでもいい。

大切なのは「白石麻衣」という状態が存在することだ。

状態を判断:色白で美人(どうでもいい)
↓
状態:白石麻衣(大切)
↓
処理:まいやん(ニックネーム。どうでもいい)


下の処理を見てみると、状態を判断(どうでもいい)と、処理(どうでもいい)だけしか書かれていない。

if has_white_skin && is_good_looking
  p 'まいやん' # 色白で美人なのが、まいやん

「状態が白石麻衣」であることは、どこにも書かれていない。
これを初めて読んだ人は「は?これ何について書かれているの?」となる。


以下の書き方が正解となる。

if has_white_skin && is_good_looking
  who = '白石麻衣'
elsif has_white_skin && is_cute
  who = '井上小百合'
end

if who == '白石麻衣'
  p 'まいやん'
elsif who == '井上小百合'
  p 'さゆにゃん'
end

「状態の判断」と「処理」が分離された。
状態として、「白石麻衣」と「井上小百合」の2パターンが存在することが見て分かる。

JavaScript バブリング中毒

以下、セレクトボックスが3つ並んだHTML。
全てのセレクトボックスが選択完了した時、何か処理を実行したいとする。

<div id="wrapper">
  <select id="select1">
    <option value="">---</option>
    <option value="1">aaa</option>
    <option value="2">bbb</option>
  </select>
  <select id="select2">
    <option value="">---</option>
    <option value="1">aaa</option>
    <option value="2">bbb</option>
  </select>
  <select id="select3">
    <option value="">---</option>
    <option value="1">aaa</option>
    <option value="2">bbb</option>
  </select>
</div>


昔の自分なら、こう書き始めていたと思う。

$('#select1').on('change', function() {
})
$('#select2').on('change', function() {
})
$('#select3').on('change', function() {
})


だが、今日知ってしまった。
changeイベントだって、上位のDOMにバブリングすることを…。

$('#wrapper').on('change', function(e) {
  // 全て選択されたら、CSSを変更
  if ($('#select1').val()
  && $('#select2').val()
  && $('#select3').val()) {
    $('#wrapper').css('background-color', '#0FF')
  }
})


もう全部バブリングでええんちゃう…?

Ruby 元のクラスを触らずに機能を拡張する

機能を追加することになった。
だが、元のクラスは触りたくない。
だってバグりそうだから。前のテストのやり直しをさせらせるかもしれない…。

自分の身を守るためにも、安全に機能を拡張する方法をまとめる。


以下クラスを拡張する。
フルネーム「白石麻衣」と表示出来れば、OK。

class Family
  def name
    '白石'
  end
end

1.継承する

定番。ぱっと見、分かりやすい。
インスタンス変数に依存した実装になりやすい。副作用大。

class Full < Family
  def name
    super + '麻衣'
  end
end

full = Full.new
p full.name # 白石麻衣

2.委譲する

委譲先から委譲元のインスタンス変数を触れないので、疎結合。良い。

class Full
  def name
    family = Family.new
    family.name + '麻衣'
  end
end

full = Full.new
p full.name # 白石麻衣

3.特異メソッドを定義

インスタンスにメソッドを定義する。

family = Family.new
def family.full_name
  name + '麻衣'
end
p family.full_name # 白石麻衣

4.モジュールをextend

インスタンスにモジュールをミックスインする。

module Full
  def full_name
    name + '麻衣'
  end
end

family = Family.new
family.extend Full
p family.full_name # 白石麻衣

5.Decoratorパターン

委譲の発展版。
インスタンス変数を内側・外側で、それぞれどう持ち回すかは謎。工夫が必要っぽい。

class Full
  def initialize(family)
    @family = family
  end
  def name
    @family.name + '麻衣'
  end
end

full = Full.new(Family.new)
p full.name # 白石麻衣


うーん、前任者のコードを触りたくない…。