CSSプロパティの「aspect-ratio」について(aspect-ratioその2)

cssプロパティの aspect-ratio という、とても便利なものを最近見つけました。
大体のブラウザで使えます(詳細はこちら)。

使い方を実際に書いて調べてみます。

CSSメディアクエリの「aspect-ratio」やアスペクト比については(その1)にまとめました。
aspect-ratio」についてもそちらで調べています。

cssプロパティの 「aspect-ratio」

参考記事:

CSS aspect-ratioプロパティの基礎知識、便利な使い方、実装に必要なプログレッシブエンハンスメント | コリス

以前レイアウトシフト(CLS)の対策を考えた記事や、レスポンシブ対応の画像の表示についてまとめた記事を書きました。

aspect-ratio を使うと、これらの書き方がさらに簡単になります。
一言でいうと「要素にアスペクト比を指定する」です。

書き方

aspect-ratio: 幅 / 高さ

です。

値は計算したものでも書けます。 縦:横が1:1や1:2の場合など簡単なものであれば、aspect-ratio: 1 aspect-ratio: 0.5 と書いてもアスペクト比が分かりますね。
計算してアスペクト比が分かりにくくなってしまうのであれば「幅 / 高さ」のままが良さそうです。

プロパティが効く要素

MDNには

インラインボックスおよび内部のルビまたは表ボックスを除くすべての要素

とありました。

widthheight が指定できる要素に使えます。

実際に書いてみる

理解を深めるため実際にコードを書いてみます。

See the Pen aspect-raito検証 by yokoyoko (@yokoyoko_code) on CodePen.

aspect-ratiowidth or heightの検証

aspect-ratiowidth」または「aspect-ratioheight」を指定した場合、どのような表示になるかを検証しました。

width height 未指定の場合 → width: 100% を基準に指定したアスペクト比の高さをとる。
width または height を指定した場合 → そのwidth または height を基準に高さ、幅をとる。

☆ちなみに widthheight を両方指定すると、 aspect-ratio の指定は無視されるようです。

aspect-ratioobject-fit を使った画像の表示

記事などのサムネイルをレスポンシブで並べるケースが多いと思います。

指定した比率で拡大縮小(aspect-ratio)と、
指定した範囲内で画像を表示(object-fit)
が同時にできるのはいいですね。

サンプル画像

縦長画像と横長画像、春っぽい画像にしました。メジロ

f:id:yokoyoko_115:20220306005545j:plain f:id:yokoyoko_115:20220306005600j:plain

アスペクト比の異なる画像を同じ大きさで表示させるのに便利な object-fit は、以下の条件で使えます。

  • img要素などの置換要素
  • 置換要素に width height を指定する(auto 以外)

参考記事:

置換要素 - CSS: カスケーディングスタイルシート | MDN

CSS – object-fitが効かない場合

なのでここでは object-fit を設定したimg要素に対して、

  • aspect-ratio も一緒に指定する
  • img要素の親要素に aspect-ratio を指定する

どちらが良いのか検証します。

ちなみに両方ともアスペクト比 1:1 で、object-fit: cover を指定しました。

***

結果、どちらでもうまく表示されました。
メジロの位置も、中央でトリミングされている表示になっています。

前者でも問題ないというのが意外でした。

レイアウトシフト対策にaspect-ratioが有効

レイアウトシフトについて書いた記事から、新しいことが分かりました!

<img src="image.jpg" alt="hoge" width="400" height="300">

と書けば、400x300pxの表示領域を画像読み込み前に確保しているのかと思いきや、
今は進化して width height の属性値からアスペクト比を出しているのです。

Cumulative Layout Shift を最適化する

そしてすべてのブラウザーUA スタイルシートは、要素に設定されている既存の width と height 属性に基づいてデフォルトのアスペクト比を追加します。

width、height属性値はピクセル値としてはもはや見られていないのでしょうね。
(%付きの値もなのでしょうか)

img {
  aspect-ratio: attr(width) / attr(height);
}

アスペクト比は、画像が読み込まれる前に width と height 属性に基づいて計算されます。この情報は、レイアウト計算の初期段階で供給されます。画像の幅を特定の値 (たとえば width: 100%) に設定するように指示があると、アスペクト比はすぐに高さの計算に使用されます。

画像が読み込まれる前、早い段階でcssの処理があって、表示領域が確保されるのですね。

UA スタイルシート側でこのような指定があったとは知りませんでした。

こちらの書き方は、読み込んだ画像のアスペクト比で表示させる場合に有効です。

object-fit を使うような、画像の一部分を表示させたりトリミングする場合は、Codepenで書いた方法でいいのだと思いました。

つまりレイアウトシフトの対策方法は

  • 画像を全表示の場合:
    img要素にwidth、height属性を指定(アスペクト比でも元画像のサイズでもOK)+ width: 100%; height: auto; などで縦・横いずれかの大きさを指定する
  • 画像を一部分表示(トリミングする場合):
    aspect-ratioアスペクト比を指定 + 置換要素(img要素など)に object-fitwidthheight を指定(auto 以外)

になると思います。

場合分けできてスッキリしました。

aspect-ratio ありがたいです!

さいごに

ずっと「aspect-raito」だと思っていました。 直接入力したおかげで気付けました。

「rate(レート、率)」と混同しました。

英語難しい。

おしまい。

CSSメディアクエリの「aspect-ratio」について(aspect-ratioその1)

メディアクエリの「aspect-ratio」を使う機会に遭遇しました。

動画をスクロールなしの1画面に収めたい場面です。

そこではメディアクエリで aspect-ratio が使われていました。

使い方はなんとなく意味は分かりそうですがなんとなくでしか分からない。

そもそもの「メディアクエリ」という言葉から「aspect ratio」という意味、書き方まで調べてまとめてみました。

CSSプロパティの aspect-ratio については(その2)にまとめました。

aspect ratioとは

日本語だと「縦横比(アスペクト比)」です。

Wikipediaには

アスペクト比は、矩形における長辺と短辺の比率。

とありました。

このアスペクト比長辺:短辺で書かれるのであれば、
縦x横のサイズが横長の400x300pxでも、縦長の300x400pxの場合、アスペクト比4:3となります。

縦長にしてよく使うスマホ画面のアスペクト比の表記が気になっていましたが、調べてみると「16:9」だったり「9:16」だったり記事によってバラバラでした。

