著者アーカイブ 杉浦

著者:杉浦

【PHP】namespaceとautoloadの概要と連携

 最近、独自のオートローダを持つPHPプログラムを触ることになったので大雑把にnamespaceとautoloadの概要と連携を紹介します。
 namespace(名前空間)は1ファイルでも有効な変数名、関数名、クラス名等の区分け用の仕組みです。次の画像の様にちょっとしたスクリプトでも機能します。

 大雑把には各定義に接頭辞がついている様なものです。よくIDEのジャンプがnamespaceを元に働きますが、namespaceはファイルの読み込みに直接の関係を持ちません。
 autoloadは大まかには

public function autoload(string $呼び出す対象の名前){
    // なんやかんや処理
    require $呼び出す対象の書かれたファイルのパス;
    // あるいは
    include $呼び出す対象の書かれたファイルのパス;
}

PHP: require – Manual
PHP: include – Manual
をいい感じに動かす機能です。autoloaderによって多数のPHPソースコードファイルの中から必要な分だけPHPソースコードファイルを読み込みます。
 namespaceとautoloadのそれぞれは互いに関係ありません。PSR-4で定められた規約に従ってnamespaceとautoloadは連携します。
PSR-4 autoloader (日本語訳) – Qiita
PSR-4: Autoloader – PHP-FIG
 PSR-4はnamespaceとファイルの配置を定義づける規約です。大雑把にいえばnamespaceの名前、階層をディレクトリ構造と一致させなさい、という規約です。
 新しめのフレームワーク(laravelなど)やオートローダ(composerなど)はこのPSR-4に従っているためnamespaceによってファイル読み込みをしているとも言えます。ファイルを読み込めない時はコーディングをしている人が謎namespaceを定義してしまった時が大半です。逆にPSR-4に従っていない古いオートローダはnamespaceが通じているといってソースコードが読み込めているとは限りません。そういった場合、使われているオートローダのドキュメントなりソースコードなりを読み込む必要があります。
 

著者:杉浦

【Chrome】開発者ツールNetworkタブInitiator列の紹介

 Chromeで開発を行う際、通信の内容を見るためにNetworkタブをよく見ます。そういった時、Initiator列は全く使いませんが、この列は意外に高性能です。具体的に何ができるかというと、カーソルを合わせた時に通信の起点を元にスタックトレースを出力します。

 これによってどのコードのどの部分で通信、特にaxiosなどの非同期通信が走っているか分かります。これだけでも便利なのですが、強力なのはスタックトレースであるという点です。仮にサーバからブラウザに暗号化したリソースを投げたとしてもスタックトレースをさかのぼり、そこからステップ実行をすると復号処理(ページ上でリソースを使うために復号するものと仮定)の場所、引いては復号した状態を得られます。これと前回のJavaScriptの圧縮を展開する方法によって謎リソースを元にした謎処理でも案外、解き明かせます。昨今のブラウザは強力ですね。

著者:杉浦

【Chrome】圧縮されたJavaScriptを圧縮されてない様に扱う

 Chromeの開発者ツールのちょっとした機能の紹介になります。
 長い行に圧縮されたJavaScriptを展開する機能です。ちょっとしたLint、prettier的な感じで改行とインデントが入ります。使うには下一枚目の赤丸で囲った{}ボタンを押すだけです。

 特筆すべきはこの状態でブレークポイントを指定できる点です。これのおかげで図の様に圧縮済みのコードでも現在、どこのどの変数に何が入っているのか、ここまでのフローはどうなっているのか、今起きているEventは何なのかと様々な情報を得られます。

著者:杉浦

【GitHub】目的に合ったスニペットをフレームワークの観点から探す

 GitHubの検索機能には様々なオプションが付いています。その中の一つにCode optionsのIn this pathがあります。これはその名の通りコードファイルが置いてあるファイルパスで絞込検索をするオプションです。
 フレームワークには大なり小なり型にはまった作法があります。よくある作法の種類に、特定の種類のコードを特定のフォルダに配置する、というものがあります。Laravelのコンソールコマンドならばapp/Console/Commands、Composerを使うなら外部ライブラリはvendor、といった具合です。
 Artisanコンソール 6.x Laravel
 この二つを合わせると特定の種類のコードと果たしたい目的の語で検索をすることができます。例えば、次のようなことができます。FuelPHPにはコンソールコマンドoilが用意されています。oilのサブコマンドとして様々なことをコンソール上で行えます。
