それが僕には楽しかったんです。

いろんなレイヤーに居ます

Nagleアルゴリズムと遅延ACK

はじめに

どうも、最近嫌な予感が段々と確信になっていくタイプのサイコホラー映画に弱いという事がわかったけんつです。


諸事情で、Linuxカーネルモジュールとして実装したEchoサーバのコードを見なおしていたらNagleアルゴリズムと遅延ACKというものに出会いました。
気になったので色々と調べているとその仕組みが面白いなと思ったのでまとめます。

本題に入る前に

今回紹介することは、ネットワークで特にTCP/IPにおける輻輳制御についてのお話になる。
がしかし、輻輳制御ってなんやねんと思うのでちょっとまずそこに触れておく。

輻輳輻輳制御とは

輻輳とは、「さまざまな物が1カ所に集中すること」*1 を指している。
ネットワークの分野に置いては「ラピュタ金曜ロードショーで放送されたときTwitterバルスが流れまくる」ときの様に一箇所にアクセスが集中するときが輻輳のいい例となっている。

対して、輻輳制御とは輻輳が発生しそうになった場合にそれを制御することを指している。インフラにおいては、ロードバランサーがいい例として挙げられる。

Nagleアルゴリズムとは

ネーグルアルゴリズムと言うみたい。
RFC 896「IP/TCPインターネットワークにおける輻輳制御」*2で制定されており、その名の通り輻輳制御に関するアルゴリズムである。
そもそも、これが何故必要かということはある問題がキーになっているとRFCには記載されている。

The small-packet problem

この問題は、アプリケーションが1byte 程のサイズでパケットを送ってしまう際に発生する問題を指している。
何が問題かといえば、IPv4TCPのそれぞれでヘッダーサイズが20byteあるため 1byte ほどのメッセージを送信するために 41byte のパケットを送信する点にある。
このヘッダーに対して明らかに大きすぎるパケットを送信してしまう現象は、 Telnetセッションでよく見られるらしく通信速度の遅いネットワークを圧迫する。

The small-packet problem の解決法としてのNagleアルゴリズム

この問題を解決するためにNagleアルゴリズムは存在している。
Nagleアルゴリズムは以下の3つの条件を満たすまで、パケットをバッファにためて条件を満たした段階で送信することで効率的にデータを送信するアルゴリズムである。
つまり、「Nagleアルゴリズムでは最大セグメントサイズ以下の複数の送信メッセージを一つに束ね、まとめて送信する。特に、送信パケットで送信側が ACK を受け取っていないのがある場合、送信するに値するまで送信側はバッファリングを行い、そして、一度にまとめて送信する。」アルゴリズムとなる*3

  • 未送信データが最大セグメントサイズ以上になる
  • 過去の送信パケットで ACK が未受信の物がなくなる
  • タイムアウトになる

このアルゴリズムで注意したい点は送信を遅延させる時間が200~500msであるという点(BDS系は最大200ms)。
これは次にまとめる遅延ACKとの相性が最高に悪く、オンラインゲームやその他のリアルタイム性を要求されるネットワークに於いて
そもそも、Nagleアルゴリズムがレイテンシを犠牲にしている分余計に遅延が発生してしまうという致命的な問題を発生させる。

余談:Nagleアルゴリズムを無効化する

前述の通り、リアルタイム性が要求される場合Nagleアルゴリズムは悪い影響を引き起こす場合がある。
そのため、特にレイヤーの低い分野においてソケットでTCP_NODELAYオプションが提供されている。
これを有効化することで、Nagleアルゴリズムを無効にできる。

遅延ACKの前に…

遅延ACKの前に前提としていくつかTCPについて知らなければならないことがあるのでそっちを先に紹介する。

ウィンドウ

TCPにはウィンドウという概念がある。
これは何かというと、TCPでは送信されてきたデータを一時的にバッファに溜め込んでアプリケーションはそこからデータを取り出し処理していく。
これをウィンドウといい、一度に溜め込める量をウィンドウサイズという。

そしてウィンドウサイズは、送信側が確認応答を待たずに一度に送信できる最大量でもある。

またこのウィンドウサイズはフロー制御によって変更される場合があり常に一定というわけではない。

スライディングウィンドウ

スライディングウィンドウについては以下のサイトがわかりやすくまとまっている。
beginners-network.com

このサイトでは、9000byteのデータを送信したい場合でウィンドウサイズが3000byteの場合を想定している。

まず9000 byteを 1セグメント 1000byte ずつ送信する場合3つのパケットを一度に送信できる。
受信側がACKを送信側に送った場合、3001byte~6000byte分のデータを送信する。

このように、相手側のウィンドウサイズに応じて送信するデータの範囲をずらしていくことをスライディングウィンドウという。

しかし、実際には1000byte送ってスライディングウィンドウすることも可能なのだがそれをしてしまうと1セグメントごとにACKを送る必要があるため
非常に効率が悪くなってしまうという問題が発生する。
これが次の遅延ACKに関連している。

遅延ACK

データを受信した側が即座にACKを返すと、フロー制御が働いて小さいウィンドウサイズが設定される場合がある。
送信側はそれに合わせてデータを送ってしまうと非常に効率が悪くなる。(これはSWS: Silliy WIndow Syndromeと呼ばれている)