また、アスペクト比の中で小数がでてきたりします(黄金比1: 1.618…など)。
「6:5」は5で割って「1.2:1」と小さくしたほうがいいのか?さらには小数が先に来るのは分かりにくいから「1:1.2」?

もしかしたら中学校で習ったのかもしれないですね。

...気になりましたがいきなり脱線です。大事なのはcssでのアスペクト比なのでここまでにします。

メディアクエリの「aspect-ratio」

まずはメディアクエリについてです。

メディアクエリとは

こちらが一番確かな情報だと思います(2022年3月現在)。
※日本語訳ページは非公式だそうです。

Media Queries Level 5 (日本語訳)

Media Queries。直訳すると「媒体の問いかけ」です。

メディアクエリー - CSS: カスケーディングスタイルシート | MDN

メディアクエリーによって、サイトやアプリを様々な引数や端末の特性に基づいて合わせることができます。

CSS では、 @media アットルールを使用して、メディアクエリーの結果に基づいて条件付きでスタイルの一部を適用することができます。 @import を使用すると、条件付きでスタイルシート全体を適用することもできます。

この端末が「メディア」、引数特性が「クエリ」を指しているように見えました。

また、アットルールというのは他にも @import@charset などいろいろあります。
cssのもろもろの設定・指定・定義に使われます。

***

メディアクエリについて、詳しく書かれている記事がありました。

メディアクエリについて記述方法などまとめ - キーワードファインダー

こちらと「Media Queries Level 5」の日本語訳ページをもとに、覚えておきたい部分をまとめます。

メディアクエリの書き方

方法は2つです。

  • htmlファイルの中でlink要素のmedia属性値として書く
  • cssファイルの中で @media の後ろに書く

スマホ / PCで読み込むcssファイルを分ける場合は前者を使ったりします。

次にcssファイルの中に書く方法のメディアクエリを見てみます。

f:id:yokoyoko_115:20220110020847j:plain

メディアタイプ

メディア(媒体、端末)の種類です。

  • all:全てのメディア
  • print:プリンター、印刷プレビューのWebブラウザ
  • screen:モニター、ディスプレイ(print以外のメディア)

Media Queries Level 5ではこの3つ以外は非推奨でした。次第にこのメディアタイプはなくなる流れのようです。

また、メディアタイプが省略されている場合は all が使われているようです。

Note: 多くの場合、 all メディア種別が他のタイプが指定されない場合に既定で使用されます。

※後述する not only 理論演算子を使う場合は、メディアタイプの省略ができません。

論理演算子

複数の条件をつなげたメディアクエリを作るときに使います。

  • and:「かつ」
  • or:「または」
  • not:「以外」。メディアクエリ全体のみを反転
  • only:「古いブラウザで適用しない」→「古くないブラウザで適用する」

また、カンマ区切り「,」のメディアクエリのリストにすると、「または」という意味になります。

例えば、以下のルールはユーザーの端末の高さが 680px 以上または画面が縦長モードであるときにスタイルが適用されます。

@media (min-height: 680px), screen and (orientation: portrait) { ... }

☆スタイルをディスプレイ画面に限定する場合は screen and と付けます。よく使われる書き方っぽいです。

日本語でメディアクエリを考える

書き方の例を見て分かるような分からないような感じになりましたが、日本語にするとしっくりきました。

andor などの理論演算子をつなげてできるのは1つのメディアクエリです。

「@media (min-width: 30em) and (orientation: landscape)」は、
「@media メディアクエリ」と考えられます。

とすると、
メディアクエリのリストは「@media メディアクエリ1, メディアクエリ2...」と書けます。
意味は「メディアクエリ1またはメディアクエリ2」です。

not

そこで notについて考えてみます。

まずは , でつながれたリストと not の関係です。

「@media not メディアクエリ1, メディアクエリ2」とあれば、
意味は「メディアクエリ1以外、またはメディアクエリ2」となります。

メディアクエリ1の中で not が使われていたら、メディアクエリ1内のそれぞれの条件を反転します。

なるほどと思っていたら単純な作りではないようです。

次はandnot を両方使う場合の解釈をみてみます。

CSSのメディアクエリを使ってレスポンシブに対応させる書き方|たかもそ/Web Creator.|note

実はメディア特性がある場合、not 演算子はメディア特性の方を否定し、メディアタイプは無視します。

@media not screen and (prefers-color-scheme: dark) { ... }
これは「デバイスがダークモードでない」となります。
「メディアタイプがスクリーン以外で、かつ...」とはならないそうです。

下記のように書き換える事ができるそうです。
@media screen and (not (prefers-color-scheme: dark)) { ... }

not は「メディアクエリ内のそれぞれの条件を反転します」が、 メディアタイプ screen の否定は無視されます。

ややこしい...。

only

only についても調べました。

CSSでメディアクエリ(Media Queries)の基本的な書き方、記述の意味を理解し、「何となく使う」を卒業する。 | WEMO

「only screen and (max-width: 600px)」は旧来のUAでは「only」の部分しか読み込まれません。 つまり、「メディアタイプが only のデバイス」として解釈します。 しかし、「only」に該当するメディアタイプは存在しないので、旧来のUAにおいては全てのデバイスでそのメディアクエリが無視される...というわけです。 ただ、onlyキーワードが必要になることはほぼ無いでしょう。

古いブラウザ(ユーザーエージェント)は、頭の only を読み込んで適切なクエリではないと判断して、only以下を無視していたのですね。

only は気にしなくてよさそうです。

メディア特性

メディア特性は () で囲って使います。

メディア特性の値は : の後ろに書きます。

調べると結構な数でした。
先ほどのnotの部分の引用記事からまとめました。
17あって心が折れたので一部抜粋です。

メディア特性
[min-/max-]width ビューポート(画面)の横幅
[min-/max-]height ビューポートの高さ
orientation バイスの向き。portrait (縦長)/ landscape(横長)
[min-/max-]resolution バイスピクセルの解像度。単位は dpi, dpcm, dppx, x
-webkit-[min-/max-]device-pixel-ratio SafariiOS Safari の、デバイスピクセル解像度。単位はdppx(省略して書く)
prefers-color-scheme light(ライトモード)/ dark(ダークモード)
prefers-reduced-motion アニメーションの動き。no-preference(設定無し)/ reduce(最小限にする)
hover ホバー機能が使えるデバイスかどうか。hover(つかえる)/ none(使えない)
[min-/max-]aspect-ratio ビューポートの幅と高さのアスペクト比 <幅>/<高さ> のように指定する
[min-/max-]color 色成分のビット数。値なしはデバイスが白黒でないとき