はじめに – Oil パッケージ – FuelPHP ドキュメント
 oilは便利なのですがルーティングを確認する機能がありません。Laravelのartisan route:listの様にコンソールで一覧を見たいものです。GitHubで次のようにググるとそれを実現したコードが見つかります。

route path:fuel/app/tasks


 このコードらをたどっていくとリポジトリkenjis/fuelphp-tools: Tools for FuelPHP 1.xにたどり着きます。このリポジトリのtaskコードの一つにルーティングをコマンドライン上で表示する機能が入っています(残念ながらmodule以下のルーティング解析は未実装です)。
 このおかげで次の様にroutingの大部分をいい感じに見れるようになりました。

著者:杉浦

PlantUMLの機能が思ったより多彩

 PlantUMLはUML図を表現できる言語です。言語故にテキストファイルであり、変更やGit管理が楽で便利です。
 PlantUMLを書いていると、あまりに図が複雑になるため複数ファイルに記述を分けたい時があります。そういった時、何度も同じ意味で同じ名前を宣言する記述が重複しがちです。そういった問題はinclude機能で定義ファイルの読み込みのみを記述することで解決できます。
 Use the preprocessorのIncluding files or URL

// define.puml
@startuml
participant "ブラウザ" as br
participant "サーバ" as svr
@enduml

// child.puml
@startuml
!include define.puml
br-->svr: ページ要求
@enduml

// child2.puml
@startuml
!include define.puml
br<--svr: ページレスポンス
@enduml


 こうすると、あるファイルだけ違う変数名で同じものを指している、という混乱やコピペコードを防げます。
 デフォルトのPlantUMLでER図を書いている時、定義の記述を一行にきれいなデザインでまとめることにくたびれます。デフォルトで等幅フォントを採用しておらず、空白を使って縦を合わせるのがめちゃくちゃしんどいです。等幅フォントを使うように宣言しましょう。
 Which fonts are available ?

@startuml
skinparam {
  defaultFontName Monospaced
}

entity users {
  # id                  int(11) unsigned auto_increment
    name                varchar(255)         not null,
    login_id            varchar(50)          not null,
    password            varchar(255)         not null,
    password_changed_at datetime             not null,
    email               varchar(255)         not null,
    last_login          varchar(25)          not null,
    login_failed        tinyint              not null,
    locked              tinyint              not null,
    locked_at           int                  not null,
    status              tinyint    default 0 not null,
    login_hash          varchar(255)         not null,
    created_at          int        default 0 not null,
    updated_at          int        default 0 not null,
    deleted             tinyint(1) default 0 not null,
}

@enduml


 ここでは特に使うと確信した二例を紹介しましたが、公式の前処理、共通事項をはじめとしたUMLから離れた欄を隅まで見ていくと思わず教えて欲しかったとなる便利機能がわいてきます。
 シンプルなテキストファイルで UML が書ける、オープンソースのツール

著者:杉浦

KISS原則のためのデザインパターン

 デザインパターンは今までに至るまで作られてきたコーディングの際のパターンです。大体これは複数ファイルにまたがるつくりになっており、とてもとても複雑な問題をなるべくシンプルにされたパターンを知るだけで解決できるようにします。図は増補改訂版Java言語で学ぶデザインパターン入門 | 結城 浩 |本 | 通販 | Amazonから引用したFacadeパターンのクラス図です。

 KISS原則はKeep is simple stupidの略で「単純な作りを保て」という意味です。短くて意味が明確だと読み解きやすいものです。原則というだけあり、デザインパターンもとてもとても複雑な問題に対してこの原則を実現するために作られていることが多いです。
 KISSの原則 – Wikipedia
 デザインパターンの実現よりも原則の実現を優先すべきです。語弊がある文なので補足ですがコーディングで最も優先すべきは要件の実現です。動くきれいなコード>動く汚いコード>動かないきれいなコード、です。
 Gitのソースコードでも業務範囲でもこの二つについてのアンチパターンに本末転倒なパターンが時折見られます。簡単な作りで済むのにデザインパターンを適用したばかりに読み解くのが面倒になります。例えば、Facadeパターンなら次のようなものです。