そこで、このACKを意図的に送らせて、ネットワークを効率よく利用する方法が挙げられた。
それが遅延ACKとなる。

遅延させる場合は

  • 2 * 最大セグメントサイズのデータを受信するまで確認応答しない。
  • 上記以外の場合は最大200~500ms、ACKを遅延させる。

大体2セグメントごとにACKを送信するように遅延させる場合がおおい。(らしい)

Nagleアルゴリズムと遅延ACK

上記で紹介した、遅延ACKとNagleアルゴリズムが両方働くと遅延が余分に働く場合がある
特にサーバが遅延ACKのタイムアウトになるまでリクエストがストップしてしまうという場合。*4

これは以下のサイトにわかりやすい例が記載されている。
postd.cc

特にわかりやすい例を以下にあげる

アプリケーション: やあ、パケット1だよ。
HAProxy:<無反応で、2つ目のパケットを待っている>
HAProxy:<そのうちACKするけど、まあいいか>
アプリケーション:<無反応>
アプリケーション:<ACKを待っているんだけど、ネットワークが混雑しているのかな>
HAProxy:もう待ち疲れた。はい、ACKだよ。
アプリケーション:やった! じゃあ2つ目のパケットを送るよ。
HAProxy:よかった。これで終了だ。

送受信側が意図的に待機している時間があるが、そこで余分な200msが発生している。
これが、リアルタイム性を要求されている場合わりと致命的な遅延となってしまうのでNagleアルゴリズムを無効化するかTCP_CORKで遅延時間を制御する場合がある

おわりに

楽しかった。

Unicode9.0 以降の複雑な符号化における letter-spacing トラップ

はじめに

どうも、最近レンタルしたレディプレイヤー1が面白すぎて購入を検討しているけんつです。

最近、Webにおいて圧倒的に面倒な絵文字対応について色々と考えているところなのですが
もろもろ対応していると不可解な現象とその原因がわかったので少しだけまとめます。

問題

Unicode9.0以降で絵文字で肌色が選択できるわけなのですが、あれをWeb上で表示させるときに何故か
以下の様に絵文字が 本体+スキントーンの形式に分離してしまう問題が発生していた。

しかもスキントーンは色が四角くなって出てきてしまい、初見ではこれが一体何なのかわからなかった。

f:id:RabbitFoot141:20181112203116p:plain

実際はスキントーン以外でも、国旗や複数人がひとつの絵文字に存在するような場合には同様に分離してしまう箇所がいくつかあった。

そして、なぜか場所によっては正常に表示されている部分もあった。

原因

肌色や複数のコードポイントにまたがって表現される絵文字は以下のような形式を取っている

U+1F466 U+1F3FE

このように結合文字列としてひとつの絵文字を表現しているのだ。

そのため、 letter-spacing が使用されていると本来ひとつの絵文字として表現されているものが
U+1F466 と U+1F3FE に分離してしまうといったものだった。

実証

f:id:RabbitFoot141:20181112204047p:plain

このように、letter-spacingを利用しているかどうかで挙動が異なる



以下のサイトで実際にどういう区切りになっているのかなどがよくわかる。
異体字セレクタセレクタ (α v0.3)

終わりに

知らんがな。

今までやったこと、これからやりたいこと

はじめに

最近、荒廃した世界で生きていく系の映画とドラマに弱いという事が発覚したけんつです。

今まで色んなことやり過ぎて、最近自分でも何やってきたか思い出すまでややしばらく時間がかかるようになってしまったのでポエムっぽく書いていきます。

ざっくりと

こんなこと今までやってきました。

時系列順です。最初にやっていたことからです。

  • 競プロやってました (Java)
  • 一瞬Androidやってた時期ありました (Java)
  • 一瞬クローラ作ってました (Ruby)
  • 画像処理を少しやってました (Python + OpenCV)
  • 自然言語処理も少しやっていました (Java + Deeplearning4j)
  • APIのラッパーライブラリ作りました (Scala)
  • Webサービス展開している企業でリモートワークはじめました (PHP7 + ES6)
  • Linux Kernel Module作りました (C-lang)
  • DHCPパケット書いてました (C-lang)
  • サーバサイドに骨埋めました (PHP7 + NodeJS)
  • AWS使ってインフラ構築しました (S3 + EC2 + Cognito + SQS + RDS)

インフラ系の技術は以下のやつ触ってきました

普段の開発環境

現在進行形でやっていること

  • サーバサイド全力で勉強しています
  • nodejsはじめました
  • API設計に関する勉強しています
  • 深層学習についてゼミで勉強しています(Python + Keras)

やっていきたいこと

  • OSS 開発に貢献していきたい
  • モバイルやってみたい
  • サーバサイド、もっと極めてみたい
  • とにかくなんでもやってみたい(これ大事)

競プロ

これは、プログラミングというものを始めたは良いが何を作ればいいかわからなくて結構やっていた時間は長い。
新しい言語をやったときは大体やる。

基本的にはAtcoder, Yukicoder, ちょっとだけTopcoderに出ていた。
今は難しいが、ABCでいうところのC問ぐらいまでは30分で解けるだけは勉強していた。

これは

  • Java8
  • Python3
  • PHP7
  • Scala2.12
  • C

で解いていた。

途中でJVM系言語のパフォーマンスチューニングやったり、無駄にワンライナーで解いたり
意味不明な方向に走り始めたのが最後になってしまっている。