aspect-ratio も出てきました。

アスペクト比「16:9」の、aspect-ratio の書き方

画面が横長、縦長の「16:9」があります。

メディアクエリでメディア特性を指定するとき、大抵が max-min- を付けて書くことになると思いますが、どちらを書けばいいのか混乱しました。

それぞれのときの「さらに横長」「さらに縦長」を考えればわかりやすいのではないかと思いました。 f:id:yokoyoko_115:20220122012934j:plain

aspect-ratio: 幅 / 高さ という書き方であることを覚えます。

メディアクエリの「aspect-ratio」は落ち着いて考えれば大丈夫そうです。

さいごに

1画面の表示は難しいです。
縦長・横長、各デバイスのディスプレイの大きさ全てに対応できるようにするには、
しっかり知識を持つことが前提になると思いました。

あとずっと「aspect-raito」だと思っていました。 直接入力したおかげで気付けました。

「rate(レート、率)」と混同しました。

英語難しい。

おしまい。

webサーバーをローカルリポジトリにする

とあるサイトで、webサーバーにリポジトリを置いて、バージョン管理ツールからリポジトリをプルすることでサイトを更新する方法に切り替えました。

はじめ「webサーバーをローカルリポジトリにする」と聞きました。
リモート環境のサーバにあるリポジトリなのに“ローカルリポジトリ”というのか疑問でした。

イメージはこんな感じです。

f:id:yokoyoko_115:20220219013133j:plain

ローカルリポジトリとは

ローカルリポジトリとは、Gitなどの分散バージョン管理システムで、利用者の手元のコンピュータに作成・複製されたリポジトリ。利用者が直にファイルの編集などを行うことができる。

ローカルリポジトリ(local repository)とは - IT用語辞典 e-Words

他にも、

ユーザーがローカルマシン上で作業するために利用するリポジトリです。

や、  

操作しているデバイスの中にあるリポジトリをローカルリポジトリと呼ぶ

とありました。

うーん、グレー!
ここまではっきりしないこともあるのでしょうか。
そもそもこのWebサーバにリポジトリを置く方法はあまり主流ではないのでしょうか。

***

とにもかくにも、これからは実際のサイトのファイルが置かれているwebサーバーにリポジトリがあるという初めての感覚です。
webサーバーといえど、手順はローカル環境に作ったリポジトリと同じと考え、リモートリポジトリをクローンする方法を調べて実践してみました。
☆ちなみにサイトにはWordPressが入っています。

1.コマンドラインsshでサーバに接続する

参考にした記事です。

Linuxコマンド【 ssh 】リモートマシンにSSHでログイン - Linux入門 - Webkaru

コマンドライン

ssh [ユーザー名]@[ホスト名]

と入れました。
パスワードを聞かれたので、FTPで接続しているときのパスワードを入れるとログインできました。

このパスワードは、プロトコルの種類に関わらず、共通のパスワードなのですね。

2.ディレクトリを圧縮する

失敗してももとに戻せるようにバックアップを用意します。

そのままFTPで落とすには容量もファイル数も多すぎるので圧縮します。

形式は .tar.gzがよいそうです。

[tar コマンド] 大量のファイルを サーバー上で圧縮・解凍する方法 - WEBサイト制作のメモ書き

FTPでダウンロードしておきました。一安心。 sshでする方法もあるのでしょうか。

3.ディレクトリ以下を削除する

ここはコマンドで削除します。

ファイル・ディレクトリを削除するrmコマンドについて詳しく!【Linuxコマンド集】

rmdir(remove directoryの略)コマンドではうまくいきませんでした。

rmdir コマンドはディレクトリの削除で使いますが、中身を空にしてからでないと削除できないそうです。
なかなか限定的ですね。
(後から -r オプションでディレクトリが消えることを知りました)

そこでrmコマンドを使いました。

rm -fr *

オプションのf(強制的に:「削除してもいいですか」の確認やエラーメッセージが出てこない)、r再帰的に:ディレクトリも削除対象になる)をつけると便利です。

*ワイルドカードで、「何らかの文字」という意味なので、全てのファイルやディレクトリが対象になります。

これでごっそり消えたのですが、なぜか rm: No match. と出たり、.htaccessだけ残っていたり、いくつかファイルが残りました。
.htaccessが残っていたのは、パーミッションの問題??)

4.リモートリポジトリからクローンする

サーバーにgitはすでにインストールされているので以下のコマンドでクローンします。

git clone [リモートリポジトリのURL]

はじめSSHでやってみようとしましたが認証でうまくいきませんでした。

The authenticity of host 'ホスト名' can't be established.
RSA key fingerprint is [公開鍵暗号].
No matching host key fingerprint found in DNS.

と出てきました。

公開鍵とかさっぱり分からないのでSSHは断念し、HTTPSでできました。

5.避難させていたファイルを戻す

避難させておいたWordPressのuploadsディレクトリを戻します。

2.で圧縮してダウンロードしておいたtar.gzファイルをFTPで上げ直して、サーバーで解凍します。

解凍は
tar -zxvf [ファイル名].tar.gz です。

6.developブランチからプルする

クローンはおそらくデフォルトのmasterから行われました。
本当はdevelopブランチのものを反映させたいのでdevelopブランチからプルします。

git pull コマンドの使い方と、主要オプションまとめ | WWWクリエイターズ

プルなら git pull origin develop で済むはずです。

ここでは練習も兼ねてフェッチ + マージ(=プル)をします。

フェッチ

git fetch [リポジトリ名] [ブランチ名]

なので git fetch origin develop です。

From [リポジトリのURL] * branch develop -> FETCH_HEAD

マージ

フェッチによって、以前も調べた「リモートトラッキングブランチ(origin/master)」またの名を「リモート追跡ブランチ」ができました。
(2回目以降はリモート追跡ブランチが更新されます)

このブランチをローカルの現在のブランチとマージさせます。

git merge FETCH_HEAD

うまくいかなかったこと

1.〜6.の方法で概ねうまくいきましたが、ちょっとしたミスやどうにもうまくいかなかったことがあり、すんなりとはいきませんでした。

ファイル名が文字化けしてしまう

そのうちの一つは日本語ファイルの文字化けです。

日本語ファイルの画像を投稿でメディアにアップロードして表示させるだけでは問題ありませんが、uploadsディレクトリを圧縮・解凍で移動させたりすると、文字化けするみたいですね。

