このエントリーをはてなブックマークに追加

はじめに

こんにちは。CTOの 金本 です。令和もよろしくお願いします!

Nagoya.php とは、名古屋で隔月を目処に開催しているPHPの勉強会です。

もともと有志の個人によって運営されていた勉強会でしたが、立ち上げメンバーが次々にカルテットに入社した結果、ここ数年はなんとなくカルテット主催のイベントという感じになっています(笑)

(少し前になりますが)2019/04/17(水)に 通算15回目のNagoya.php を弊社セミナールームにて開催しましたので、その様子を簡単にレポートしたいと思います :elephant: :sparkles:

なんと今回は、日本PHP界の大家、BEAR.Sunday 作者の @koriym さんが移動の合間を縫って参加してくださいました! :raised_hands:

Nagoya.phpの特徴

さて、Nagoya.phpは、よくあるLT中心の勉強会ではなく、「実際に手を動かしてプログラミング問題を解いてみる」 という取り組み(通称「どう書く」)を中心にした内容になっています。

問題は主に、@Nabetani 氏が開催されている 横浜へなちょこプログラミング勉強会 という勉強会で出題されたものを拝借しています :pray:

問題は主に、@Nabetani 氏が過去に開催されていた「横浜へなちょこプログラミング勉強会」という勉強会で 出題されたもの を拝借してきました :pray:

2019/04/07をもって「横浜へなちょこプログラミング勉強会」のDoorKeeperコミュニティが閉鎖されており、今後は同勉強会は開催されないようです。
過去問題のリンクは残してくださっていますが、新しい問題がもう出てこないというのは寂しいですね :cry: 今までお疲れさまでした。

多くの方が独力で問題にチャレンジされますが、手ほどきが必要な初心者の方や、自分では解かずに他の人の解答を見るだけでいいやという人のために、スタッフ(私)が説明を交えながらライブコーディングを行う方式をとっており、お酒を飲みながらそれを見ていただくだけというスタイルでの参加もOKです。