一瞬系

これはほんとに一瞬やっていたぐらいのやつ

android

競プロやりすぎて、もっとグラフィカルなものをやりたいとおもって少しだけ始めた。
何を血迷ったか WebView おもしれぇ!!ってなってブラウザを作りたいなってなって、実際に作っていた。

クローラー

Androidをやった後に、なにやろうってなって本屋でクローラー開発技法の本を見つけ一瞬はまっていた。
多分この時期ぐらいしかRuby書いてない。

画像処理

これは他のに比べると少し長い。
すぐに結果が画像としてわかるっていうのが面白くてのめり込んでいた。

Python + OpenCVと書いたけど、画像処理に関する基礎的な勉強をしたくて
OpenCVは画像のロードにだけ使っていた。
フィルタとかその他色々なものは、二次元配列を直接いじって再現したりなんだりと。
このブログに色々まとめている。

自然言語処理

この時期が一番、本当に一番迷走していた。
なにかをこじらせて、彼女造る宣言をして急にやり始めた。
結局会話はできなかったけど、形態素解析マルコフ連鎖、単語のベクトル化はやった。
なぜかJavaで書くことに固執していた。

APIラッパーライブラリ

これはかなりやっていて、ここまでのものに比べると本当に頑張っていた。
やった内容は、ScalaでTwitter4Sというラッパーライブラリを作っていた。

というのも、これ実は数年前にもやっていて、その時は難しすぎて作れなくてリベンジの意味合いもあった。
なので、本気でリベンジしようと思って ver 1.0.0 は Java, Scala の標準ライブラリのみで処理を書いていた。
なんだったらそれにこだわるが故に、Apache Base64Scalaで書きなおしていた。

その後、デザインパターンを学んだり、テストを導入してCIで回したりして公開することになったので
Base64 部分と JSON 解析部分はライブラリを使うようになった。
HTTP系の処理は今も標準ライブラリでフルスクラッチしたままだけど、これが最高に楽しかった。
寝ても醒めても、考えていたのはこのプロダクト以外には無い。

github.com

リモートワーク

社名とかは出せないが、大きなトラフィックを誇るWebサービスを自社展開している企業でリモートワークを昨年から始めた。
基本的に作業内容は新機能開発とリファクタリング、バグ修正を行っている。
今の自分が半人前ながらサーバサイドエンジニアをやっているということや、色んな所で自分の得意分野はサーバサイドですと胸を張って言えるのはここで現場に突っ込まれて鍛えてもらえたからだとおもっている。
けんつのサーバサイド人生には欠かせない要素。

未経験だった自分を採用して頂いたことや、そのあと色々と勉強させてもらったこと
いろんな経験を積めたこと、お世話になった方々
その全てに感謝してもしきれない。

カーネルモジュール

これは初めてのインターンで KLab さんの技術系インターンに参加することになった時にでた事前課題「カーネルモジュールでEchoサーバを作る」に取り組んだことに関連している。

事前面談で
「ネットワーク系に触れて欲しいけど、単にEchoサーバじゃ簡単すぎるからカーネルモジュールにしてもらおうか」
ということではじめた。

カーネル周りはバージョンによっては本当にドキュメントがなくて
必要とあらば、カーネルソースコードを読んだりして理解してきた。

大変だったけど、ただのネットワークどころかカーネルで扱えるネットワークについての知見を貯めることができたので面白かった。

この時点で、やった分野がカーネルからWebまでという意味不明な広さになっていく。

github.com

DHCPパケット

その後のインターンでは
メンターの山本雅也さんが作っていたアプリケーション空間で動作するプロトコル・スタック「microps」にDHCPクライアント機能をつける
という課題に取り組んだ。

C言語でやっていたわけだが、アドレスやポインタ、16進数、ネットワークと格闘しながらどうにかこうにか実質3日で実現できた。
RFCと格闘したのも今となってはいい経験となった。

github.com


これの本当に大事な部分は、「問題にぶちあたった時に、経験が無くとも問題を解決していく」という過程にあった。
また「わかることと、実際にできること」の違いなどを実感することにもなった。

この経験は大きく、このインターンで経験したこれらは今のリモートワークでも生きていて
パッとみたときに全くどうしたらいいかわからない状況でも問題を分析し解決していけるだけのノウハウを手に入れた。

技術的な部分ではこのあたりから低レイヤーと言われるものに抵抗がなくなり
画像処理の講義でPNGのヘッダーをバイナリから解析したりしていた。

サーバサイド

インターンの後は一層Webサーバサイドに注力していった。
やったことは、PHPでシンプルテンプレートエンジンをつくったりOSSを読んだりしていた
基本的に全てフルスクラッチすることで勉強して、ブログにアウトプットし泥臭く経験を積んでいった。

ここでは主にPHPとJSをつかった。

作ったものはいろいろあるが、特に注力したのは
マストドンの OAuth2 を扱うライブラリを作ったこと。

github.com

これは The PHP League の oauth2-client をベースにしている。
これをつくったあとプルリクを出したらマストドンのドキュメントにある、Mastodon APIを使う上で推奨されるライブラリリストに加えてもらえた

Add oauth2-mastodon to list of libraries by lrf141 · Pull Request #712 · tootsuite/documentation · GitHub

AWS

