Technote

by sizuhiko

Polymer.co-edo day 2019 春を開催しました

2019年4回目となる Polymer.co-edo ミートアップ を開催しました。

edo-blogcardに最初のコードをアップしました。 developブランチにpushしています。

まだ何も動きはないのですが、LitLoader を使ってビルドするところまで実装しています。

ES Module import との戦い

Web Components はHTMLファイルからだと以下のように読み込みます。

<script type="module" src="node_modules/xx-element/xx-element.js"></script>

独自のカスタムエレメントから別のエレメントを利用する場合は、以下のように記述します。

import '@polymer/iron-ajax/iron-ajax.js';

ここでブラウザは @polymer/iron-ajax/iron-ajax.js のようなパスを解決できないため、webpackでコンパイルすることが必要になります。 この話は、以前の記事PHPer Kaigi 2019に参加しましたでも書いているとおり、import maps待ちな状況です。

2つのビルド結果

そこで、2つのビルド結果を保持するようにしました。

/dist/edo-blogcard.js

これは

import '@edo-elements/edo-blogcard/dist/edo-blogcard.js';

のように利用するためのビルド結果で、LitLoader形式で記述された .lit ファイルを .js にコンパイルしたものです。

/dist/edo-blogcard.bundle.js

これは

<script type="module" src="node_modules/@edo-elements/edo-blogcard/dist/edo-blogcard.bundle.js"></script>

のように利用するためのビルド結果で、HTMLからブログパーツとして利用したい(webpackなどを実行しないWebページで使いたい)場合のためにコンパイルしたものです。 LitLoaderとwebpackを使ってHTMLから利用可能な状態になっています。

工夫したところ

後者の bundle.js については、普通にLitLoaderとwebpackを使うやり方なので、特にはありません。 LitLoaderはwebpackのloaderとして実装されているので、configに設定を追加するだけです。

一方前者はwebpackのloaderようにできているLitLoaderを、webpack使わずにどうやって呼び出すか?というところを工夫しました。

それが build.js です。 そして loader-runner という webpack のモジュールを使っています。 このモジュールは Runs (webpack) loaders という説明のとおり、loaderを実行することに特化しています。

const fs = require("fs");
const path = require("path");
const { runLoaders } = require("loader-runner");

runLoaders(
  {
    resource: path.resolve(__dirname, "edo-blogcard.lit"),
    loaders: [require.resolve("lit-loader")]
  },
  function(err, result) {
    if (err) throw err;
    fs.writeFileSync("dist/edo-blogcard.js", result.result[0]);
  }
);

これだけで .lit ファイルを .js ファイルに(webpackなしで)コンパイルできます。

次回

通常どおりの、もくもく会として開催予定しています。 皆様の参加をお待ちしております。

Doorkeeperのコミュニティページ

PSR-17 HTTP Factories のライブラリ

令和最初の記事も、PHP。 この記事はPSR-15リクエストハンドラーのライブラリ比較の続編です。

前回のサンプルプログラムでは zendframework/zend-diactorosHTTP Message Factories を使って PSR-7のHTTPレスポンス(Psr\Http\Message\ResponseInterface)を作っていました。

今回は、他のライブラリを使っても大丈夫か考えてみたいと思います。

PHP-17 の HTTP Factories に対応したライブラリ

PackagistでPSR-17を検索してみました。 PSR-15ほどはないようです。ということで独断と偏見(ダウンロード数が多いもの)を選んで比較してみたいと思います。 なお「フレームワーク」と書いてあるものや、特定のPSR-7ライブラリ だけ に依存したもの(たとえばhttp-factory-guzzleなど)は除外しています。

それぞれのライブラリの説明を読んで気づくでしょう。

PSR-17 HTTP-Factory with auto-discovery support

そう先ほど「特定のPSR-7ライブラリ だけ に依存したもの」と書きましたが、実は特定のライブラリに依存します。 それはそうですよね。PSR-7のHTTPレスポンスインターフェースの実装インスタンスを 生成 しないといけないので、何かしらに依存します。

ただ、こういった auto-discovery してくれるライブラリを使っておくと、PSR-7のライブラリを変更したときも影響が少ないですね。 でもそれ、さっきのサンプルで DI してたから、それで良くない?というのは、まさにそのとおりです。

で、結局どうすると良いの?

  • PSR-7/PSR-11/PSR-15/PSR-17 を使うアーキテクチャにするなら、FactoryInterfaceをDIすれば影響範囲は設定箇所だけになります
  • PSR-7/PSR-15/PSR-17 を使う(PSR-11を使わない)アーキテクチャにするなら、上記のような PSR-7 を自動判定してくれるライブラリを使うと良い