// 極端にやってしまったパターン
function client() {
    $fasade = new Fasade()
    $fasade->echoA();
    $fasade->echoB();
    $fasade->echoC();
    $fasade->echoD();
}

class Fasade {
    use EchoA, EchoB, EchoC, EchoD;
}

trait EchoA {
    function echoA() {
        echo 'a';
    }
}
trait EchoB {
    function echoB() {
        echo 'b';
    }
}
trait EchoC {
    function echoC() {
        echo 'c';
    }
}
trait EchoD {
    function echoD() {
        echo 'd';
    }
}
// これで十分だったパターン
function client() {
    echo 'a';
    echo 'b';
    echo 'c';
    echo 'd';
}

 無暗にクラスを分けたせいで複雑化しています。例は極端ですがアクティブレコードのみで十分なのに、コントローラ->コマンド->リポジトリ->モデル->SQL文発行、の様な段階が過度に多いものはままあります。簡単な問題を複雑にしていないか、本当にそこまで大がかりなものを用意する必要があるのか、都度考えてコーディングしたいものです。

著者:杉浦

【JavaScript】配列関数を使った読みやすいオブジェクト生成の書き方

 次の様な入出力を実現する関数dictMergeをなるべく読みやすく短い関数で実現するのが目標です。dictMergeは未定義や余分な定義を含む複数の辞書から必要なカテゴリーの語のみをまとめるための関数です。たとえなので辞書ですが今回紹介する配列、オブジェクト操作のようなやり方は色々な場面で使えます。

// 入力
const categoryKeys = ['hoge', 'fuga', 'hogefuga'];
const dictA = {
	hoge : ['hoge1', 'hoge3', 'hoge5'],
	fuga : ['fuga3', 'fuga7', 'fuga13'],
}
const dictB = {
	hoge : ['hoge7', 'hoge9', 'hoge11'],
	bar : ['bar2', 'bar4', 'bar6'],
}
const dictC = {
	hoge : [],
	fuga : ['fuga19', 'fuga29', 'fuga37'],
	hogefuga : ['hogefuga'],
}

// 出力
const mergedDict = dictMerge(categoryKeys, dictA, dictB, dictC);
console.log(mergedDict);
/*
{
	hoge : ['hoge1', 'hoge3', 'hoge5', 'hoge7', 'hoge9', 'hoge11'],
	fuga : ['fuga3', 'fuga7', 'fuga13', 'fuga19', 'fuga29', 'fuga37']
	hogefuga : ['hogefuga']
}
*/

 実装したdictMerge()が次になります。コードの細部はコメントの通りです。

/**
 * @param {Array} categoryKeys
 * @param {Array} dicts
 * @return {Object}
 */
function dictMerge(categoryKeys, ...dicts) { // 第二引数以下を変数dictに配列として格納する. 今回ならdicts = [dictA, dictB, dictC]
    return categoryKeys.reduce((mergedDict, category) => { // reduce関数で配列を一つのオブジェクトに変換する。詳しくは下記
        return {
            ...mergedDict, // スプレッド構文でreduceの前のループ結果を引き継がせる
            [category]: dicts.map(dict => dict[category]) // [category]で変数をキー名にする。map関数で扱う配列を[dictA[category], dictB[category], dictC[category]]にする
                .flat() // 配列の中身を一段flatにする。例えば[Array(3),Array(3),Array(0)]の二次元配列がArray(6)の一次元配列になる
                .filter(v => v !== undefined)// 未定義でない値のみを配列の中身に残す
        }
    }, {});
}

 上記の様にJavascriptに用意されている関数をチェーンするだけで少々複雑な問題も簡易な記述で解けます。この様なやり方を用いる際に注意する必要があるのはreduce関数です。reduce関数はJavaScriptの配列操作においてとても汎用的であり、書こうとさえ思えばほぼ全ての配列操作関数をreduce関数で表現できます。これを使うと次のように黒魔術的もかくやというコードを書けます。