これはインフィニットループさんのインターンで使うことになった。
というのも、学生が主体になって開発するインターンだったのだがサーバサイドをやっている人間が自分しかいなくて
インフラは誰もいない状況で、構築からする必要があったためである。

流石に物理サーバを用意している時間も無かったので、AWSの無料枠を全力で使い倒していた。
初めてAWSをつかってインフラを構築したがめちゃおもしろかった。

f:id:RabbitFoot141:20181024231454p:plain

やったのはこんな感じに、
マストドンのストリーミングAPIを叩いてコメントを取得しVRコンテンツ内に降らせる処理を作り
VRゲームのスコアを扱うAPIサーバを作り
最後にそれらの色々を Webサービス化した。

APIサーバには Lumen を使って、基本的にインフラはAWS 以外は docker に全部乗っけた。

このあたりでいろんな技術に触れる楽しさに目覚めた。

NodeJS

これは最近やっていることなのだが、サーバサイドをいくらPHPでやったところで
リアルタイム性が求められるものはしんどすぎる、そうなるとサーバサイドで出来ない部分があることになってしまう。
それが嫌で NodeJS を始めた。

ただ作るものが思いつかなかったので、軽量でセキュアな画像プロキシサーバをnode v10.10.0に移行して無駄にhttp2対応させようとしている。

github.com

すごく便利なのに、メンテされていないのと
node → coffeescript と書きなおされている上に node v6対応なのが残念だったので
ちゃんと書きなおして色々と便利にしたい。

というわけで、昨日どうにかES2016版 camo と言われる donuts という画像プロキシを動くところまではもっていけた。
ただ3**リクエストの場合に location が消えるというバグを潰している最中。

github.com

深層学習

これは最近、本当に最近やり始めたばかりなんだけども深層学習をゼミでやっている。
ニューラルネットワークとは何かみたいなところからやっている。

つい最近、keras を使って iris の学習をやったところですごく理解が進んで楽しかった。
家にGPU積んだマシンとか無いので楽しめなさそうだけど、勉強する分には面白いとは思った。

ここまで

序盤、主に高校時代に独学していた部分は本当に広く浅くという感じだが大学以降の経験はうまいこと積めたと持っている。
色んなことに触れてきたけれど、どの経験も無駄というわけではなく問題にぶち当たったとき解決に必要なアプローチを増やしてくれるツールになっている。

技術的には主に PHP と NodeJS を使ってサーバサイドを中心にスキルアップをしている最中となっている。
ここまで分野がばらけた自分が年単位で取り組めているのできっとこれが自分の専門分野になっていくとおもっている。

これから

業務に携わって思ったが、OSSを使う上でバグや仕様で意外と困ることが多い。
そして同じような問題で困っている人もよく見るので、今後は積極的にOSS開発に携わっていきたい。


それと、自分がサーバサイドの分野にハマったようにきっとまだやっていない事でも自分が楽しめる分野が埋もれているはずなので
もっともっといろんな分野や技術に触れていきたい。

そして一番はそういった成長ができる環境に身を置きたい。


以上がけんつの戦歴とこれからの抱負でした

MVCに基づいて設計する時に思う自分なりのベストプラクティス

はじめに

最近、ヴァイオレット・エヴァーガーデンスペシャルをみてダメになったけんつです。

割とよく「MVCってなんだ」とか「MVCを採用している開発でどう切り分けたらいいかわからん」と聞かれる事が多いので
自分なりに考え、「こうなっていると最高に嬉しかった、良かった」というベストプラクティスをまとめてみたくなったのでまとめてみます。

MVCは様々なケースで使われていますが、今回は特にWebサーバサイドに限定して紹介していきます。

MVC とは

特にWebサーバサイドの開発に携わってきた人にとっては古くからお馴染みな Model View Controller という
UIをもつアプリケーションソフトウェアを実装するためのデザインパターン

特徴として、内部で扱うデータをユーザが直接参照・編集する情報から分離する構成を取ることが多い。

そして、これはwikipediaにも書いている。
Model View Controller - Wikipedia

次に、一般的に言われているMVCそれぞれの役割についてまとめる。

Controller の役割

一般的にControllerの役割は、ユーザの入力情報に応じて
ModelとViewを制御する役割を持っている。

Model の役割

一般的にModelは、ビジネスロジックに関するものをもたせることになっている。
ビジネスロジック、つまり何らかの値やそれに関連したアルゴリズムを適用した処理などを指す。

View の役割

一般的にViewは表示や、ユーザの入出力が行われるものを制御する。
特に表示、レンダリングに関する事を行う場合はModelを受け取って行うことが多い。

MVCのメリットとデメリット

メリット

MVCを使うことで、ロジック部分とレンダリング部分、それらを制御する部分という
役割がはっきりとしている状態に分離することができるので開発も効率的に行うことができる。

また、MVCはそれぞれModel View Controllerという分類もされるがModelならModelの中でもわけられていることが多く、それぞれに独立性が高くなるため
仕様が変更されても比較的柔軟に対応できる。

デメリット

プロダクトの規模が大きくなると抱える問題も増えてくる。

例えば、規模が大きくなるとビジネスロジックも多くなってしまうため
モデルがFat Modelになってしまいがちで、管理が大変。

これはControllerにおいても言えることで、必要な機能を増やしていくと
Controllerで行うべき処理も増えてしまうため、Fat Controller になってしまう。
この状況を作ると、ControllerとViewの依存性が始めからある程度強いMVCでは変更が難しくなってしまう。