ということになると思います。

それならPSR-7のライブラリも使い分けたサンプルが見たかった!とかあるかもしれませんが、それはまた今度!(機会があれば)

おまけ

さて、PSR-7/PSR-15/PSR-17 の話は面白かったですか? これに加えて、フロントエンドも含めてフレームワークに依存しないアプリケーションとは? みたいな話をPHPカンファレンス福岡2019でします。 午後一のセッション。また @soudai1025 の裏になりましたが、私の方がマニアックな話なのかな。

では6月に福岡でお会いしましょう!

PSR-15リクエストハンドラーのライブラリ比較

平成最後の記事は、PHP。 久々のPHP記事ですが、PSR-15に準拠したリクエストハンドラーライブラリの実装を比較してみようと思います。

PSR-15とは?

HTTPメッセージを使用するリクエストハンドラーと、ミドルウェアコンポーネントの共通インターフェースです。

それPSR-7なのでは?という鋭い人は、 @tanakahisateruさんのスライド PHP-FIGのHTTP処理標準の設計はなぜPSR-7/15/17になったのかを参照ください。

PSR-7については、だいぶ知れ渡っていると思うので、PSR-15のリクエストハンドラーに着目していきます。

PHP-15のリクエストハンドラーに対応したライブラリ

PackagistでPSR-15を検索してみました。 たくさんありますね。ということで独断と偏見(ダウンロード数が多いもの)を選んで比較してみたいと思います。 なお「フレームワーク」と書いてあるものは除外しています。 例えば zend-expressiveminimalist PSR-7 middleware framework for PHP と書いてあるとおりなので。 Slimなどのマイクロフレームワークも同様です。

の4つを使ってサンプルプログラム集を作ってみました。

sizuhiko/psr15-requesthandler-examples

PSR-7

まず前提事項として、PSR-7対応のライブラリが必要です。 本サンプルでは、主に zendframework/zend-diactoros を使っています。 sunrise/http-routerの例ついては、同系統にsunrise/http-messagesunrise-php/http-server-requestがあるので、それを利用しています。 PSR-7のライブラリが違うと、どのような記述の違いがあるのかわかりやすいでしょう。

サンプルの内容

サンプルプログラム集clone するか、ダウンロードして、READMEの手順に従ってください。

ドキュメントルート(/)にアクセスすると、Hello, World! 出すだけの簡単なものです。

サンプルコードの流れ

で、結局PSR-15とは?みたいな話になるわけですが、どの例も以下のような流れで動いています

  • PSR-7 でHTTPリクエスト(Psr\Http\Message\RequestInterface)を受け取る
  • PSR-15の Psr\Http\Server\MiddlewareInterfacePsr\Http\Server\RequestHandlerInterface でいい感じに PSR-7のHTTPレスポンス(Psr\Http\Message\ResponseInterface) を生成する。
  • PSR-7 のHTTPレスポンスをエミットする

つまり、真ん中の部分を処理することになります。

リクエストハンドラ

サンプルのsrcフォルダの下には

  • Middlewares
  • RequestHandlers

があり、それぞれ Psr\Http\Server\MiddlewareInterfacePsr\Http\Server\RequestHandlerInterface を実装したクラスが置いてあります。 で、それぞれのリクエストハンドラーからは、適したハンドラが呼び出されるようになっています。

このサンプルでは PSR-17 (HTTP Factories) を先行導入し、PSR-11 (Container Interface) に対応したライブラリでDIしてますが、 そのあたりは、次回以降で解説します。

ミドルウエアもリクエストハンドラも同じようにHello, World!をレスポンスボディに入れるだけです。

このサンプルをとおして言いたかったこと

このサンプルでは、ドキュメントルートだけの簡単な実装でしたが、ルーターの設定を追加するだけで、 実際のアプリケーションでも使えるようになることがわかると思います。

もちろん実際にはDBにアクセスする必要もあったりするわけですが、そこにはPHP Data Objects(PDO)があり、 実際のDBの差分を吸収してくれます。 そこで利用するSQLは標準であり、Webの標準よりも長い期間利用されています。

フレームワークを利用してアプリケーションを作ることは、速く簡単に構築できます。 しかしフレームワークに依存せずPHPの標準を使うことで、柔軟にライブラリを変えても、自分のビジネスロジックを流用することができるようになります。

もちろん今日のフレームワークの多くが PSR 対応を進めていますので、フレームワークを使っていても、より互換性が高いアプリケーションを作ることができるようになるかもしれません。