ここまで気を配れませんでした。盲点。
やはり日本語ファイルは良くないですね。

参考

Git のフックを利用したデプロイの方法 | ソフトウェア雑記

.gitignoreから外したファイルが消えてしまう

もう一つは.gitignore関連です。
git管理後のファイル・ディレクトリを.gitignoreから外しても管理からは外れません。

$ git rm --cached [削除したいファイル]

これでファイルを残したまま管理対象から外すことができるそうですが、ファイルが消えてしまったり、なぜかうまくいかないことがありました。

ローカルリポジトリ(この場合は自分のPC)にファイルは残るがGit管理から外れる、ということであれば、ローカルリポジトリからは消えてしまうということなのかもしれません。

なんかそんな気がしてきました。

地味にこの問題がたびたび起こることがあるので、いつか解決したいと思います。

さいごに

これまではローカルリポジトリに変更のあったファイルを、FTPでアップロードしてサイトを更新していました。
これが良くなかったのですね...場合によってはSFTPでもないという...。

SSHや踏み台サーバーなど、よく分かっていない方法もまだあるので、サイトの更新に安全な方法を学んでいきたいです!

おしまい。

もう迷いたくない!isset,emptyなどの書き方

WordPressでカスタムフィールドの値を出力する際に、変数に代入させることがあります。
「値があれば代入」「値があれば出力」など色々書いていますが、理由もわからず出てくるエラーに対してあれこれ直しています。
デバックモードでは特にエラーを出してしまいます。

ちなみにタイトルは「もう迷わない!」と言えるほど強くないので希望を込めました(笑)

変数の状態・型を基準にまとめて、実際にどのように書けばよいか調べようと思います。

参考記事

PHPで未定義変数やnullかどうかを調べる関数まとめ | PisukeCode - Web開発まとめ

isset, empty, is_null の動作まとめ - Qiita

PHPで出来てしまうが控えたほうががいい書き方 - Qiita

変数の宣言、定義・未定義

宣言

$var;

一方で定義何かしら値が代入されている状態です。
値が代入されると型が決まります。

$var = null;

これでも定義されています。

なので未定義とは、

  • 変数が代入されていない
  • そもそも変数が未宣言

いずれかの状態を指します。

連想配列の存在しないキーというのも未宣言なので、アクセスすると未定義扱いになります。

定義されているかどうかを調べる書き方

それは isset です。

isset($var);

NULL以外、空でも代入されていて「定義」されていれば true になります。
未定義またはNULLのときに false を返します。
NULLは要注意です。

NULL

変数の中身がNULLnull でもよい)の状態です。

変数の型が「NULL」なので、$var = ''$var = []当てはまりません。 

NULLかどうかを調べる書き方

純粋に「NULLか否か」を調べるには is_null関数を使います。

is_null($var);

NULLで true、それ以外では false を返します。

中身が空

変数の型が決まっていて、中身が空の状態です。
ぱっと思いつくのは $var = ''(文字列)、$var = [](配列) です。

NULLはさておき、それ以外の型で空の状態はあるのでしょうか?

調べていて思いついた方法で型キャストがあります。

変数 - 型の変換 - 型キャストの一覧表 - PHP入門 - Webkaru

$var = '';
var_dump( $var );
var_dump( (int)$var );

空の文字列型を整数型に変えたら空の整数型になるのではという狙いです。
果たしてうまくいくのでしょうか?
※以下は改行させています(本当は1行)

string(0) ""
int(0)

0でした。
試しに $hoge = '100'(int)$hoge としてvar_dumpすると int(100) となるので、$var には0という整数が入っていると分かります。

空にすることはできるのでしょうか。
答えは「整数の0は空」でした。

空かどうかを調べる書き方

それは emptyを使います。

empty($var);

emptytrue が返ってくればそれは空であると捉えます。
※純粋な「空の値」のみを調べる場合は別の書き方になります(後述)。

...だんだん複雑になってきました。表が必要です。
型ごとにempty の「空」をまとめました。

型別の空の値

文字列 ""
文字列 "0"
整数 0
浮動小数点数 0.0
配列 array()、[]
boolean false

表からは外しましたが、NULLempty では空(true)の扱いです。

空ではない値が入っている

上記以外の値、0や空以外の値が数値、文字列、配列などに入っているものです。

空ではない値が入っているかどうかを調べる書き方

$var == true であるとき、空でない値が入っていると言えます。

というのも、
if() の引数に変数を入れると、その変数がbool(真偽の値、trueかfalse)に変換されます。
その変換パターンは上記の「空の値」の表に沿っています。中身によって true / false を返します。

下記は$varに空ではない値があれば true を返します。

if($var) { ... }  

これも同じです。

if(!empty($var)) { ... }

図にしてみる

これらのことを視覚化してみました。

f:id:yokoyoko_115:20211127025004j:plain

未宣言かつ未定義、みたいなところが若干モヤモヤしますが、未宣言だったら当然未定義、未定義だったら未宣言・宣言両方の場合がある、ということを表したかったです。

条件式の書き方

ここまでおさらいしたところで、実際に書くif文をまとめてみます。

変数に値が入っているかどうか

if (!empty($var)) { ... }

if ($var) でも同じかなと思いましたが、この場合は $var未定義の場合はエラーが出ます

定義されていることが確実であれば if ($var) でもいいと思います。

もちろん、文字列や数字の0も値が入っていない扱いになるので、その場合は別の方法を考えましょう。
(問題に直面して調べ次第追記します)

if (isset($var) && !empty($var)) { ... }

この書き方も見かけましたが、これは冗長であるようです。
たしかに作った上記の図を見ても if (!empty($var)) で十分ですね。

変数にNULL以外の空の値が入っているかどうか

empty だけでは未定義やnulltrue とみなされます。
下記は0や空文字のみを判別する書き方になります。

if (isset($var) && empty($var)) { ... }

変数に値が入っているかどうか(配列の場合)

if (is_array($array) && !empty($array)) { ... }

!empty($var) で配列が空ではないかどうかが分かりますが、これだけでは配列かどうかの判別がつかないので is_array() も条件に入れます。

これも配列であることが確実であれば if (!empty($array)) だけでいいと思います。

新しく知ったこと

issetとemptyは関数ではなく言語構造

調べると「isset関数」や「empty関数」とよく出てきます。ドキュメントにすら「この関数は」と書かれています。