また、ControllerとView、Modelの依存性が強くなってしまいがちで
修正が困難になってしまう場合がある。

MVCを使うためのベストプラクティス


上記に上げた、概要やメリットとデメリットを考慮した上でいくつかポイントを絞って
自分の思うWebサーバサイドに適用できる、MVCのベストプラクティスについてまとめていく。

Controller の使い方

まず、大事なのがModel View Controller で、特に Controller をどう切り出すか。
つまり、どのような処理を Controller にまとめるかということだと思っている。

これを意識することで、Model と Controller の境界をハッキリさせ必要以上に Model にロジックを集約させてしまうことを防ぐ。

では、どのように分けるのがよいかというと以下の一点を考えるとうまく行く。

  • HTTP に関する処理を Controller でのみ扱い、 Model に含めない。


これに尽きると思っている。

確かにビジネスロジックを Model にまとめるのは重要な話だが、Webサーバサイドにおいて
それらと HTTP では、DBなどから取ってくるという処理や、その結果として現れるデータと
Webサーバサイドの根幹にある通信技術というように、そもそもデータとしての属性が大きく異なる。


そのため、Controller では Cookieやセッションに関する処理、Query String や Json POST で渡ってきたデータのバリデーション、それに伴うエラーハンドリングを行う。


このようにすると、ModelにHTTP関連の処理を負わせないため比較的すっきりとまとまる上に
Controller と Model を確実に分離することができるため必要以上に Controller と Model を依存させないという利点をもたらしてくれる。

イメージは下記の画像の感じで
f:id:RabbitFoot141:20181016193500p:plain

URL設計を見なおしてみる。


これは、「MVCと関係無いのでは?」と思うかもしれないが実は上の Controller にHTTP関連の処理をまとめることにも関連している重要な点だと思っている。


上の記述で、HTTP関連のバリデーションを追加すると辛くなってくるのが
Query String を多用する場合で、そのような場合に Controller で実装するメソッドは規模が大きくなってしまいがちになる。
また、それらを多用するとそもそもにそのクエリがリクエストに存在するかどうかから確かめないと行けない場合もあって冗長になりやすい。


それを未然にルーティングで吸収してしまうことで、必要のないHTTPリクエストの時点でエラーをはいてしまうようなリクエストを Controller で扱うことがなくなる。


特に以下のような場合で有効。

ユーザプロフィールを表示するためのルーティングを前提とした場合

/profile?id=xxx&x=a&y=b  <= 一意に情報が定まるものもQuery Stringにしている


/profile/xxx?x=a&y=b <= 一意に定まるものはURLのパスに含めてしまう

この様に、必要となる一意で定まるものをパスに含めてしまうことで
それが確実に存在するように保証する、そしてそれの形式が異なる場合や存在しない場合は
その先、Controllerなどにリクエストを飛ばさないようにする。

これで必要のないバリデーションを行わないようにする。

Controller と Model でやりとりを増やし過ぎない

Controller はその名前の通り、View と Model の流れを管理しているわけだが
Model が増えてくると Controller で呼び出すべき Model が増えて依存性が高くなってしまう。


こうなってくると、後々の修正や変更が大変になってくる上にMVCのメリットである独立性を維持できていない状況が生まれてしまう。

そのような状況を増やさないためにも、ControllerからViewに渡すべき Model が多いと感じた場合はそもそも Model の設計が適切かどうかを見なおすか

Facade パターンを採用することが解決策として挙げられる。

Facade パターンを採用する

複数のModelを参照する必要がある場合、それらをひとつの処理と見なして Facede パターンを適用するのが得策の場合がある。

f:id:RabbitFoot141:20181016194118p:plain

Facede パターンとは、Modelの動作順を守ったうえでControllerで行っていた処理をひとまとめにしているだけだが
Model を多く呼び出す必要がある場合に Fat Controller になるのを防ぐことができる。

おわりに

MVC を Web サーバサイドに適用する場合の、自分が思うベストプラクティスに関してまとめてみたが
これが必ずしも正しいわけでもなく、あくまでも自分が経験してきた中でこれがよいのではという一つの提案程度に見てもらえると嬉しい

【PHPのお話】PSRって知ってますか?

はじめに

どうも、最近ヴァイオレット・エヴァーガーデンの5話にメンタルブレイクされたけんつです。

最近、さらなるスキルアップを目指してサーバサイドで別の経験を積もうとしている。
それでGoとか調べていると、PHPが盛大にディスられていて悲しい気持ちになっている。

確かにPHPはバグとかを生み出しやすい感じは自分で書いていても思うが、そういう記事ほどPSRについて触れていない。
なので、こういう便利なものが標準であるんだよということを今回は書いていきたい。

PSRとは

PSRとは、PHP Standards Recommendations の事で PHP-FIG(PHP Framework Interop Group = PHPフレームワーク相互運用性グループ)が策定しているPHPコーディング規約のことを指す。
The PHP League とか、PHPで使えるOSSをリリースしているところではPSRを厳守する様に書かれていて、これにはいくつかの種類がある。