次回はPSR-17について、いくつかのライブラリを紹介します。

アスカン2019を開催しました

明日の開発カンファレンス2019の中の人として運営参加しました。

私はプログラム枠1つを決めたり、事前準備をお手伝いしたり、当日雑用したりと、まぁ大したことはしてません(汗

私の渾身のオススメ枠、ということで今回は弊社岡島から「総売り上げ:35,400円 ~ 受託エンジニアが自社サービスのPOをやって学んだこと。」という発表をしてもらいました。 直前のセッションが、ヴェルク株式会社 田向 祐介さまの「受託開発の会社が自社サービスを立ち上げて軌道に乗せるまでの取り組み」だったこともあり、受託開発会社のサービス開発成功事例からの、失敗事例ということで、プログラムの流れも良かったと思います。

また今回のアスカンでは、はじめての2トラックということで、たくさんの事例をご紹介することができたと思います。

当日の様子は、Togetterにまとまっています。

@akiko_pusuさん、@NEKOGETさんが、グラレコを投稿してくれていますので、よりトーク内容含めてふりかえってもらえると思います。

昨年は秋にも開催したアスカンですが、今年はどうなるでしょうか?

そうそう、最後にアンケートもありますので、参加したみなさま、スピーカーやスタッフのためにも回答をお願いしたします。

Toilet EvolutionのフロントエンドをPolymer3対応する(4)

この記事はToilet EvolutionのフロントエンドをPolymer3対応する(3)の続編です。

前回までの対応で、ページをロードしたときに、ブラウザのコンソールにエラーが出ることが少なくなりました。 ただし多くのページで表示が崩れていたり、うまく表示できないエレメントがあります。 これは今回の ES Modules に対応してなかったエレメントに関連するところだったのですが、これらの対応を解説していきます。

dom-module has style outside template というワーニングが出る

ブラウザのコンソールを確認していると dom-module has style outside template といったワーニングが表示されます。 これは <dom-module> の中で <template><style> が並列に記述されている場合に発生します。

なので、すべて <template> の子要素に <style> を移動しました(コミット)。

動かすようにするための微調整

続いて1つずつの修正はそれぞれ大きくないものの、動作するまでの調整をたくさん実施しました(コミット)。

クリック処理が動かない

これは1から2の段階で廃止になった listeners を使っていたためで、利用箇所を addEventListener や、 on-tap, on-click に変更しました。

moment でロケールを利用しようとするとエラーになる

Toilet Evolution では、トイレの利用状況を表示するときに 10分前 のような表示をしているのですが、これに moment.js の機能(toDate())を使っています。 moment だけ(ロケールを使わない)なら npm でインストールして

import moment from 'moment';

と記述すればよかった(動いた)のですが、どうしてもロケールの解決がうまくいかない(Webpackするとエラーになる)ので、今回はあきらめてHTML側でグローバルに利用できる形にしました。

  <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/locale/ja.js"></script>

いくつかWebpackとmomentのissueも見つけて解決策もありそうだったのですが、とりあえず困らないのでこの方法で。

style や slot が解決されない

modulizerで自動変換された <google-map-marker> エレメントは以下のようになっていました。

const $_documentContainer = document.createElement('template');

$_documentContainer.innerHTML = `<dom-module id="google-map-marker">
  <style>
    :host {
      display: none;
    }
  </style>
  <template><slot></slot></template>
</dom-module>`;

document.head.appendChild($_documentContainer.content);

もとのHTMLでの記述は以下のようになっていました。

<dom-module id="google-map-marker">
  <template>
    <style>
      :host {
        display: none;
      }
    </style>

    <slot></slot>
  </template>
  <script>

自分が作ったカスタムエレメントでは、 Polymer_templatehtml を使うように変換されていたのですが、bowerでインストールしたコンポーネントの多くは上記のように変換されていました。これを html を使うようにしてみたところ解決しました。

Polymer({

  _template: html`
  <style>
    :host {
      display: none;
    }
  </style>
  <slot></slot>
`,

<google-map> エレメントも同様にうまく動作しなかったので、 html をつけて _template で返却するように対応しました。

さいごに

4回にわたって、Polymer1のプロジェクトをPolymer3に移行した解説をしてきました。 この連載がPolymer1からアップグレードする他の方の手助けになれば幸いです。

基本的な部分は、ほとんどmodulizerで変換できるので、これは便利だなーと思うのでした。 Polymer3対応した依存エレメントは、どこかのタイミングでPRを出してフィードバックしたいなと思っています。

では良いWeb Componentsライフを!