言語構造については難しそうなので割愛しますが、issetempty は言語構造なので、
isset は変数のみを引数として受け取れるようになっています(複数の変数を引数に持てる)。

オーバーヘッドについて

「issetとemptyは関数ほどオーバーヘッドが大きくない」という文があったのでオーバーヘッドを調べました。

[PHP] function() がどれほど遅いか調べてみた | 株式会社オルタ

関数になってなければ、次はスグ下の行に移ればいいだけであることを考えると、全く違う場所に書かれている getHoge() を探さなきゃいけないってことはなんとなくイメージできるのではないでしょうか?

この、”どこにあるのか探す”という処理のことを、オーバーヘッドと呼びます。

issetとemptyは関数より処理が早いのですね。

他には比較演算子と速さを比べていましたが、ここでは突き詰めないようにします。

!isset() とも書ける

ドキュメントのemptyのページで書かれていました。

変数が存在しなくても警告は発生しません。 つまり、empty() は本質的に !isset($var) || $var == false と同じことを簡潔に記述しているだけです。

あまり見かけないのですが !isset() と書けるのですね。
使う場面がでてくるかどうか、出てきたら追記します。

変数の初期化

未宣言、未定義に加えて「初期化」という言葉も出てきました。

【PHP入門】これで完璧! 5分でわかる変数の基本徹底解説 | 侍エンジニアブログ

変数の初期化とはプログラムの初期処理において、あらかじめ変数に決められた値を設定したり、初期値であることを示すために0やNULLを指定することを言います。PHPでは変数の宣言と値の代入はセットで行いますので、一番始めに変数の宣言と値を代入する処理が初期化処理となります。

PHPでは初期化されていない変数はNULLが格納されているとみなされます。

他の言語を学ぶと違いがはっきり分かりそうですね。
ほぼ学んでいない身としては混乱です。

// 宣言のみ(初期化されていない)
$var;

// 初期化処理
$var2 = 'hoge';

// 「Notice: Undefined variable」というエラーとNULLが出る。
var_dump($var);

初期化されていない変数にはNULLが格納されているとみなされているはずなのに「Undefined variable」と「未定義の変数です」とエラーが出ておきながら、
未定義のはずなのにNULL が出るというのはどういうことなのでしょうか?
同じこと2回書いていますね。

これまで調べてきたことが覆る事態です。

【PHP入門講座】 NULLと未定義の違い - Qiita

PHPでは、未定義の値を取得しようとしたときに、Noticeを発生しながら NULL を代わりに取得します。

取得しようとした時に、NULLを取得して定義されるのですね!

カスタムフィールドを設定しただけでは未宣言?未定義?

ここはカスタムフィールドのプラグイン「Advanced Custom Fields」のお話です。

存在しないカスタムフィールドや設定済みのカスタムフィールドでも、その後に投稿ページを開いて「更新」しない状態でカスタムフィールドをvar_dumpすると、エラーなしのNULLが出ます。

これも、関数の中で呼び出された際にエラーが出ないような処理とNULLが入る処理が入っているものと思われます。
未宣言や未定義のカスタムフィールドでも安心設計になっているのではないでしょうか(想像)。

そして未入力のまま投稿を「更新」すると、設定した型のデフォルト値が入ります(「テキスト」の場合は文字列型の空文字)。

これまではその都度場当たり的に解決してきたので、どのような状況でエラーを出していたか具体例が挙げられず。残念です。

エラーについては別でまとめようと思います。

array_key_exists、property_exists

配列の中身のあるなしを調べる関数です。
正直ここまでは頭の容量オーバーで調べきれませんでした。

配列で詰まったら調べます。

さいごに

太字を多用して「迷いたくない!」気持ちが出てしまいました。

たくさんこの関連の記事は見るのですが、true/falseの表を見てもどうも覚えられず。
覚えられそうで覚えられない。

ただ覚えるだけでは応用が効かない気がして、視点を変えてまとめてみました。

図も作ってみました。自画自賛していますが、良くない解釈が入っていそうな気もします。

あくまで自分自身のメモなので、間違っている部分はその都度直していこうと思います。

おしまい。

CSS、Sassでの変数の書き方

Hugoブログを絶賛カスタム中です。
(テーマは「Clarity」を使っています)

このテーマではSassが使われているのですが、変数を用いて色やwidthの数値を設定しています。

そしてなぜかSassの中でcssのカスタムプロパティを使っていました。
しかも私にとって馴染みの薄いSASS記法...。

Sass(SCSS記法)でSassの変数を使うことはあっても、このテーマのような使い方はしたことがありませんでした。

今までカラーコードの統一ぐらいにしか利用できていないので、この機会に調べてまとめます。

CSSの変数・カスタムプロパティ

cssで変数の定義、呼び出し、ということができます。

参考記事です。

CSSで変数(カスタムプロパティ)を使ってみよう | Webクリエイターボックス

CSSの変数(カスタムプロパティ)便利な使い方を詳しく解説 | コリス

変数の定義

:root {
  --var_name: value;
}

--変数名: カラーコードや単位付きの数値など;

です。

値には''などクォーテーションは必要ありません。
また --sukima: margin などのように、プロパティ名は設定できません。

この場合は :root 以下の子要素で var_name という変数を呼び出すことができる設定になります。

要素に対してプロパティに当たる変数を設定するので「カスタムプロパティ」というのでしょうね。

この↑後に同じ変数に別の値を再定義することができます。

再定義は、子要素に対して、と
同要素に対してウィンドウサイズのメディアクエリなどで、行います。

※「再定義」と書いてますが、これはこのままこのような考え方でいいのか、それともスコープが異なるとはいえ同名の変数に値を入れるので「代入」として考えた方がいいのか、分からなくなってしまいました。

他の言語でもスコープの概念は出てきますし基礎的な部分だと思うので、判明したら修正しようと思います!

:root {
  --font-large: 20px;
  --font-small: 16px;
}

/* 子要素で再代入 */
.child {
  --font-large: 16px;
  --font-small: 14px;
}

/* メディアクエリで再代入 */
@media screen and (min-width: 769px) {
  :root {
    --font-large: 18px;
    --font-small: 12px;
  }
}

実はメディアクエリでの変数の定義・再定義は、CSS変数のみSass変数ではできません。

:root とは

:root - CSS: カスケーディングスタイルシート | MDN

CSS の :root 疑似クラスは、文書を表すツリーのルート要素を選択します。 HTML では、 :root は 要素を表し、詳細度が高いことを除けば html セレクターと同等です。