ただ、今回がこのスタイルでの実施2回目だったのですが、プログラミング初学者の方が多かったこともあり、ほとんどすべての方が自力で解かずに私の解説を聞く形になってしまったので、次回からは初心者向けとベテラン向けで2パターンの問題を用意するなどの工夫をしてみようと考えています。(参加者さんのレベル感のばらつきが大きくて、なかなか上手い運営方法にたどり着けず、ずっと模索中です :sweat_smile:

乾杯&自己紹介タイム

19:00のオープニングとともに、早速全員で乾杯 :beers: :sparkles:

時間も限られているので一人30秒制限で全員に簡単な自己紹介をしていただきました。

自己紹介の題材としてPHP歴を発表していただいたのですが、始めたばかりという初心者の方もいれば、PHP歴15年以上といった大ベテランの方もいて、いつもながら非常にバラエティに富んだ参加者層でした :+1:

プログラミング問題タイム

自己紹介が済んだら、早速プログラミング問題タイムです。(制限時間は40分ほど)

今回使わせていただいた問題は、正八角形の分割 というものでした。

今回も「早い人なら40分程度でなんとか解ける」ぐらいの問題を苦労して選んだつもりだったのですが、上述のとおりそもそも自力でプログラミングに挑戦する方が少なかったので、ほぼ私の解説タイムになってしまいました :sweat_smile:

答え合わせ&交流タイム

20:00からは、ピザ :pizza: を片手にプログラミング問題の答え合わせをしつつ、交流タイムです。

ところで、前回は私が ピザの発注を忘れる という事故がありましたので(笑)、今回は忘れずに勉強会開始前にネットから宅配を申し込もうとしたのですが、なんと20:00到着の便が予約でいっぱいになってしまっており、ピザの到着が20:30頃になってしまいました :sweat: (毎度毎度、段取りをミスってしまって参加者の皆さまには本当に申し訳ないです…)

今回はLTの申し出が2本あったので、ピザを待つ間にLTを発表していただきました!

発表者 内容
@inaka_phper 前回の”どう書く”をもっと高速にしてみた
@koriym ハイパーメディアAPI設計手順

@inaka_phper さんの 前回の”どう書く”をもっと高速にしてみた は、前回取り組んだプログラミング問題 を、私が解説した方法よりもさらに高速に処理するアルゴリズムを紹介するという内容でした。

私の解答例よりも10倍速い実装を見せつけられました!悔しい!(笑)

@koriym さんの「ハイパーメディアAPI設計手順」は、RESTの本質に関するLTだったのですが、プログラミング初学者の参加者の方々にはさすがに内容が難しすぎたようで若干キョトンとされていました :sweat_smile:

逆に、ベテラン組の皆さんには非常に刺激的で興味深いお話だったようで、その後の懇親会の中にも積極的に質問や意見が飛び交い、活発な意見交換が行われていました!

参考ツイート

その後、無事にピザが届き、参加者の皆さんで食事をしながら好き好きに交流していただき、少し盛り上がりすぎて予定よりも1時間オーバーの22:00頃に解散という流れでした :pray:

おわりに

今回も隔月開催という約束を守って2ヶ月ぶりで開催することができました :muscle:

なお、参加者の皆さんの解答例や当日の様子がTwitterのハッシュタグ #nagoyaphp で見られたりもしますので、興味のある方は覗いてみてください :smile:

引き続き隔月開催を目標に頑張って運営していこうと思っています(次回は2019年6月開催予定)ので、名古屋近郊にお住まいのPHPerさんはぜひ気軽に遊びに来ていただけると嬉しいです!


このエントリーをはてなブックマークに追加

条件分岐禁止とは言ってもガード節は書きたい?

PHPerKaigi本編で 設計力を上げる!バリエーションの見極め術 を発表した直後、懇親会であるPHPer茶会で「条件分岐禁止について」という枠が急遽設けられて、駆けつけて話した際に「条件分岐禁止の趣旨はわかるがガード節は書きたい」という意見をもらいました。

ガード節とは

ガード節とは、引数がそのメソッド・関数の処理する対象として合っているかどうかをメソッド・関数の先頭でチェックするif文です。通常、マッチしない場合は例外を投げるか、early returnにより処理から抜けます。
ガード節は一般に良いプラクティスとされており(防御的プログラミング)、質問者の方もガード節を良いものという前提に立った時、私の言う条件分岐禁止はそれに反しないか気にされたのでしょう。

ガード節と条件分岐禁止

さて、PHPerKaigiの発表スライドではほぼ割愛しましたが、私が想定している条件分岐禁止を守って書いたコードは下記のような感じです。
※ スライド中では良い例としてappのコードのみ示し、詳細は 昨年の@hidenorigotoさんのスライド を見てください、と口頭で言いました。

appのコード

$targetDate = new \DateTimeImmutable('2019-05-02');
echo $wareki = $warekiProviderResolver->resolve($targetDate)->provide($targetDate); // 令和1年5月2日

Resolverのコード

<?php

class WarekiProviderResolver
{
    /**
     * @var WarekiProviderInterface[]
     */
    private $providers;
    
    // ...
    
    public function resolve(\DateTimeInterface $targetDate): WarekiProviderInterface
    {
        foreach ($this->providers as $provider) {
            if ($provider->supports($targetDate)) {
                return $provider;
            }
        }
        
        throw new \LogicException(sprintf('No matching WarekiProvider found for date %s', $targetDate->format('Y-m-d')));
    }
}

そして令和のためのWarekiProviderがこんな感じ。(平成、昭和、大正…それぞれの元号についてWarekiProviderInterfaceを実装したクラスを作ります)

<?php

class Reiwa implements WarekiProviderInterface
{
    public function supports(\DateTime $targetDate): bool
    {
        return $targetDate->format('Ymd') >= 20190501; 
    }
    
    public function provide(\DateTime $targetDate): string
    {
        // ★ここにガード節書きたい?★
    
        $year = (int)$targetDate->format('Y') - 2019;
        
        return sprintf('令和%d年%d月%d日', $year, $targetDate->format('n'), $targetDate->format('j'));
    }
}

先ほどのガード節は必要だと主張された質問者の方は、おそらく下記のような使い方がされることを危惧されているのでしょうね。

<?php
$targetDate = new \DateTimeImmutable('2018-04-30'); // 平成の年月日
$warekiProvider = new Reiwa();

echo $warekiProvider->provide($targetDate); // 令和-1年4月30日

質問された方には、「各クラスResolverでresolveする使い方を想定していると周辺のクラス構成からわかるときは、別にガード節を書いておかなくても誰も変な使い方をしないだろうから、ガード節は書かなくても問題ない」とお答えしました(契約による設計)。
少なくともカルテットでは文化としてこの書き方が浸透していますし、必ずコードレビューをするので、ここにガード節が必要とは考えたこともなかったです。
が、このような変な使い方をしてしまうメンバーがいて、それをレビューで止めることができない環境であれば、ガード節を書くこともやむを得ないのかもしれない、と考えていました。

nazonohito51/dependency-analyzer

PHPerKaigiのLTで @nazonohito51 さんが nazonohito51/dependency-analyzer というライブラリを発表されました。
dependency-analyzerは静的解析を用いてプロジェクト内のクラス間の依存グラフを出力したり、ルールに沿った依存のみが書かれているかバリデーションしたりすることを通じて、単方向依存を実現しやすくする・設計の意図をプロジェクト全体で共有することを目的としたツールです。(詳細は @nazonohito51さんのスライド をご覧ください)
LTを聞きながらこのライブラリを使えば前述の質問者の方が危惧されていた変な使い方はCIで検出できると思ったので、早速使ってみました。

使い方はとても簡単です。
composer require –devで追加して、使い方を限定したいクラス(今回の場合は Reiwa )に @canOnlyUsedBy をdocコメントとして書き込み、CIでdependency-analyzerのチェックコマンドを追加実行するよう設定するだけです。
設定済サンプルが下記にあります。

https://github.com/77web/DependencyAnalyzerUsage

dependency-analyzerで想定外の使われ方を防ぐ

CIにdependency-analyserを設定しておくと、Resolverで使うことを想定されたクラスが変な使い方をされてしまった場合には下記のようにCIが落ちます。

phpdoc in Quartetcom\TryDependencyAnalyzer\Wareki\Min
+------------------------------------------------+-----------+----+---------------------------------------------+-----------+
| depender                                       | component |    | dependee                                    | component |
+------------------------------------------------+-----------+----+---------------------------------------------+-----------+
| Quartetcom\TryDependencyAnalyzer\WrongUsageApp | other     | -> | Quartetcom\TryDependencyAnalyzer\Wareki\Min | phpdoc    |
+------------------------------------------------+-----------+----+---------------------------------------------+-----------+
The command "./vendor/bin/analyze-deps verify ./src" exited with 1.

https://travis-ci.org/77web/DependencyAnalyzerUsage/jobs/522931490

CIが落ちたらマージしない、という簡単な判定で想定外の使われ方を確実に防ぐことができるようになりました。

まとめ

カルテットは世界一効率的な広告代理店を目指しており、開発部のみならず社内全体で人の注意力より道具・仕組みの力でミスを防ごうとする雰囲気があります。
新しい道具も積極的に使って、よりメンテしやすいコードを書いていきたいですね。


このエントリーをはてなブックマークに追加

はじめに

こんにちは、@ttskch です。今日はちょっとした小ネタで、以下のツイートの焼き直し記事です :hand:

伝えたい内容はこのツイートでほぼすべて言い終わっているのでこの記事を書く意義はかなり謎ですが(笑)、一応細かいところを軽く補足しつつ記事の体裁にしてみようと思いますので、よろしければ最後までお付き合いください :joy:

※ GitHubを使っていることを前提とした内容になっていますのでご了承ください :pray:

WIP-PR駆動開発とは

WIP-PR駆動開発とは、開発の最初にWIP(Work In Progress:進行中)な状態からPull Requestを作ってしまって、そこにどんどんコミットを追加しながら開発を進めていく開発スタイルのことです。

開発の途中経過がGitHub上で可視化されるため、チームで開発している場合の情報の透明化に一役買ってくれます。もちろん一人での開発にも使えます。

WIP-PR駆動開発のやり方

基本的には以下のような流れで行います。

  1. ローカルでmasterから開発用のトピックブランチを切る
  2. そのブランチに git commit -m "[ci skip] wip" --allow-empty などのようにして空のコミットを追加
  3. とりあえずこの空のコミットをGitHubにpush
  4. GitHub上でmasterへのPull Requestを作成
  5. ローカルで開発を開始(最初のコミットは git commit --amend で空コミットと結合する)

「まだ何も開発してない状態でPRを作って、それから開発を始める」というのがポイントですね。

※ 空コミットのコミットメッセージに書いた [ci skip] はCircleCIやTravis CIなどのCIサービスでビルドをスキップさせるためのキーワードです(参考: CircleCI / Travis CI

めんどくさい

やってみると分かりますが、ブランチを切る度に空コミットを作ってpushしてGitHubのWeb UIでPull Requestを作って、という作業はまあまあめんどくさいです :dash:

そこで、一連の作業をシェルスクリプトで自動化してしまいましょうというのがこの記事の主旨です :muscle:

hubコマンド

シェルスクリプトを書く前に、お使いのマシンに hub というコマンドをインストールしておいてください。これは、GitHubの操作をコマンドラインで行うためのGitHub公式のCLIツールです。

https://hub.github.com/

このコマンドを使ってシェルスクリプトからPRを作成します。

hubコマンドには色々な機能がありますが、hub browse で今いるプロジェクトのGitHubリポジトリをブラウザで開いてくれる機能だけでもめちゃ便利なので、WIP-PRしない人もとりあえずhubコマンドは入れておくことをお勧めします。

hubコマンドの利用にはGitHubの認証が必要

hubコマンドを使うためには、当然ながらGitHubの認証が必要です。

ユーザー名とパスワードでも認証できますが、2FAを設定している場合は毎回2FAトークンの入力が必要になってしまうので、GitHub上でPersonal Access Tokenを発行して設定ファイルに記入しておくのがお勧めです。

https://github.com/settings/tokensGenerate new token というボタンからトークンを作成できます。スコープは repo だけでOKです。

作ったAccess Tokenを ~/.config/hub というテキストファイルに以下のようにユーザー名と合わせてセットしておけば、それ以降特に意識せずに認証ができます :ok_hand:

github.com:
- user: user_name
  oauth_token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

gitのサブコマンドは自作できる

ところで、gitコマンドのサブコマンドは簡単に自作できるということをご存知でしたでしょうか。

やり方はとても簡単で、 git-hoge といったファイル名の実行ファイル(シェルスクリプトなど)をパスの通ったディレクトリに置いておくだけです。こうしておくと、 git hoge のようにハイフンなしでそのコマンドを実行できます。

というわけで、今回はシェルスクリプトを git wippr というgitのサブコマンドとして実行できるようにしてみます。

実際のシェルスクリプトの内容と使い方

では、いよいよ実際のシェルスクリプトの中身です。(最初のツイートでネタバレしてますが笑)

#!/bin/sh

# 第1引数の文字列をブランチ名としてブランチを切る
git checkout -b $1

# 空コミット
git commit -m "[ci skip] wip" --allow-empty

# push
git push -u origin $1

# hubコマンドを使って、第2引数の文字列をタイトルとしてPRを作り、作られたPRをGoogle Chromeで開く
# ※ macOSでGoogle Chromeがインストールされている場合の例です
hub pull-request -m $2 | xargs open -a "Google Chrome"

これを /usr/local/bin/git-wippr に保存して、以下のようにして実行可能なパーミッションを設定すれば完了です :ok_hand:

$ chmod u+x /usr/local/bin/git-wippr

使い方も見たまんまで、masterブランチにいる状態で

$ git wippr branch-name "PRのタイトル"

という感じで実行すれば、 branch-name という名前でブランチが切られて、そのブランチからmasterへのPRが PRのタイトル というタイトルで作成され、Google Chromeで自動的にPRの画面が開かれます。

なんて便利なんでしょう :tada:

番外編

シェルスクリプトの中に

# hubコマンドを使って、第2引数の文字列をタイトルとしてPRを作り、作られたPRをGoogle Chromeで開く
# ※ macOSでGoogle Chromeがインストールされている場合の例です
hub pull-request -m $2 | xargs open -a "Google Chrome"

という箇所がありましたが、この方法だとリポジトリに PRのテンプレート がセットされている場合にもそれは適用されずにdescriptionが空のPRが作成されてしまいます。

これを回避する代替案として、上記の箇所を以下のように書き換えることもできます。

# hubコマンドを使って、標準のブラウザでGitHubのPR作成画面を開く
hub compare

この場合、git wippr コマンド実行時にPRのタイトルまで決めてしまうことはできなくなりますが、その代わりに、PRテンプレートが反映されたdescriptionが入力された状態のPR作成画面が開かれます。(PRのタイトルも、作成画面であとから入力することになります)

個人的には最近は個人開発でしかWIP-PRを使っておらずPRテンプレートとかほとんど使ってないので先述の方法をご紹介しましたが、チーム開発メインでPRテンプレートを多用する方は、こちらの方法のほうが便利かもしれません。

コマンド名を変えて2つとも作っておいて使い分けるという手もありますね。

おわりに

というわけで、140文字で書けることを長々と記事にしてお送りしました(笑)

ご自身の好みに合わせてカスタマイズしたりしつつ、ご活用いただければ幸いです :raised_hands: