鍵認証でSSH、SFTP接続をしてみる

これまではサーバーとのファイル転送接続は、パスワード認証のSFTPほぼ一択だったので、念願(?)の鍵を使った接続方法を試してみました。

ファイル転送プロトコルについては、こちらの記事で調べました。

鍵認証でのSSH接続の手順

昨年契約して以来、全く使っていないエックスサーバーがあるので、そちらで試してみることにしました。
(その時の模様をまとめた記事です)

エックスサーバーにSSHの接続方法が載っているので下記を見ながら進めていきます。

SSH設定 | レンタルサーバーならエックスサーバー

【初心者向け】エックスサーバーでSSH接続する手順と設定まとめ | ゆるグラミング!!

1. エックスサーバーでSSHを有効化する

サーバーパネルにログインします。
下記のように「状態:ON」となっていればOKです。

2. サーバーに公開鍵の登録を行う

いよいよ登場しました公開鍵!

お客様にて生成の公開鍵を登録していただく方法と、サーバー上で公開鍵認証用鍵ペアを生成し秘密鍵をダウンロードしていただく方法があります

何らかの方法で生成した公開鍵をすでに持っていれば前者(登録)なのだと思います。

自分のPCで鍵ペアを作る→登録、とする事もできるようですが、なにせ初めてなのでエックスサーバーで公開鍵認証用鍵ペアを生成します!

後述する参考サイトに書かれていた

公開鍵と秘密鍵の仕組みを一言で言うと錠前(公開鍵)と鍵(秘密鍵)のようなものです。

サーバーに錠前(公開鍵)を設置することで、さらにセキュリティが強化されて第3者が盗聴や不正侵入するリスクを減らすことができます。

そして、鍵(秘密鍵)は自分のパソコンだけに持っておくことで、サーバーに設置した錠前(公開鍵)を開けてサーバーへ接続できるのは自分だけとなるわけです。

ここでは鍵・錠前(南京錠)の関係をイメージしてみます。

この「パスフレーズ」というのは?

パスフレーズSSH接続の時に使用するパスワードです。 接続時に入力する必要があるので、忘れないよう注意してください。

パスワード認証でつかうものとは別のパスワードですね。大事なものでした。

またこの画面には下記のように注釈がありました。

本機能のご利用により、サーバー上の公開鍵が更新されます。 本機能を利用して鍵ペアを作成すると、秘密鍵のダウンロードが始まるとともに、サーバー上の公開鍵が更新されます。

「生成する」ボタンを押すと秘密鍵がダウンロードされました。

秘密鍵.key が拡張子のファイル。Skypeのアイコンで、押したらSkypeが起動してしまったので慌てて閉じました。

ファイルの情報を確認すると「種類:Keynote書類」とありました。

PC(Mac)に入っているソフトが反応しただけなのかなと思います(調べてもよく分からず)。

秘密鍵を任意の場所に保存すればいいのかと思いきや、

鍵がダウンロードされたら、自分のPCの「.sshディレクトリに移動させましょう!

置く場所が決まっているようです。
ひとまず置いてみます。

3. SSHで繋ぐ

コマンドラインsshコマンドを使ってサーバーに繋いでみます。

$ ssh -i サーバーID.key サーバーID@サーバーID.xsrv.jp -p 10022

サーバーIDはエックスサーバーの「サービス一覧 > Xserver レンタルサーバー」から確認できました。

.sshディレクトリでコマンドを打つと、

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'キー名.key' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "キー名.key": bad permissions
サーバーID@サーバーID.xsrv.jp: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

アットマークがものすごく怖い!

どうやら秘密鍵ファイルの権限が緩くて警告を受けているようです。パーミッション644から所有者のみ読み書きができる600へ変更します。

chmod コマンドを使います。(おそらくchange modeの略)

chmod 600 キー名.key 

パーミッションの変更をして再度 ssh コマンドで接続すると、例のパスフレーズを聞かれました。

すると [サーバーID@sv*** ~]$ と表示されていました。サーバーに接続できました♪

これでパスワード認証ではない、鍵認証でのSSH接続ができました!

ペアになっている、サーバーに置く公開鍵とローカルPCに置く秘密鍵
割とすんなりとできた印象です。

鍵認証でのSFTP接続手順

こちらを参考にしました。

FileZillaでエックスサーバーにSFTP接続で安全にファイル転送する方法

「鍵認証でのSSH設定」は、SFTP接続でも必要なことなので、サーバーのコンパネでSSH設定をONにして、公開鍵・秘密鍵を作成します。

私はFileZillaを使っています。設定をしていきます。

FileZillaの設定

FileZilla秘密鍵の登録をします。
「鍵認証でのSSH接続の手順」で鍵ファイルを置いた.sshディレクトリは隠しフォルダのせいか、FileZillaからでは秘密鍵ファイル選択の際に見つかりません。
一時的に移しても、.sshに戻したらパスが変わって繋がらなくなってしまいます。

致し方なく保存場所を変えてやってみたところ秘密鍵を登録できました。

秘密鍵のファイル形式がFileZillaには合わないようで、変換されました。

「[編集]→[設定]→[SFTP]」から秘密鍵を登録する方法と、接続先の設定画面で秘密鍵を登録する方法と2通りあるようです。
前者の方がFileZilla全体の登録されている鍵が把握できていいのかなと思いますが、どのサーバーとの鍵なのは記載がありませんでした。

ちなみに、sshで繋いだときもポート番号は10022
セキュリティ対策のため、エックスサーバーがsshのポート番号をデフォルトの22から変更しているようです。

接続時にはパスフレーズも聞かれます。

このような画面も出ます。

「常にこのホストを信用し〜」にチェックを入れてOK。無事繋がりました!

さいごに

プロトコルを調べた際に大混乱したので、実際には「習うより慣れろ」かなぁといいう印象です。

秘密鍵は大事にして、鍵認証の接続に慣れて、何かでつまづいたときに復習したいと思います。

おしまい。

ファイル転送プロトコル関連(FTP、SFTP、SSL/TLS、SSHなど)

なんとなくな知識で普段サーバーと繋いでいるので、プロトコルについて調べました。

ファイル転送プロトコル - FTP、FTPS、SFTP、SCP

こちらをはじめに見ました。

ファイル転送プロトコルについて(FTP、FTPS、SFTP、SCP)

ファイル転送プロトコルは主に以下の4つがあります。

  • FTP:「File Transfer Protocol」
    暗号化されていない
  • FTPS:「File Transfer Protocol over SSL/TLS
    FTPSSL/TLSで暗号化されている
  • SFTP:「SSH File Transfer Protocol」
    SSHで暗号化されている
  • SCP:「Secure Copy Protocol」
    SSHで暗号化されていてSFTPよりも通信速度が早い

FTPについて

ファイル転送プロトコルといえば、のFTPについて調べました。

FTPとは? ファイル転送の仕組みとデメリット、FTPSとSFTPの違いについて | 物理サーバ愛が止まらないホスティング事業者のブログ | ベアメタルブログ

TCP/IP - FTPとは

コネクション

コネクションとは、サーバーとクライアントの間で確立される通信経路のことを指します。FTPの通信においては、「制御用コネクション」(コントロールコネクション)と、「データ転送用コネクション」(データコネクション)の2つのコネクションを確立する必要があります。

表にするとこうなります。

コネクション FTPサーバのポート番号 特徴
コントロールコネクション 21 ファイルの転送方法などの命令と応答など
データコネクション アクティブモード:20、パッシブモード:サーバーが指定したポート 転送されるデータの送受信

VSCodeFTPを使う際、ポート番号を20にしたら繋がりませんでした。
コントロールコネクションの21にしたら繋がりました。

これは通信で最初に使われるコントロールコネクションのポート番号を指定するものと思われます。

ちなみに後述するFTPSもこの2つのコネクションを確立して通信します。

一方でSFTPは1つのコネクションを使い、またFTPとは根本的に通信方式が異なるそうです。
「SFTP」という名前なのに誤解されそうな名前...。

アクティブモード・パッシブモード

さらにアクティブモード・パッシブモードについて調べました。
モードの違いについては、データコネクションFTPクライアントFTPサーバ、どちらから接続要求を行うかによって決まります。

モード 接続要求の開始 特徴
アクティブモード サーバー ファイアーウォールの設定などでうまく接続できない場合がある
パッシブモード クライアント データコネクションのサーバーのポート番号がぎりぎりで決まる

それぞれのモードの通信の流れは、参考記事の図を見ると頭に入ります。

なお、データ転送モード(ASCIIモード・バイナリモード)については割愛します!

SCPについて

一番言葉で聞き馴染みのないSCPについて調べました。

「SFTP?FTPS?なにが違う?」 https://academy.gmocloud.com/keywords/20170308/4155

SFTPとの違いはファイルの転送が再開できないことや、SFTPより通信が高速であると言われています。また、フォルダの転送はできませんが、フォルダをzipやtar形式で圧縮して転送することは可能です。

SCPもSFTPもSSHを使っているプロトコル

SCPは、ファイル管理や削除もできないそうです。

一度に大きな容量をアップロード・ダウンロードする際などに、通信が切れてファイルの転送が途中で止まってしまうことがあります。
SFTPの場合は、通信再開時に転送できなかった分を続きから転送することができます。
SCPの「ファイルの転送が再開できない」ということは、途中まで転送できたのに、通信再開時に転送できた分を含めてまたイチから送ることになります。
そしてまた通信が切れてしまったら...と思うと永遠に全てを転送することはできないように思えてしまいます。

一長一短ですね。

以前 scp コマンドの代わりに rsync コマンドを教えてもらった事がありました。

scp の代わりとしての rsync コマンドとそのオプション – ラボラジアン

【rsync】差分同期の仕組みとオプション, ログの見方(フォーマット) | SEの道標

rsync(アールシンク)「remote synchronization」だそうです。

rsyncSSHを使うこともできるそうですが、SCP同様、コピー、同期の意味合いが強い印象です。

rsyncプロトコルなのかどうかも、rsyncを使った繋ぎ方によって変わるようです。
後述するSSHのように、時にアプリケーションだったりします。

プロトコルと言われているものもいろいろあるのですね。

転送はするけれども保存はしないプロトコル - HTTP、HTTPS

ダウンロードで,FTPとHTTPの違いは何? | 日経クロステック(xTECH)

HTTPはファイル転送は行っているものの、目的が「転送」ではなく「配布」であるとありました。

  • HTTP:「HyperText Transfer Protocol」
    ウェブサーバーとブラウザで通信する
  • HTTPS:「HyperText Transfer Protocol over SSL/TLS
    SSL/TLSで暗号化されているところをHTTPで通信する

HTTPは,Webページのコンテンツをサーバーから読み出すのに使われる。Webブラウザは,WebサーバーにあるHTMLファイルや画像ファイルなどをダウンロードし,パソコンのハードディスクやメモリーに一時的にためたものを表示している。

ソフトウエアなどをHTTPでダウンロードする場合で違うのは,Webブラウザで表示するか表示せずに保存するかというユーザーからの見え方だけ。しくみはまったく同じだ。

ウェブページの画像を保存する場合は、表示のために「一時的にためたもの」を保存用としてさらに端末の別の場所に保存している、ということなのかなと思いました。

ブラウザに表示なしで、ボタンを押してすぐにPDFをダウンロードしたりする場合も、例えば

<a href="./file/sample.pdf" download="sample.pdf">ダウンロード</a>

このようなテキストリンクがあったとしたら、リンクのパスは相対パスでも https:// から始まるので、HTTP(S)を使っているのでしょう。

HTTP、HTTPSはファイル転送プロトコルではあるけれども、目的が他のプロトコルと異なるため、少し立ち位置が違うみたいです。

暗号化に関わるプロトコル - SSL/TLSSSH

次によく混乱するSSL/TLSSSHについて調べました。

どちらも暗号化した通信をするプロトコルですが、ざっくりした2つの違いは

  • SSL/TLS: 不特定多数のサイトへアクセスする
  • SSH:特定のサーバーへアクセスする、遠隔操作する

です。

5分で読める!SSHとSSLとは?それぞれの違いを分かりやすく解説 - Pimlusメディア

言い換えると、ご自身のコンピュータから不特定多数のサーバー、すなわちサイトへアクセスする場合に利用されるのがSSLであり、一方、ご自身のコンピュータから特定のサーバーへアクセスし、遠隔操作する際に利用されるのがSSHです。

SSL/TLS

SSL(Secure Sockets Layer)とTLS(Transport Layer Security)をまとめて表記した形が「SSL/TLS」です。
2022年現在多く使われているのはTLSの方のようです(SSLは安全ではないそうです)。

SSL/TLSはそれぞれ同じ枠組みの中にあり、SSLがバージョンアップを重ねてTLSに至ったものの、SSLが一般化したことから現在ではSSL/TLSと表記されることが多くなっています。

とのことなので、断りがない場合は以降SSLTLSを同じと捉えて「SSL」と書きます。

暗号化通信であるSSLには、「公開鍵暗号」や「共通鍵暗号」が使われています。
ハイブリッド暗号方式

SSLの流れとしては、
電子署名秘密鍵・公開鍵、CSRなどを使って、サーバー側から「認証局お墨付き、ドメインを保証するサーバー証明書」を送ってもらい、 最初は端末のルート証明書と検証して通信先のドメインを確認します。

そこからやっとSSLの暗号化通信がスタートします。

HTTPSの場合は、SSLで暗号化された通信路をHTTPでやり取りするイメージです。

その後は、共通鍵を使って通信するデータを暗号化して送信、受け取り側は共通鍵を使って復号してやり取りします。

SSLって何?意味や仕組みをわかりやすく解説! | さくらのSSL

ここで少し気になったルート証明書について調べてみました。

https://wa3.i-3-i.info/word16604.html

↑すごく分かりやすくて感動しました。

サーバー証明書/中間CA証明書/ルート証明書の違いとは? | さくらのSSL

さくらサーバーのコラムには

ルート証明書は端末やWebブラウザなどにあらかじめ組み込まれており、Windows Update等で更新されます。

とあります。

詳しくはまたの機会に調べます。

FTPSについて

FTPSもSSLが使われていますが、ファイル転送で「不特定多数への」というのはよろしくない気がします。

FTPSの接続方法について調べたところ、FTPを使うのでサーバー情報やユーザー名・パスワードが必要で、不特定多数ではない事がわかりました。
そりゃそうだ。
この言葉に囚われすぎていたようです。

FTPSの接続方式にはexplicit(明示的な)方式implicit(暗黙的な)方式があり、
「implicit」は

httpsで利用されているTLSに近い。

とあります。

【vsftpd】FTPをSSL/TLSで暗号化する設定 - Qiita

FTPSではFTP同様2つのコネクション ー ポートを使う点などから、セキュリティ面ではSFTPの方が優れていると言われます。

SSH

SSH(Secure Shell)です。

暗号によって保護された通信を通じて遠隔操作を行う1アプリケーション、またはプロトコルを指します。

SSHプロトコルの1つですが、ファイル転送プロトコルではありません。

コマンドラインssh コマンドでサーバーにアクセスすると、たしかに遠隔操作という感じがしました。

gitでpullやpushをする時にSSHを使う方法もありますが、その場合もファイル「転送」が目的ではないようです。

HTTPSと同じようなものかもしれませんが(機能はあるけどその仲間には入らない、みたいな)...すごく気になりますがここでは割愛します。

SSHでサーバーと繋ぐ方法は「パスワード認証」と「公開鍵認証」があります。

現在は公開鍵認証が推奨されています。
しかし下記の記事を見ると、一概に「パスワード認証は危ない」「公開鍵認証がいい」とは言えないようです。

SSHの公開鍵認証における良くある誤解の話 - Qiita

SSHの通信の流れについても、こちらの記事の図を見て覚えた方が良さそうです。

FTPではつながるのにSFTPではつながらない原因

ファイル転送のFTP設定の際、プロトコルを「SFTP」に切り替えるとつながらない、ということがありました。

ずっと原因が分からず、そういうものなのだろう、と思っていました。

いろいろ見てみると、サーバー会社、契約プランによって可能な接続方法は異なるということが分かりました。

本当に“そういうもの”でした...。

エックスサーバーではSFTPのパスワード認証ではつながらない(2022年6月現在)のでFTPでつないでいました。

今思うととても危険なことをしていました!

そこで鍵認証でSSH、SFTP接続を試してみます!

☆続きはこちらの記事に書きました。

暗号化についてもう少し理解を試みる

できればもう少し調べて暗号化について知っておきたいものです。

暗号化のタイミング・対象

一口に「暗号化」と言っても何を暗号化するのか、という対象があります。
ぱっと調べて出てきたのは以下の2つです。

通信の暗号化:SSL

「HTTPの教科書」という本には暗号化について以下のように書かれています。

1つは通信を暗号化する方法です。

SSL等によって安全な通信路を確立してから、その通信路を使ってHTTPの通信を行います。

コンテンツの暗号化:盗聴を守ることはできても、改ざんやなりすましの危険性がある

もう1つは、通信しているコンテンツの内容自体を暗号化してしまうことです。

コンテンツの暗号化を有効にするためには、クライアントとサーバーがコンテンツの暗号化や復号の仕組みを持っていることが前提となりますので、ユーザーが普段使うブラウザとWebサーバーという図式では利用することは難しいでしょう。

SSL通信のところで「データの暗号化・復号」が出てきましたが、もしかしてSSLもコンテンツの暗号化をやっているということなのでしょうか??

え、HTTPSの転送なのにファイルも暗号化するんですか???

ファイル(データ)を暗号化しないで通信するHTTPSの方法もあるのですね。
転送」という言葉が引っかかります。

暗号化の方法

2つの暗号方式が出てくるのでまとめました。

共通鍵暗号方式

またの名を秘密鍵暗号方式。 通信し合う者同士で同じ鍵を使い、そのため第三者に鍵が渡ると危険なので、「共通」「秘密」という名前がついている。

公開鍵暗号方式

通信相手に渡す公開鍵と、自分だけが持ち、人に知られてはいけない秘密鍵を使った暗号方式。
公開鍵と秘密鍵はペアになっている。

RSA(Rivest Shamir Adleman)」も「公開鍵暗号方式」で使われる暗号化の1つだそうです。

どちらにも「秘密」が出てきますね。軽く混乱します。

公開鍵暗号に関して誤解がいろいろとあるようです。

公開鍵暗号に関するQ&A - Qiita

【分かった部分】

公開鍵暗号の一分類である「電子署名

電子署名」は、現実の「印鑑」での喩えに非常にマッチしている。
→ 「はんこ」が秘密鍵、「印影のサンプル」が公開鍵、また「印影のサンプル」の載った「印鑑登録証明」が公開鍵証明書に相当

イメージです。

印影のサンプルというところが一筋縄ではいかないですね。
分かっていないかも。

さいごに

今回はどんどん沼にハマってしまいました。

いろいろな記事を読んでいくと、正しいと思っていたことがひっくり返されたり、何度もスタート地点に立ち返ったり。
一体何を学んだのか。

書く側としても、記事のほとんどが正しくないことを書いているかもしれないという恐ろしさを感じました。

難しいです><

お気をつけください。

おしまい。

Hugoのコードブロックのシンタックスハイライトをカスタマイズする

Hugoのカスタマイズをしています。テーマは「hugo-clarity」です。

記事の本文はマークダウンで書きますが、コードを書く部分をいじりたくなりました。

直したいところ

優先順で挙げると

  1. 文字の色を変えたい
  2. 言語表記の色を変えたい
  3. 行数表示をデフォルトにしたい
  4. 折りたたみの表示を変えたい
  5. ツールチップを日本語にしたい
  6. 「...」に「もっと見る」を入れる

です。

好きな配色の文字だと気分も上がります♪

2.と5. は何とかなるのではないかと楽観的に考えています。

用語のおさらい

そもそもの用語を調べました。

コードブロック(Code block)とは

引用元はこちらです。

コードブロックとはプログラミング言語におけるコードのまとまりのことを指します。

記事で見かけるコードのかたまりを指す言葉かと思いきや、広い意味で使われているのですね。

シンタックスハイライト(Syntax highlight)とは

引用元はこちらです。

シンタックスハイライトとは、テキストエディタなどの文字表示に関する機能の一つで、あらかじめ指定された文中の特定の記号やキーワードなどを他とは異なる色で表示すること。

VS Codeでもシンタックスハイライトを設定したことがあったので、認識はあっていたようです。

以前Hugoでもシンタックスハイライトを設定できるライブラリを教えていただいたことがあるのですが、覚えていない...。

文字の色を変える

まずは一番変えたい文字の色表示(シンタックスハイライト)について方法を調べました。

シンタックスハイライトのライブラリ(シンタックスハイライター)

やはりライブラリなるものがありました。

  • Rouge:Qiitaはこれ
  • highlight.js:対応している言語がたくさん
  • Prism:Rubyでできている
  • syntaxhighlighter:白とグレーのしましまの行
  • code-prettify:Google
  • Pygments:Pythonでできている

もっとありました。

赤い画面のhighlight.js!見たことがありました。思い出しました。

ただ、現在のHugoはChromaというライブラリが使われているそうです。

Pygmentsを参考に、Goでできています。

上記の変えたいことを実現できるか分かりませんが、ここは標準で付いているChromaを使おうと思います。

Chromaを使う

以下記事を元に設定していきます。

hugo 単体でシンタックスハイライト適用 - チラシのすきま

If you run with markup.highlight.noClasses=false in your site config, you need a style sheet.

ドキュメントにもこのように書かれていたので、カスタマイズにはcssファイルが必要なようです。

スタイルはこちらから選び、monokaiにしました。

デフォルトはテーマでオリジナルのシンタックスハイライトが設定されているようです。すでに_syntax.sassファイルがあり、「monokai」とは違う配色でした。

コマンドラインに以下のように打ちました。
なお、hugo server でサーバー起動中ではできませんでした。1度停止させます。

hugo gen chromastyles --style=monokai > syntax.css

すると「monokai」のスタイルが入ったsyntax.cssファイルがルートディレクトリにできるので、然るべき場所に移して、読み込むようにします。

テーマ「hugo-clarity」では、/static/css ディレクトリを作り、中にsyntax.cssファイルを配置しました。
ファイルの読み込みはstaticディレクトリ以下が読み込めるように設定されているようです。

README.md には

However, sometimes you may need to load additional style or script files. In such cases, you can add custom .css and .js files by listing them in the config.toml file (see the snippet below). Similar to images, these paths should be relative to the static directory.

とありました。
翻訳は、

しかし、時には追加のスタイルファイルやスクリプトファイルをロードする必要があるかもしれません。そのような場合には、カスタムファイルである .css.jsconfig.toml ファイルに記述して追加します (以下のスニペットを参照してください)。 画像と同様に、これらのパスは static ディレクトリからの相対パスである必要があります。

でした。

スニペットというのは、その下に書かれていた

[params]
...
customCSS = ["css/custom.css"] # Include custom CSS files
customJS = ["js/custom.js"] # Include custom JS files
...

のことのようです。

はじめconfig.tomlにカスタムcssの設定を書かずに
/layouts/partials/head.html ファイルに読み込みについて記述してみたところ、うまくいきました。

<link rel="stylesheet" type="text/css" href="/css/syntax.css">

このあとにconfig.tomlで設定できることを知りました。

どちらでも同じ結果になりますが、config.tomlに書く方がhead.htmlファイルを触らずに済むのでいいなと思いました。

ちなみに複数ファイルを読ませたい場合は

customCSS = ["css/custom1.css", "css/custom2.css"] 

と書くとできました。

Chromaではうまく色分けしてくれない

これで晴れて「monokai」のsyntax.css ファイルを読めるようになったのですが、肝心のシンタクスハイライトに難点が。

試しにphpのコードを入れてみると、<?php ?> の部分がエラー扱いされ、エラーの配色で表示されてしまいました。

これは...嫌です。

言語によって綺麗に色分けされるものと、ところどころうまく色分けされないものとあるのかもしれません。

私の書き方に問題があるのでしょうか(それもありそう)。

ライブラリを変えて解決できるのであればそうしたいです。

ということで、次は highlight.js を試してみます。

highlight.js を使う

以下記事を参考にさせていただきました。

シンタックス・ハイライト・ライブラリ「highlight.js」の使い方 – ラボラジアン

ファイルをダウンロードしてサーバに上げて読み込む方法で行います。

記事にある highlight.pack.js はダウンロードファイルにはなかったので、highlight.min.js を使ってみます(おそらくバージョン違いによるものかと思います)。

highlight.js のテーマにも「monokai」はありましたが、テーマの数が豊富なのであえて別の「Base16/Onedark」にしてみます。

scriptタグの書き方

2つのファイル

  • onedark.min.css
  • highlight.min.js

の読み込みに加えて以下のコードを書く必要があります。

<script>hljs.highlightAll();</script>

highlight.min.js ファイルを読み込んだ後に書きたいのですが、/layouts/partials/head.html ファイルの </footer> の下に書くのでは highlight.min.js のになってしまいます。

これは1行だけどもファイルとして読み込ませるしかなさそうです。

customJS = ["js/highlight.min.js", "js/highlight_script.js"]

として、highlight_script.js ファイルに hljs.highlightAll(); を入れました。

これでうまくhighlight.jsが適用されました!

この時点での表示はこちらです。

なぜか行数が出るようになりました。そして非表示にできなくなりました。

どうやら

  • 指定した言語をサポートしている → 行数が出る(非表示にできない)、
    さらに言語によって色が違ったり、行数を囲むタグも異なる(行数がドラッグ対象になったりならなかったり)
  • 言語未指定 → 行数の表示・非表示切り替えができる(デフォルトは非表示)

しかも、コードブロックの書き方によっても挙動が異なるようです。
☆これらは ''' で囲って書く方法で調べています。

highlight.jsの導入方法に問題がありそうです。

今度はこちらを参考に進めてみます。

hugoでシンタックスハイライトに対応する

記事に忠実に今回はCDNを使います。

行数表示をする

highlight.jsから行番号を表示させるライブラリも見つけたのでこちらも入れてみました。

highlight.jsに行番号を追加する方法 - kotonoha

すると...

言語間で表示が安定しない

スクショを撮るのを忘れてしまいましたが、行番号が表示されたりされなかったり、二重で表示されたり、
コードブロックの機能(コピーなど)ボタンも出てこなくなったり、
さらにはダークモードからライトモードへの切り替えもできなくなったり...うまくいきませんでした。

おそらくconfig.tomlファイルの [markup.highlight] の設定がよろしくないのかなと思います。

Configure Markup | Hugo

こちらを見ながら設定項目を翻訳してtrueにしたりfalseにしたり...いろいろいじってみましたが、Hugoに翻弄されている感がありました。

ここまでの挙動を見ていると、
Chromaとhighlight.jsが両方動いてしまっているために、表示がおかしなことになっているのではないかと思いました。

Chromaをオフにする方法はあるのでしょうか。

...これ以上は技術的に突き詰められなさそうなので、Chromaに戻して調整していきます!

☆Chromaでのphpのエラー表示の問題は、WordPressテンプレートみたいなhtmlとphpが混在しているコードではだめ、ということが判りました。

Chromaで再度調整する

ここまで悪戦苦闘して、いろいろと諦めることにしました。

結局スタイルは、ページの色味に合わせてsolarized-dark256にしました。よいです。

デフォルトで行数表示させたい

themesにあるcode.jsをコピペして、編集したcode.jsを読ませるようにしました。 とある関数の判定で false だったところを true にしました。

問題なく動いています。が、ベストな書き方かどうかは分かりません。

折りたたみの表示を変えたい

こちらもcode.jsを書き換えました。
行数表示のように簡単にはいかず、試行錯誤しました。
(コメントもたくさん書き込んで...このままコミットします)

関数名をわかりやすくするのは大事だなと改めて思いました。

その他の修正

このように変えました。

言語表記の色を変えたい(その他コードブロックの表示調整) → _custom.sass
ツールチップを日本語にしたい → code.js
「...」に「もっと見る」を入れる → 変更するのをやめました

結果、満足のいくコードブロックになりました!

長かった...

Hugoで使われているパーサー

せっかくなのでGoldmarkとBlackfridayを調べました。

Goldmark・・・markdown(マークダウン)をHTMLに変換するパーサー
Blackfriday・・・HugoでGoldmarkの前に使われていたパーサー

だそうです。

以前もマークダウンについて調べていたことを思い出して記事を確認したところ、
Blackfridayについて調べていました。すっかり忘れています。

パーサー」について調べたことも以前あったような気がします。
なんとなくの理解でここは良しとします。

さいごに

今回はてなブログでやっとコードブロックを書くときに言語指定をしました。

色が付くと見栄えがいいですね。

また、最近ではHugoで調べ物をすると、記事がたくさん出てくるようになりました。

2年前はもっと少ない印象でした。

とてもありがたいです。

私はいつになったらブログを移行できるのでしょうか。

おしまい。

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の表を見てもどうも覚えられず。
覚えられそうで覚えられない。

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

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

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

おしまい。