:root はグローバルの CSS 変数を宣言するのに便利です。

:hover などと同じく擬似クラスなんですね。

:root {} 内で定義した変数はグローバル変数になることは覚えておきます。

ところで、 E:root と書かれている記事もあったように、要素をくっつけて書くこともできるのでしょうか?
検証しました。

html(一部省略)

<html>
   :
  <h1>:rootとhtmlの検証</h1>
  <div>
    <p>あいうえお</p>
  </div>
   :
</html>

css

/* :rootよりも詳細度が高い */
html:root {
  color: pink;
}

/* html要素以外の要素を指定しても無効 */
p:root {
  color: green;
}

/* htmlよりも詳細度が高い */
:root {
  color: red;
}

html {
  color: blue;
}

p {
  color: green;
}

h1とp要素の文字色を確認しました。

p:roothtml:root を検証したところ、html:root は有効であることが分かりました。
要素の指定が入っている分、:root よりも詳細度が高いです。
おそらく E:root はhtml要素以外は使えないのだと思います。

html:root の使い所があるかは分かりませんが、使っているところを見たことがないので、個人的にはこれからも使わないです。

h1とp要素の文字色は、結果
h1:pink → html:rootcolor の指定がh1要素に継承された
p:green → 直接の要素指定によりp要素に継承されたpinkがgreenに上書きされた

でした。

検証したプロパティが color なのでここに「継承」が絡んでいて少しややこしいですが、 そもそもルート要素には継承するようなプロパティを設定することがほとんどだと思います。

そこで :roothtml どちらを使うかですが、好みの問題な気がしました。

カスタムプロパティの設定は :root で、普通のプロパティの設定は html にしてみたり、どちらかで統一してみたり。
変にごちゃまぜに使わなければいいのかと思います。

かなり脱線しました。

変数の呼び出し

.class {
  property: var(--var_name);
}

var(--変数名);です。

変数の呼び出しは、親要素で定義した変数が対象なので、 :root で定義すればすべての要素から呼び出すことができます。

Sass関数内にCSSの変数を呼び出せない

sass変数をcss変数(カスタムプロパティ)に渡す方法 - Qiita

Sassでこの書き方は駄目だそうです。

body { color : darken(var(--color-primary), 10%); }

darken() はSass関数です。

ちなみによく使う calc()CSSの関数なのでCSS変数は使えます。

Sassの変数

Sassの変数についても定義と呼び出しについて調べました。

【Sass実践編】Sassを使いこなす第一歩、「変数」の書き方と使い方! : ビジネスとIT活用に役立つ情報

変数の定義

$var_name: value;

$変数名: カラーコードや単位付きの数値など;

です。

セレクタの外にグローバル変数として定義ができますし、特定のセレクタの中でローカル変数として定義もできます。

変数の値の後ろに !global を付けるとグローバル変数扱いになるそうです。

メディアクエリ内でSass変数は定義できない

メディアクエリ内の変数設定はなかったことになります。

Sass(SCSS)

$color: red;

p {
  color: $color;  // ウィンドウサイズの幅が767px以下でもred
}

@media screen and (max-width: 767px) {
 $color: blue;
  // メディアクエリの中で変数を呼び出せば適用される
  // p {
  //   color: $color;
  // }
}

コンパイルcolor の値が決まりますが、メディアクエリは動的なので、この時点では青文字になるかどうかは分かりません。
よって color: blue; にはなりません。

メディアクエリの中で変数を呼び出せば適用されますが、↑この書き方はまるでSassの恩恵を受けていないので書かないほうがいいです。

ここはmixinを使って書くべきなのでしょうね。

一連の書き方が丁寧に載っていました。

【Sass】mixinでメディアクエリを管理する方法【応用編】 - TechnoBlog(テクノブログ)

変数の呼び出し

selector {
  property: $var_name;
}

プロパティの値の一部として変数を呼び出す場合は #{$var_name} と書きます。
これをインターポレーション(補完)といいます。

background-image: url(#{bg-image-path});

sassファイルでCSS変数とSass変数両方使う理由

ここまで見てきてどちらも一長一短であることが分かりました。

CSS変数 Sass変数
変数の定義 --変数名: 値; 要素に対して(プロパティ):rootグローバル変数 $変数名: 値; セレクタの外でグローバル変数
変数の呼び出し var(--変数名) プロパティの値 $変数名 関数の引数、プロパティの値の一部として呼び出す場合は #{$変数名}
Sass関数内
メディアクエリ

ざっくりしているのでもう少し表に加えていきたいです。
(内容があっているかどうかは少し心配です)

例のHugoテーマのSASSファイルを見てみても、まずSass変数を定義して、CSS変数でも同じ変数を定義して使えるところでどちらかの変数を呼び出す、ということをしていました。

まだ釈然としませんが、Sass変数をメディアクエリなどで効率的に使う方法を学べばわかるようになるのではないかと思います。

これでSassファイルをしっかりと読んで、Hugoブログのcssカスタマイズを進めていきたいと思います。

Hugo - メニュー、各項目の表示

前回でロゴやファビコンの変更ができたので、次はメニューなどの表示面を整えていきます。

理想のブログの構成

ただ記事が探しやすく見られればよいので簡単な構成にしました。

TOP
 ┗カテゴリー
  ┗記事
 ┗タグ
  ┗記事

About(筆者紹介のページ)などヘッダーに表示されていたメニューも不要なので消します。
多言語対応もできないので日本語一択で表示させます。

Light/Darkモードの切り替えはおもしろそうなので残しておこうと思います。

その前に調べました

前回の教訓を受けてドキュメントは見よう!ということで、使用しているテーマ「Clarity」のGitHubを見ました。 ごりごり翻訳ツールを使って解読しました。

設定ファイル、params.tomlの存在

ドキュメント(README.md)を読んで大まかに分かったことは、設定の変更はconfig.tomlファイルと、その中の [param] の部分で行うということでした。
ここでconfig.tomlファイルの [param] をいじっても反映されない問題に直面しました。
サイト名の変更をするときと同じです。

では別のファイルで設定されているのかと、params.tomlというファイルの存在を思い出しました。

あれこれ試してみましたが、同じ設定項目は、params.tomlの設定値が優先されるようです。
Hugoのドキュメントにありました。

Configure Hugo | Hugo

Configuration File
Hugo uses the config.toml, config.yaml, or config.json (if found in the site root) as the default site config file.

設定ファイル
Hugoは、config.toml、config.yaml、config.json(サイトのルートにある場合)をデフォルトのサイト設定ファイルとして使用します。

config.tomlはルートに置いているのでデフォルトの設定ファイルだそうです。

Configuration Directory
In addition to using a single site config file, one can use the configDir directory (default to config/) to maintain easier organization and environment specific settings.

  • Each file represents a configuration root object, such as params.toml for [Params], menu(s).toml for [Menu], languages.toml for [Languages] etc…
  • Each file’s content must be top-level.

設定ディレクト
サイトの設定ファイルだけでなく、configDirディレクトリ(デフォルトはconfig/)を使用して、組織や環境に応じた設定を簡単に行うことができます。

  • 各ファイルは、[Params]にはparams.toml、[Menu]にはmenu(s).toml、[Languages]にはlanguages.tomlというように、設定のルートオブジェクトを表します。
  • 各ファイルの内容はトップレベルでなければなりません。

「ルートオブジェクト」というのは、最上位の設定ファイル、ということでしょうか。
「トップレベル」というのは設定項目と値が入れ子になっていない、ということかなと思いました。

config.tomlファイルでは、

# [languages]
# config/_default/languages.toml

# [menus]
# config/_default/menus/menu.xx.toml

コメントアウトになっており(ちなみに [params] の部分には同じような # config/_default/params.toml といった記述がない)、# を取って有効にすると、画面が表示されなくなりました。
コマンドラインで「bare keys cannot contain '/'」というエラーが出ました)

これは、どうやら「[languages] は config/_default/languages.toml」で設定しています」というコメント のようです。
とんだ勘違いでした。

エラー内容の確認、大事ですね!

config.tomlファイルでは [params] のように [languages] を設定できました。
[languages] の下の [markup] を見ながら書いてみるとうまくいきました。

[languages]
# config/_default/languages.toml
  [languages.ja]
    title = "サイト名"

ただlanguages.tomlファイルが優先されるので、languages.tomlファイルで設定がある限りは、config.tomlの設定値は反映されません。

少し分かった...!

これで諸々の設定もスムーズにいく、ようになるかもしれません。

複数のサイトをHugoで作るとなった場合、configディレクトリ内に環境(あるいはサイト?)ごとにディレクトリ・設定ファイルを置いて管理することができる、というところまでは分かりました(Hugoのドキュメントより)。

その先が謎なのですが、複数サイトを作らないので、今回も謎なままで themes/hugo-clarity にあるexampleSiteについてはスルーしようと思います。

テンプレートについて

そしてHugoのテンプレート機能について調べました。

静的サイトジェネレータ「Hugo」と技術文書公開向けテーマ「Docsy」でOSSサイトを作る | さくらのナレッジ

Go言語では、text/templateやhtml/templateというテンプレート機構が標準で用意されている。Hugoではこのテンプレート機構をそのままレンダリングに使用している。

Go言語のテンプレート機構について、今度はGo言語のドキュメントを見ました。

template package - text/template - pkg.go.dev

template package - html/template - pkg.go.dev

Hugoを使うにあたり、Go言語自体の学習は不要であるという記事も見かけました。
それでもテンプレート機構の書式は理解してないとカスタマイズは難しいと感じました。

「{{」および「}}」で囲まれた部分はテンプレートエンジンによって記述された処理結果に置き換えられる。

などなど。

↑引用元の「さくらのナレッジ」に文法が載っていました。
引き続き調べながらカスタマイズしていきます。

こちらもテンプレートの構成について詳しく載っています。

Hugo をしばらく触ってなかったので、わかりやすくまとめ直した - ばうあーろぐ

メニューの修正

ようやく英語やHugoに馴染んできたところで、ヘッダーのメニューを変えていきます。

ClarityのGitHubより該当箇所を見つけました。

Main Menu To add, remove, or reorganize top menu items, edit the files here. Specifically look for items with main.

If you prefer the more traditional approach, delete content\config folder and enter a main menu entry inside the config.toml file

メインメニュー トップメニューの項目を追加、削除、再編成するには、ここでファイルを編集します。特に、mainが付いている項目を探してください。

より伝統的な方法をお望みの場合は、content\configフォルダを削除し、config.tomlファイル内にメインメニューの項目を入力してください。

[[main]] があるところは言語ファイルの config/_default/menus/menu.xx.toml でした。

メニューの設定が色々できそうですが、そもそもメニューが要らない。
どうしたものかと思いましたが、テンプレート側からごっそり外してしまいました。

すなわち layouts/partials/header.html です。

メニューの設定値を弄ったほうがいいのか迷いましたが、このときはconfig周りが謎だらけだったので、テンプレートをいじりました。

ここでまた1つ問題が。
スマホになるとメニュー部分が空になります。メニューボタンもなくなってしまいました。 スマホのメニュー部分にはタグやカテゴリーの一覧が表示されていてほしいです。

## スマホハンバーガーメニューにカテゴリー・タグメニューを入れたい

メニューは要らないと言ったものの、スマホではカテゴリーやタグからすぐリンクできるようにしたいので、ハンバーガーメニューにカテゴリー・タグ一覧を入れることにしました。

新しく入れるメニュー

これはPC表示だと右カラムに出てくる部分、すなわちsidebar.htmlのタグ・カテゴリー部分をコピーして入れました。

ここで少し問題が。ハンバーガーメニュー内はスクロールしない仕様のようです。

メニューだけならそんなに高さは取らないからですね。
こちらは後で直そうと思います。

既存のメニュー

当初はテンプレートからまるまる削除していましたが後々メニューを出したくなるかもしれないので、なるべくコードを消さない方向でやってみました。

1つ目は日本語用に作ったmenu.ja.tomlの [[main]]コメントアウトすることです。

こんな感じです。

# [[main]]
  # name = "Archives"
  # url = "post/rich-content/"
  # weight = -109

「Home」は一応残しておくことにしました。

「データがない」という状態にしておかないと、テンプレート側でdiv要素が作られたりします。

同様に、「多言語対応」の扱いを受けてしまっていたようで空のdiv要素が書き出されていました。
これはmenu.ja.tomlファイルの他に残しておいたmenu.en.toml、menu.pt.tomlファイルを削除することで解決しました。

ファイルごと削除は当初抵抗があったので、全てコメントアウトで解決しないか試してみたところ、ダメでした。

このあたりのテンプレートの条件分岐のコードが読めればいいのですが、よくわからないので探り探りです。

次回へつづく

これでようやく表示したい内容が揃いました(多分)!

全体の色やハンバーガーメニューの調整をして、一覧の記事、詳細の記事の部分を触っていきます。

Sassはsassファイル、SASS記法で書かれています。
慣れないですが良い機会です。

ひとまずおしまい。

gulpのアップロードタスクの書き方について

gulpを使うようになって設定するものの1つとして「ファイルアップロード」があります。

具体的には「vinyl-ftp」というプラグインを使っています。

vinyl-ftp - npm

最新の更新も4年前(2021年10月現在)と、更新が滞っているプラグインですが、FTPクライアントを使って1つ1つファイルを上げていた身としては、今でも重宝しています。

セキュリティ上SFTPで上げた方がいいのでは、と思うのですが、SFTPでしか上げられない場合とFTPでしか上げられない場合があり、
その違いや仕組みをまだ理解していません。

むしろFTP/SFTPでデプロイする方法を改めた方がいいのでしょうが、こちらも残念ながら勉強不足です。
GitHubからnetlifyを使ってHugoブログを作るような方法が現在はメジャーなのかと考えています)