// dictMergeと等価
function dictMergeByReduce(categoryKeys, ...dicts) {
    return categoryKeys.reduce((mergedDict, category) => {
        return {
            ...mergedDict,
            [category]: dicts.reduce((acc, value, index) => {
                acc[index] = value[category];
                return acc;
            }, [])
                .reduce((acc, value) => acc.concat(value), [])
                .reduce((acc, value) => {
                    if (value !== undefined) {
                        acc.push(value);
                    }
                    return acc;
                }, [])
        }
    }, {});
}

 なるべくreduceを使わず特化的な名前の少ない引数の配列関数を使うとわかりやすいコードになります。最初の例も正直reduceを使うまでもありません。reduce((o, k)=>{return {…o, [k]:kに関する何か}},object型引数)という構文は便利なのですが慣れない人に渡すとけっこう混乱します。注意するべきでしょう。

// 多くの人がわかりやすく読みやすいであろう版
function dictMergeSimple(categoryKeys, ...dicts) {
    const mergedDict = {};
    categoryKeys.forEach(category => {
        mergedDict[category] = dicts.map(dict => dict[category])
                .flat()
                .filter(v => v !== undefined)   
    });
    
    return mergedDict;
}
著者:杉浦

PlantUMLをwebページ的にダンプする

 具体的には各UML図をハイパーリンクによってつなげたSVGファイルにしてブラウザ上で見れるようにします。目的はアクティビティ図->シーケンス図、ER図全体->ER図部分の様な関連した別UML図を閲覧者が探しやすくすることです(WWWの元ネタそのまま)。この様にできるとPUMLを一つの場所に過度に巨大に濃密に記述する必要がなくなります。
 実際にこれを行うための手順は、PlantUMLを記述する際に出力状態のSVGリンクを相対パスで作る、階層構造を維持したままSVGにダンプする、の2ステップだけです。これによってGIFの様にできます。

 PlantUMLに次のように書くことで図のようにハイパーリンク付きの文字列を作れます。使える場所はかなり広く文字列を表示する場所ならどこでもOKな印象です。今回はpumlファイルのあるフォルダと同じ場所にsvgファイルを展開するつもりなので、記述中のpumlファイルの場所からみた特定のpumlファイルへの相対パスを記述します。

:[[greeting/HelloWorld.svg Hello world]];

 特定のディレクトリ下にsvgとしてpumlをダンプするコマンドは次になります。