現状で承認されているPSRは以下の通り

  • PSR-1 Basic Coding Standard(基本的なコーディング標準
  • PSR-2 Coding Style Guide(コーディングスタイルガイド)
  • PSR-3 Logger Interface(ロガーインタフェース)
  • PSR-4 Autoloader(オートローダー)
  • PSR-6 Caching Interface(キャッシングインターフェイス
  • PSR-7 HTTP Message Interface(HTTPメッセージインターフェイス
  • PSR-11 Container Interface(コンテナインタフェース)
  • PSR-13 Hypermedia Links(ハイパーメディアリンク)
  • PSR-15 HTTP Handlers(HTTPハンドラ)
  • PSR-16 Simple Cache(シンプル・キャッシュ)
  • PSR-17 HTTP Factories(HTTPファクトリー)


これらをざっくりと、ところどころ重要なものはある程度詳細に書いていく。

PSR-1 Basic Coding Standard

自分はあまり意識することが少ないが PSR-1 は最低限準拠することが求められるコーディング規約を示している。
といっても、本当に基本的なことなので次にくる PSR-2 を満たす過程で自動的に満たしている事が多い。

詳しいことは以下のドキュメントを参照して欲しい。

www.php-fig.org

PSR-2 Coding Style Guide

これはPHPで何かOSSを書くときや、その開発に参入するときや業務で使うときなど
ありとあらゆる場合で絶対といってもいいぐらいに使う最高に重要なコーディングおよびコーディングスタイル規約。

公式DocにあるOverviewを一部、紹介すると

  • 一行は80文字以下が望ましい
  • namespace のあとには空行をいれる
  • クラスを宣言している中括弧はclass キーワードを含む行におく必要がある

などなど、結構色々ある。

公式DocにあるPSR-2を満たしたコーディングを以下に引用する。

<?php
namespace Vendor\Package;

use FooInterface;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class Foo extends Bar implements FooInterface
{
    public function sampleMethod($a, $b = null)
    {
        if ($a === $b) {
            bar();
        } elseif ($a > $b) {
            $foo->bar($arg1);
        } else {
            BazClass::bar($arg2, $arg3);
        }
    }

    final public static function bar()
    {
        // method body
    }
}

ただ、こんなこと毎回意識していられないので自動でfixしてくれるツールをいつも使っている。

github.com

基本的にPHPを用いた開発ではcomposerを使うので、requireしたあとはコミットする前に

$ ./vendor/bin/php-cs-fixer fix . --verbose

を走らせる。


これは、多くの PHP 製の OSS で満たされている。

以下が公式Doc

www.php-fig.org

PSR-3 Logger Interface

これも普段あまり意識することがない。
なぜなら、大抵error_log関数にまかせてしまうからだ。

PSR-3 はロギングに関する規約となっている。

これを満たすのはOSSではあまり見たことないので詳しいことは割愛。

以下が公式Doc
www.php-fig.org

PSR-4 Autoloader

これは、最高に重要な規約の一つでファイルパスからクラスをオートロードするための規約。
PSR-0っていう削除予定の規約もあるが、こっちは現役バリバリで使える。

むしろ、これを満たさないと本当にuse を使うのがしんどかったり
composerを使っている場合なら、そもそもオートロードされないとかっていう最高にヤバい問題が起き始める。

  • 完全修飾クラス名は、「ベンダーの名前空間」として知られているトップレベルの名前空間名を持っている必要がある
  • 完全修飾クラス名は、1つ以上のサブ名前空間名を持つことがある
  • 完全修飾クラス名は終端クラス名を持つ必要がある
  • アンダースコアは、完全修飾クラス名のいずれの部分にも特別な意味を持たない。英字は小文字と大文字の任意の組み合わせで構わない。
  • すべてのクラス名は大文字と小文字を区別して参照する必要がある。
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>


例を以下にしめす。

Abraham\TwitterOAuth\TwitterOAuth;

vendor name\(sub namespace)\class name となっているものをよくみる。

composerの設定を書くときもオートロードの設定はこれがベースになっているから
知らないとオートロードではまりがちなポイントが出来上がる。

公式Docは以下の通り
www.php-fig.org

PSR-6 Caching Interface

割と基本的な機能が完成された場合はまず意識することがなくライブラリで自動的に対処されている場合があるのでここもさくっと書く。

PSR-6はキャッシュに関する規約がまとまっている。
キャッシュを扱う際にこれだけは最低限用意して欲しいというメソッドが固まった interface を指す。

公式Docは以下

www.php-fig.org

PSR-7 HTTP Message Interface


これは、色々なプロダクトを触るのに最高に重要なHTTPのリクエスト、レスポンスに関する規約。
HTTPリクエスト、レスポンスを扱う上で絶対にこれだけは用意してくれっていう関数群がinterfaceとなっている。

これは、Laravelでも標準のリクエスト、レスポンスクラスをPSR-7のInterfaceに置き換えることが出来たりするぐらいWebフレームワークでは対応していることが多い。

あと、HTTPが絡んだOSSをテストする際にこれらのInterfaceをモックする場合もある。

書いていることはそんな大した量じゃないが最高に重要な役割を持つ規約の一つである。


これがベースになっていることが多い

github.com


公式Docは以下
www.php-fig.org

PSR-11 Container Interface

これは、あまり注目されていないけど実は重要でOSSではよく使われている。
というのもPSR-11はDIコンテナに関する規約がまとまっているからだ。

これを満たしている、利用しているPHP製のOSSは意外と多い。

うだうだ語るよりも以下の記事を見れば大体わかる。

rabbitfoot141.hatenablog.com

ルーティングやバッチ系のライブラリで使われているのをよく見る。

公式Docは以下
www.php-fig.org



PSR-13、普段はめったに考えることないけどハイパーメディアリンクに関する規約。
これもハイパーメディアリンクを扱う上で必要な関数群を定義しているがこのあたりのライブラリを見たり実装をすることがないから本当にわからない

なので公式Docだけ晒しておく
www.php-fig.org

PSR-15 HTTP Handlers


これは、PSR-7と関連して満たされる場合が多いためさくっとまとめる。
PSR-15はHTTPハンドラーの規約になっている。

HTTPハンドラーと言っても、リクエストハンドラとミドルウェアに関するコンポーネントが含まれている。

が、これを自前で実装することはなくLaravelなどのFWで最初から組み込まれていることが多い。

それなりに重要なはずなんだけど、よく見えないところに隠されていることが多いため
意識しなくても使っている場合がある

以下が公式Doc
www.php-fig.org

PSR-16 Simple Cache

これはキャッシュに関する規約になっている。
キャッシュ系のライブラリを作ることは少ないと思うが使う際には、この規約にあるインターフェースを利用することになる。

中にはメソッドからキャッシュとして保持する必要のあるデータ型などが書いてある。

以下が公式Doc
www.php-fig.org

PSR-17 HTTP Factories

PSR-17はHTTPファクトリ、つまりPSR-7準拠のHTTPオブジェクトの作成方法についての規約となる。
これができるまでは、HTTPメッセージに関しての規約(PSR-7)しかなく、そのオブジェクトについてはバラバラだったため
それらの規格を統一するためにある。
これも同様にインターフェースなどが定められている。

こういった部分はよくFWの内部に埋もれがちになってしまっている。

以下が公式Doc
www.php-fig.org

おわりに


途中、FWに隠れがちで実例を示すのが難しい部分はかなり説明を端折ってきたが
特に有名なOSSやプロダクトの裏ではこのように制定された規約の上に成り立っていることが多く
これらを満たすことで、快適に開発することができる。

第10回LOCAL学生部総大会をやってきました

はじめに

どうも、よく訓練されたPHPerのけんつです。
開催自体は9月最後の週だったけど、そのあと仕事なり集中講義なり新学期なりで
過去最高にバタバタしたので今書く。

今年も総大会をやった。
第9回も同人誌執筆合宿とか最高に頭のわるいことをしたけど、今回はもっとヤバく
「石狩データセンターに泊まってもくもく会をする」とかっていうもっと頭の悪いことをしてみた。

それらをいい感じにまとめて書いてみる。


総大会中の部員たちのツイート集もある↓
togetter.com

1日目

この日は、さくらインターネットさんの石狩データセンタに現地集合となった。
諸々の入館手続きを済ませた後は、石狩DCの諸々を見学した。
最高に胸熱なものを色々と見れて、「まじかぁ」って感じでずっと語彙力を失っていた。

それが終わったら、参加したみんなでもくもく会を始めた。
今回は最高に人数が多くて、出身も札幌や室蘭、函館、旭川、苫小牧などなどとバラバラだったので各地の何かをおみやげとして持ち込んだ。

これらを食しながら大画面で映画見ながらサーバプログラムを組んだり
机を大きく占領して、電子工作を始める輩がいたり
ハンモックを独占してrailsを書く女子達がいたり
マッサージチェアを独占してrailsやったりnode書いたりしている自分がいたりと
これもまた色々な部員がいて面白かった。

そんな、こんなで毎回総大会本番と呼ばれる1日目夜がやってたきた。

夜は夜で、大画面で映画見ていたはずがスプラトゥーンがいきなり展開されていたり
徹夜電子工作勢がいたり、車運転勢は早々に眠っていたり
色々とやっている分野がバラバラなだけあって、みんなワイワイしながら進捗を出していた。

2日目

夜に、APIサーバの構築を手伝って寝て朝起きると
何故かしりとりをしている連中がいたので、一緒に情報系縛りしりとりをしてみんなが起きるのを待っていた。


退館した後は男子達は温泉へと向かった。
その後は、いつもお世話になっているインフィニットループさんに会議室をお借りして
LT大会となった。

お題は何でもオッケーにしたので、見るべきでないサメ映画について
悲劇の夏休み、コントリビュートした話、総大会での進捗などなど色々とあった


そんなこんなで、あっという間に楽しかった二日間も過ぎてしまった。
メンバーも増え、大勢でワイワイできるようになったので色々と今後もやっていきたい。

おわりに

総大会でこっそり書いてた画像プロキシを完成させたい。

そろそろ同人誌第二版を書きたい。
そして次の総大会は何しようか。

oEmbed を叩いてメディア共有サイトのコンテンツ情報を簡単に取得する

はじめに

どうも、よく訓練された PHPer のけんつです。
突然ですが、みなさん YoutubeInstagram などのメディア共有サイトの情報で特にコンテンツの埋め込みに必要な情報だけ欲しいときに
わざわざデベロッパー登録して、コンシューマーキー等を取得してAPIを叩くのって面倒じゃね?と思うことありませんか?

今日はそんなあれこれを手軽に便利にしてしまう「oEmbed」というものについて書こうと思います。

oEmbed とは

oEembed については以下のサイトがドキュメントを公開している。

https://oembed.com/

じゃあ、oEmbed とは何かというと「コンテンツ共有サイトの情報を埋め込み可能な形式で提供するAPI」のことを指していて。
これは開発者登録等をしなくとも使用することができるためコンテンツ情報だけを使いたい時に非常に便利なもの。

この後、詳しい規格について記述していこうと思いますが「そんなんしらん、使えればok」という人は以下の例を見てください。

Quick Start

今回は Youtube を例に説明していく。
www.youtube.com
上記の動画を題材にする。

まず、oEmbed で必要になるのはそのコンテンツのURL。
今回で言えば

https://www.youtube.com/watch?v=SX_ViT4Ra7k


これ。
youtube の url に /watch というルーティングがあり、 クエリストリングで動画IDが付与されているこれ。


コンテンツのURLがわかれば次にやることは youtube の oEmbed エンドポイントにリクエストを投げるだけ。
Youtube の oEmbed エンドポイントは

https://www.youtube.com/oembed

↑これ。

それのエンドポイントに url 情報と format 形式をクエリストリングで指定してリクエストを送ると json が返ってくる。
※本当はクエリストリングの部分は url encode が必要。

https://www.youtube.com/oembed?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DSX_ViT4Ra7k&format=json

{
	"width": 480,
	"version": "1.0",
	"author_url": "https://www.youtube.com/user/08yakari",
	"provider_name": "YouTube",
	"type": "video",
	"provider_url": "https://www.youtube.com/",
	"html": "<iframe width=\"480\" height=\"270\" src=\"https://www.youtube.com/embed/SX_ViT4Ra7k?feature=oembed\" frameborder=\"0\" allow=\"autoplay; encrypted-media\" allowfullscreen></iframe>",
	"thumbnail_url": "https://i.ytimg.com/vi/SX_ViT4Ra7k/hqdefault.jpg",
	"thumbnail_width": 480,
	"title": "米津玄師  MV「Lemon」",
	"thumbnail_height": 360,
	"author_name": "米津玄師",
	"height": 270
}

あとは json を煮るなり焼くなり好きにするといい感じにできる。
ひとつ言うなら、 html メンバーの値は直接埋め込み可能な iframe 形式となっている。
なんならサムネイルのURLもついてきているのでスゴク便利。

oEmbed の規格

使うだけならあれでいいんだけど、それじゃ気がすまねぇ!!っていう人のためにしっかりと規格についてもまとめていきたい。

Consumer Request


oEmbed のエンドポイントに送るリクエストには必要なパラメータと形式が存在する。

まず大前提としてリクエストはエンドポイントに対してGETリクエストである必要があり、全てのパラメータは RFC 1738 に準拠した形式で URL Encode された状態で無いといけない。

その大前提の元に以下のパラメータをリクエストに含めることができる。

  • url (require)

これは情報を取得したいコンテンツのURL。

  • maxwidth (optional)

埋め込みリソースの最大幅。一部のリソースタイプ(以下に記載)にのみ適用される

  • maxheigth (optional)

埋め込みリソースの最大高さ。一部のリソースタイプ(以下に記載)にのみ適用される。

  • format (optional)

レスポンス形式を指定する。

この中で max*** 系 はあまり使わないが、注意が必要なものが format になる。
これは大抵 json or xml を指定するが存在しないものを選択するとエラーが返ってくる。
なので、どちらかを指定してリクエストを投げるのが良い。

Provider Response

ここからはレスポンスに関すること。

Response Format

これは大抵、 jsonxml になっている。
どちらかしかサポートしてないサービスも結構あるが、今回は json についてのみ書いていく。

Response parameters

json に含まれるパラメータについて。

  • type (required)

コンテンツの種別を表す、video, image などがある

  • version (required)

oEmbed のバージョンを表す、今は1.0

  • title (optional)

コンテンツのタイトル

  • author_name (optional)

コンテンツの作者等。

  • author_url (optional)

コンテンツの作者のURL

  • provider_name (optional)

コンテンツを提供しているヤツの名前、youtube などサービス名がおおい

  • provider_url (optional)

これもコンテンツを提供しているサービスのURL

  • cache_age (optional)

推奨されるコンテンツのキャッシュ有効期間。

  • thumbnail_url (optional)

サムネイルを管理しているURL

  • thumbnail_width (optional)

サムネイルのサイズ

  • thumbnail_height (optional)

サムネイルのサイズ


この中で特に注意が必要なのが type である。
これによって更に require なパラメータが増えたり、iframe の埋め込みコードがあったりなかったりしてしまう。

Errors

渡したurlパラメータの値となるurlにコンテンツが無い場合に返る

レスポンスを要求された形式で返せない場合に返る。

  • 401 Unauthorized

指定されたURLには、プライベート(非公開)リソースが含まれている場合に返る。

Security considerations

oEmbed 対応のAPIを提供する場合に対応するURLについて。
URL スキームを http, https, mailto などに限定しないと、javascript:... などXSSの攻撃に利用されてしまう点に注意

おわりに

oEmbed は、コンテンツ情報をひっぱってくる際にかなり便利なのでぜひ使っていきたい。
ただ、コンテンツの説明などは手に入らない場合があるのでそういうときは黙って開発者登録などしてAPIを使うしかない。