今回は何回もうまくいったりいかなかったりしている「vinyl-ftp」の設定についてメモします。

ファイルの構成は以下のようになっています。
(フォルダには「/」を入れました)

***

/www
┗/テストドメイン
 ┗/res ←imgやcss、jsフォルダに各ファイルが入る
 ┗/wp
  ┗/wp-admin
  ┗/wp-content
  ┗/wp-includes
  ┗/src ←ローカルリポジトリにのみある
   ┗/scss ←scssファイルが入る
   ┗/node_modules
   ┗package.json
   ┗deploy-config.json
   ┗gulpfile.js ←ここに書くコードの話

***

最終的にうまくいったgulpは以下のように書きました。

gulpfile.js

const fs = require('fs');
const ftp = require('vinyl-ftp');
const { src, dest } = require('gulp');
// ↑const src = gulp.src; const dest = gulp.dest;

// サイトへアップロード
const config = JSON.parse(fs.readFileSync('./deploy-config.json', 'utf8'));
const conn = ftp.create(config['production']['ftp']);
const srcList = [
  '../../res/**/*',
  '../../' + THEME_PATH + '**/*',
  '!../wp-config.php',
  '!../src/',
  '!../wp-content/uploads/',
  '!../wp-content/plugins/'
];
const uploadProduction = () => {
  return src(srcList, { base: '../../', buffer: false })
    .pipe(conn.newer(FTP_UPLOAD_PATH))
    .pipe(conn.dest(FTP_UPLOAD_PATH))
};

// タスクの宣言
exports.up_prod = uploadProduction;

つまづき1:パーミッションエラー - newerとdestの部分

conn.newer

「vinyl-ftp」のドキュメントぺージの説明

Returns a transform stream which filters the input for files which are newer than their remote counterpart.

リモート側のファイルよりも新しいファイルの入力をフィルタリングする変換ストリームを返します。

gulp.dest

公式

dest() | gulp.js

Creates a stream for writing Vinyl objects to the file system.

意味はこちらのほうがわかりやすかったです。

gulp.dest | gulp 日本語リファレンス | js STUDIO

pipeされたものを、ファイルに書き出します。 渡された全てのデータが再度選択されるため、複数のフォルダに書き出すことが可能です。 フォルダが存在しない場合、自動的に生成されます。

conn.newerconn.dest の引数は接続先のパスを書きます。 conn.newer でリモートと比較してローカルで新しく更新があったものをチェックします。
その結果を受けて conn.dest で配置しています。

FTP_UPLOAD_PATH は定数にしています。

直した所は、この定数を /home などルートディレクトリから書くということです。

接続したディレクトリから相対的に(途中から)書いてしまうとエラーが出ます。
この例では /www と間違えて

Error: /www: Permission denied

とエラーが出ました。

試しにFTPのパスワードを変更してみると別のエラー(認証エラー)が出ました。接続はできているみたいです。

パーミッションで書き込み権限がないというのは、サーバー側の問題の場合もあるし、クライアント側の問題の場合もある。
と調べたら出てきました。

サーバー側はディレクトリのパーミッションを変えてみましたが元から書き込み権限があてられていたため当然変わらず。
クライアント側は、FTPクライアントでも同じアカウント情報で接続しているのに権限とは??という状態でした。

設定のパスをいろいろ試してみて解決しました。これでとりあえずファイルをアップロードすることはできました。

つまづき2:アップロード先がおかしい - srcのオプションbase

アップロードができるようになったものの、今度は対象のアップするファイルがすべて FTP_UPLOAD_PATHディレクトリ にアップされるようになってしまいました。

cssディレクトリもなく、cssファイルがサイトトップのディレクトリに配置されてしまいました。

const srcList = [] の書き方か、glob形式の書き方が間違っているのではと調べましたが、なかなか望む内容の記事に出会えず。

こちらの記事が頼りになりました。

gulpでFTPサーバにデプロイする - なおしむ論

そこで gulp.src のbaseオプションに着目しました。

「vinyl-ftp」のドキュメントには

base: Set as file.base, default is glob beginning. This is used to determine the file names when saving in .dest().

base:file.baseとして設定され、デフォルトはglob beginningです。.dest()で保存する際のファイル名の決定に使用されます。

さっぱり分からない...。

実はこの時点まで、何を思ったのか、不要だったbaseオプションを省いていました。

../../ は、このgulpfile.jsからサイトトップのディレクトリまでの相対パスを書いています。

過去に携わったサイトのgulpfile.jsを見てみるとbaseオプションが書かれていたので、真似て入れてみました。

すると、うまくアップロードされたのです。

道理がわからないのですが、ひとまず解決です☆

gulp.src

こちらも調べてみました。

gulp.src | gulp 日本語リファレンス | js STUDIO

アップロードに指定されたglob、またはglobの配列にマッチしたファイルを選択します。 プラグインとpipe可能なVinylファイルのストリームが返されます。

配列で書けます。こちらもgulpfile.jsからの相対パスで書きます。

glob形式の指定の書き方が何となくでしか理解できないのですが、いつかまとめられた記事が見つかるといいいです。

さいごに

なんとも後味の悪い終わり方ですが、うまくいったのです。

Vinylファイル、ストリームとは??
理解していないのでまたつまづく可能性がありますが、そのときはまた唸ることにします。

おしまい。