java -jar plantuml.jar ./**.puml -tsvg -charset utf-8

 現在のディレクトリ以下の.pumlファイル全てを文字コードutf-8のSVGにして.pumlのあるフォルダと同じ場所にダンプします。これだけでできます。特定フォルダ以下に出力するには-oオプションを用います

java -jar plantuml.jar ./**.puml -tsvg -charset utf-8 -o svg

ならば./svg以下にSVGファイルが出力されます。

著者:杉浦

GitHubを使って一般的な変数名を選ぶ

 学生時代、分かりやすい論文を書くために気にするべきことの点として「一般的な語を選ぶ」という点がありました。ここでいう一般的とは、対象の界隈(論文なら学術分野)で広く使われていて共通認識が存在する、ということです。「一般的な語を選ぶ」とは、角速度センサとジャイロセンサの様な同じものを指す語の内より多く使われている方を選ぶ、といった感じの行為です。このどれが最も多く使われているかを簡易に調べる方法としてググった際のヒット件数の比較があります。界隈を限定するべき語(プログラミング言語のRubyと宝石のRubyを分けて検索したいとか)の場合は検索する語を追加したりGoogle Scholarまで行ったりします。先ほどの例ならばジャイロセンサの方が多く使われているので、角速度を計測するセンサを表す時は”ジャイロセンサ”を使うと判断できます。

 コーディングの際に気にするべきことに可読性、検索容易性があります。中長期の開発や機能改善の際にはいくらか忘却したコードを改めて読む必要があります。この時に変数名、クラス名などの様々な値の名前から値の意味が理解できない場合、コードを読み解くのに時間がかかります。また、ある機能について調べる時に値の名前から探そうとするもあり、一般的でない名前が使われているとまともに検索できません。次のツイートは極端な例ですが似たようなことは簡単に起きます。


 この様な名前の問題を避けるためにリーダブルコードでは次の様に指南されています。

名前をつけるときには、それが変数であっても、関数であっても、クラスであっても、同じ原則を当てはめることができる。名前は短いコメントだと思えばいい。短くてもいい名前をつければ、それだけ多くの情報を伝えることができる。プログラムに使われる名前というのはハッキリしないものが多い。例えば、tmpなんかがそうだ。でも、sizeやgetみたいに一見すると問題がなさそうな名前であっても、情報が含まれていないことがある。これから情報を詰め込んだ名前のつけ方を紹介する。本章は、以下の6つのテーマで構成されている。
– 明確な単語を選ぶ
– 汎用的な名前を避ける(あるいは、使う状況を選ぶ)
– 抽象的な名前よりも具体的な名前を使う
– 接尾辞や接頭辞を使って情報を追加する
– 名前の長さを決める
– 名前のフォーマットで情報を伝える

Dustin Boswell; Trevor Foucher. リーダブルコード (Kindle の位置No.228-232). 株式会社オライリー・ジャパン. Kindle 版.

 共通認識の成り立っていると仮定できるくらい一般的な名前ならば、少ない文字数で「明確な単語を選ぶ」「汎用的な名前を避ける(あるいは、使う状況を選ぶ)」「抽象的な名前よりも具体的な名前を使う」の三つが満たせます。また「接尾辞や接頭辞を使って情報を追加する」をした際に過度に長い名前ができあがらなくなります。
 論文における一般的な語の選択の様に、コーディングにおける一般的な名前を選択しようとするならば、コーディング界隈から使用されている数を調べる必要があります。この時、Googleは程々にしかあてになりません。Googleで検索できるのはweb上のコンテンツ全てでありソースコードから離れています。代わりにGitHubの様なソースコードリポジトリを用いることができます。特にGitHubならば言語、フレームワークでソースコードを絞り込むこともでき、より適した範囲から語の検索ができます。
 例えばJavaScript界隈で緯度経度を変数にするならばlat, lngにしておくのが適切だと分かります。

著者:杉浦

【Vue.js】コンポーネント外で定義した値をテンプレート内で使う

 Vue.jsの単一ファイルコンポーネントはコンポーネントを構成するHTMLテンプレート、JavaScript、CSSを一ファイルにまとめる仕組みです。コンポーネントの構成要素全体を把握しやすい点で優秀なのですが、テンプレート部で扱うことのできる変数が若干不透明で時として直感的でない振る舞いをします。
 次の例ではコンポーネント内全体がスコープっぽいところで変数hogeStrを宣言してテンプレート内で使用しようとしますが、これはhogeStrが未定義扱いされます。

<template>
  <div>{{ hogeStr }}</div>
</template>

<script lang="ts">
const hogeStr = 'hoge';

import Vue from 'vue';
export default {};
</script>

 実際に扱うときにはVueインスタンスの内部に含まれなければいけません。変化のない定数でも必須です。そのため実際は次のようにします。

<template>
  <div>{{ hogeStr }}</div>
</template>

<script lang="ts">
const hogeStr = 'hoge';
import fuga from 'fuga';

import Vue from 'vue';
export default {
  data() {
    return {
      hogeStr // 変化する値はdata
    };
  },
  computed: {
    nearMsgEnum, // 変化せず、getterのみでいいならcomputed
  },
};
</script>

 単一ファイルコンポーネントの作りではテンプレート、JavaScript、CSSがタグごとに分かれますが、結局のところtemplateタグもVueインスタンスの一部でありVueインスタンス外部を見るスコープにないようです。
 余談ですがdata, computedで同じ名前の値を読み込んでもVue.jsは動き続けます。外部から値を読み込むように作り替えた時に想定外の挙動が起きたならば、これが起きているのではないかと疑えます。