それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

exim4 のスマートリレーはローカルホストに向かない?

Exim4 を設定してて気づいたんだけど、

Exim4のスマートリレーは、「外部アドレス」が前提になっていて、ローカルホストへは向かないようだ。

詳細設定をいじくり回さないといけなさそう

exim4 のスマートリレーは 127.0.0.1:12345 のように、同一ホストへは転送できない。

これは、exim4 のローカルホストへの配送とバッティングするのだろうか、どうやってもローカルホストの別ポートへExim4がフォワードすることはなかった。

Exim4から、XOAUTH2のDockerにフォワードしたかったんだけど。。

もう、postfix 入れることにした。

サービスアカウントでGmailアクセスできない?

XOAUTH2も、アプリパスワードもめんどくさいので、サービスアカウントをアカウントを作ってGmailのアクセスを試みた。

あれこれ、ためしたけど、無理っぽいなぁ。普通にWEBアプリとして自分のアプリで自分をOAuthするしかなさそう。

参考資料

rsyslog の転送機能でログを集約する

ログ集約

rsyslog/syslog で設定する

受信側

sudo -E cp /etc/rsyslog.conf{,.bak}
sudo systemctl restart rsyslog
sudo -E vim /etc/rsyslog.conf

18行目付近を、有効にする。TCP 514 ( または UDP 514 )

--- /etc/rsyslog.conf.bak       2022-08-29 08:13:05.132978351 +0900
+++ /etc/rsyslog.conf   2022-08-29 08:13:16.340874261 +0900
@@ -18,8 +18,8 @@
 #input(type="imudp" port="514")

 # provides TCP syslog reception
-#module(load="imtcp")
-#input(type="imtcp" port="514")
+module(load="imtcp")
+input(type="imtcp" port="514")

送信側

conf.d が使えるのを確認

$ sudo -E cat /etc/rsyslog.conf | grep rsyslog.d
# Include all config files in /etc/rsyslog.d/
$IncludeConfig /etc/rsyslog.d/*.conf

ファイルを作る

sudo -E touch /etc/rsyslog.d/remote.conf   
sudo -E vim /etc/rsyslog.d/remote.conf

転送先を記入( TCP@@ip_or_host:514, UDPなら@ip_or_host:514)

*.* @@raspi-ubuntu.lan:514     

再起動して反映

sudo systemctl restart rsyslog

実験する

ログを書き出すコマンドが次の通り

logger -p syslog.info -t TEST "Test"

ログをtail して確認

受信側

takuya@raspi-ubuntu:~$ sudo tail -f  /var/log/syslog

送信側

takuya@pi-zero:~ $ sudo tail -f  /var/log/syslog

送信側でログを書き出す。

logger -p syslog.info -t TEST "Hello from pi-zero"

結果

きちんと集約しているのがわかる。

無事に、ログが転送されことがわかる。

転送済ログの分割

受信側では、ログをサーバーごと、エラーレベルごとに振り分けすることが出来る。(参考資料 https://www.secuavail.com/kb/tech-blog/tb-210521-01-2/)

書込みレート

デフォルトでは、5秒、200行まで書き込みを行いますが、200行以降は破棄されてしまいます。 5秒に200行以上ログが転送されるようなシステムの場合、チューニングが必要な項目になります。 https://qiita.com/tasakii/items/10db42392c487d5276da

ログの転送量には上限が決められているので注意する。

journald のとの関係?

journalctl はsyslog に転送する。

$ cat /etc/systemd/journald.conf  | grep -i syslog
#ForwardToSyslog=yes
#MaxLevelSyslog=debug

journalctl と syslog が互いにログを保持しているのでわかにくい。

以前は、syslog に転送しjournald自身は破棄していたため、journaldはvolatile (揮発性)になっていたようだが、いまは、journald もauto(デフォルト動作不明:たぶんファイル保存)で自分でログを保持するので、結果としてsyslog と journaldの両方にログが出来ることになる。

たとえば、次のように複雑に入れ込んで人も、やっぱり両方に出ている。

https://boscono.hatenablog.com/entry/2015/12/30/095620 /dev/kmsg -> imklog -> rsyslog -> /var/log/messages -> journald -> imjournal -> rsyslog -> /var/log/messages という流れになっているっぽいので

参考資料

https://www.kagoya.jp/howto/engineer/itsystem/syslog/ https://www.secuavail.com/kb/tech-blog/tb-210521-01-2/ https://isleofhoso.com/journald-conf/

あとで

journald と syslogd

https://gihyo.jp/admin/serial/01/ubuntu-recipe/0546

エクスプローラでプロパティを開くキーボードショートカット

ファイルのプロパティを開く

矢印でファイルを選択し
Alt-Enter

サクッと開く、TABで移動

開いた項目は「Tab」「Shift-Tab」で移動する

マウスは時間がもったいない。無駄動作が多すぎる

フォルダを右クリック、プロパティまでダウン、クリック。長いわ。カーソルを合わせるのがもうしんどい。

ノートPCの人はおすすめ

ノートPCにマウスを接続しないとまともに使えない人は、ショートカットがおすすめです。

写真アプリでも使えます、

写真(microsoft photos)アプリでも同じような操作ができるようになっている。

参考資料

https://support.microsoft.com/ja-jp/windows/windows-%E3%81%AE%E3%82%AD%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89-%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%82%AB%E3%83%83%E3%83%88-dcc61a57-8ff0-cffe-9796-cb9706c75eec

エクスプローラーのファイル一覧にフォーカスする

Ctr-Tab をすると、サクッとフォーカスするので便利

Win-E  エクスプローラーを開く
Ctrl- Tab ファイル一覧へフォーカス
Ctrl- Shift-Tab 、で左のツリーへフォーカス

エクスプローラーのキーボードショートカットがないと、矢印キーで選択が不便。

画像ファイルから全部のexifを除去する

方法1:exiftool

exiftool -all:all= -r /path/to/file [/path/to/file...]

方法2:mogrify

mogrify -strip /path/to/file

結果

サイズが変わってる。mogrify は画質いじってるのでは・・・

$exiftool -all:all= -r sample1.heic
$mogrify -strip sample2.heic
$exiftool -all  sample1.heic > a
$ exiftool -all  sample2.heic > b
$ ls -l sample*.heic
-rwxrwxrwx 1 takuya takuya 3751758 Aug 25 17:46 sample1.heic
-rwxrwxrwx 1 takuya takuya 2327024 Aug 25 17:46 sample2.heic

exiftool と mogrigfy の差分をみると

きれいに消えてるのがmogrify 、一部残ってるのがexiftool 。

vimdiff a b

どっちを使うのか迷うところであるが、画像をアップロードしたり公開するならmogrify ですかね。

写真のExiftをJSONで取り出す。

ファイルのExiftをJSONで取り出したい。

EXIFJSONで取り出せたら、集計が便利なんじゃないのかと思って調べたら、対応していた。

exiftool -s -G -j ファイル名...

実行サンプル

exiftool -s -G -j 2021-04-04_15-56-43_926.heic  | jq .

出力サンプル

[
  {
    "SourceFile": "2021-04-04_15-56-43_926.heic",
    "ExifTool:ExifToolVersion": 12.42,
    "File:FileName": "2021-04-04_15-56-43_926.heic",

    "EXIF:BrightnessValue": 5.483765591,
    "EXIF:ExposureCompensation": 0,
    "EXIF:MeteringMode": "Multi-segment",
    "EXIF:Flash": "Off, Did not fire",
    "EXIF:FocalLength": "4.2 mm",
    "EXIF:GPSSpeed": 0,
    #略
    "Composite:LensID": "iPhone XR back camera 4.25mm f/1.8"
  }
]

複数ファイル対応です。

exiftool -s -G -j ファイルA ファイルB

複数ファイルを前提としたフォーマットになっています。

出力は次のようになってました。

[
   { Exif 情報 },
   { Exif 情報 },
   { Exif 情報 },
   { Exif 情報 },
]

参考資料

https://stackoverflow.com/questions/2089194/example-of-how-to-parse-exiftool-json-output-in-haskell

windows terminal だけでUAC出来るようになっていた。

Windows Terminal だけでUAC出来るようになっていた。

Windows Terminal に設定が登場していた

いつのまに。。。

UACPowershellの呼び出し。

UACPowershellを起動するのは、Win-X then A でメニューから呼び出していたが、ターミナルから使うほうが便利かもしれない。

ウインドウをすべて閉じるをキーボードから高速で呼び出す

このClose All (すべて閉じる)をキーボード・ショートカットで呼び出す方法は無いのかな。

方法1:タスクバーにフォーカスする

Win - T でタスクバーにフォーカス
矢印キーで選択
Appキーでメニュー呼び出し

Appキー(右クリックメニュー)のキーが、存在しないノートPCのキーボードなどもあるので万能じゃないが、確実。

縦配置で、Win-Tを押したときにフォーカスが動く例。

方法2:ジャンプリストのショートカットを使う

Win-Alt- 数字{1,2,34,5,6,7,8,9} でジャンプリストのショートカットが使える。

左から順に1,2,3,4、5となっている

Win-Alt- 1
Win-Alt- 2
Win-Alt- 3

Appキーを使う方法より、確実だが、5以降は一瞥識別が厳しいので難しいわ。

Windows難しい

写真(photo)とエクスプローラーで便利

写真のプレビューや、エクスプローラーをたくさん開いたときに、ものすごく便利です。

そして、Excelファイルを大量に開いて作業しているときにも重宝します。

参考資料

windowsで最速サインアウト

Windows最速サインアウトにはいろいろある

ショートカットキー の連携

Win+X でメニューを呼び出して、メニューのアクセスキーをたどる

Win - X  → u → i

ロックより確実

ロックだと、ユーザー名が残る。

ADで共同でログインして使い分ける端末だと、ロックが大変。

シャットダウン・メニューを使う

シャットダウン・メールを使う場合

Win -D でデスクトップ表示してから、Alt-F4でシャットダウンメニューをだして、上矢印を2回

Win -D 
Alt-F4
↑
↑

これでもいいけど、Alt-F4が押しづらいので好きじゃない。

参考資料

https://support.lenovo.com/jp/ja/solutions/HT505153

jc と jq の組み合わせで、ファイル名のクォートが便利

jc と jq の組み合わせで、ファイル名のクォートが便利

ファイル名にスペースが入る

次のように、ファイル名にスペースが入って、コマンドで処理するときにIFS(区切り)として解釈されちゃってとっても邪魔。

takuya@:$ls  -l  *5576*
-rwxrwxrwx 1 takuya takuya 2733101 Oct 26  2021 'IMG_5576 (2).JPG'
-rwxrwxrwx 1 takuya takuya     689 Oct 26  2021 'IMG_5576 (3).AAE'
-rwxrwxrwx 1 takuya takuya     689 Oct 26  2021  IMG_5576.AAE
-rwxrwxrwx 1 takuya takuya 2964682 Oct 26  2021  IMG_5576.JPG
-rwxrwxrwx 1 takuya takuya 2439727 Oct 26  2021 'IMG_E5576 (2).JPG'
-rwxrwxrwx 1 takuya takuya 2679346 Oct 26  2021  IMG_E5576.JPG

jc コマンドと jq コマンドでサクッと

これで、確実にクォートがされる。

takuya@:$ ls  -l  *5576*  | jc --ls | jq .[].filename
"IMG_5576 (2).JPG"
"IMG_5576 (3).AAE"
"IMG_5576.AAE"
"IMG_5576.JPG"
"IMG_E5576 (2).JPG"
"IMG_E5576.JPG"

jc コマンドで、JSONにできると、処理が格段に楽ですね。

bashでぶん回すときに、楽ですね。インストールは面倒だけど。

エスケープ処理がめんどくさくて、python/ruby のexec()スクリプト作ることも多いが、jc | jqで少し楽ができそう

関連資料

jc コマンドの例 - コマンドの結果をJSONの配列やオブジェクトにする。 - それマグで!

prinft でクォートする例 - シェルでファイル名をエスケープにしてクォート地獄を解決する。 - それマグで!

montage(画像を並べる)するときにファイル名を入れたい。

montage するときにファイル名を入れたい

画像をならべて、インデックスの画像を生成したい。

インデックスプリントのような、証明写真のような、写真をならべてサクッとインデックス・プリントにしたい。

似た写真があるときに並べて表示することで「違い(差分)」を、目視し、目で差異を確認したいとき、montageコマンドが便利。でもファイル名がわからないと差分がわかってもどこのファイルかが分かりづらい。そこでファイル名を入れたい。

ファイル名をラベルとして表示する例

montage \
    -geometry 1200x \
    -pointsize 150 -font Liberation-Serif-Italic \
    -set label '%f\n%wx%h' \
        "IMG_5576 (2).JPG" 
        "IMG_5576.JPG" 
        "IMG_E5576 (2).JPG" 
        "IMG_E5576.JPG" 
    out.jpg

montage コマンド

montage a.jpg b.jpg c.jpg d.jpg out.jpg

montage コマンドで出力幅指定 (縦幅を省略して自動計算

montage -geometry 1200x 

montage コマンドでファイル名を入れる

montage -set label '%f\n%wx%h'
  • %f ファイル名
  • \n 改行
  • %w
  • %h

montage コマンドにフォントとフォントサイズを指定

montage -pointsize 150 -font Liberation-Serif-Italic 

フォントはフォント一覧から選ぶ。

フォント一覧の取得

montage -list font

出来上がりサンプル

参考資料

lxc のコンテナ内部でパーミッションエラーでapt updateができなくなった。

lxc 内で パーミッションエラー

lxc のapt を最新版にしようとしたらエラーになった。

lxc exec myhost apt update 
chown to _apt:root of directory /var/lib/apt/lists/partial failed - SetupAPTPartialDirectory (1: Operation not permitted)
W: chown to _apt:root of directory /var/lib/apt/lists/auxfiles failed - SetupAPTPartialDirectory (1: Operation not permitted)

/var/lib/apt/lists の中がおかしい

nobody になってる。。。

lxc exec myhost  ls -l /var/lib/apt/lists/partial/ -ld
drwx------ 1 nobody nogroup 0 Aug 18 19:05 /var/lib/apt/lists/partial/

ただしくはこうなるはず

ssh root@sample ls  /var/lib/apt/lists/partial/ -ld
drwx------ 2 _apt root 6  8月 18 11:48 /var/lib/apt/lists/partial/

lxc のストレージをマウントして修正を試みる

lxc storage list
+-----------+--------+----------------------------------------------+-------------+---------+---------+
|   NAME    | DRIVER |                    SOURCE                    | DESCRIPTION | USED BY |  STATE  |
+-----------+--------+----------------------------------------------+-------------+---------+---------+
| bt01      | btrfs  | /var/snap/lxd/common/lxd/disks/bt01.img      |             | 4       | CREATED |
+-----------+--------+----------------------------------------------+-------------+---------+---------+
| default   | zfs    | /var/snap/lxd/common/lxd/disks/default.img   |             | 2       | CREATED |
+-----------+--------+----------------------------------------------+-------------+---------+---------+

ストレージを利用しているコンテナを確認し、念のため一時的に停止しておく

$ lxc storage info bt01
info:
  description: ""
  driver: btrfs
  name: bt01
  space used: 19.04GiB
  total space: 49.94GiB
used by:
  instances:
  - jitsi
  - nextcloud
  - rails
  - rclone-web
$ lxc stop  jitsi nextcloud rails rclone-web

ストレージをマウントして様子を見る

$ losetup  | grep bt01
/dev/loop7          0      0         1  0 /var/snap/lxd/common/lxd/disks/bt01.img           1     512
$ losetup  | grep bt01 | awk '{print $1}'
/dev/loop7          
$ sudo mount $(losetup  | grep bt01 | awk '{print $1}')  /mnt 
$ ls /mnt
$ sudo ls /mnt/containers/nextcloud/rootfs
$ sudo ls /mnt/containers/nextcloud/rootfs/var/lib/apt

様子を見ると、

$ sudo ls /mnt/containers/nextcloud/rootfs/var/lib/apt
$ sudo ls /mnt/containers/nextcloud/rootfs/var/lib/apt/lists -l
total 145820
drwxr-xr-x 1 root root 0 Aug 18 19:05 auxfiles
-rw-r--r-- 1 1000000 1000000   115942 Jul  9 19:14 debian-mirror.s
-rw-r--r-- 1 1000000 1000000 45506443 Jul  9 18:12 debian-mirror.s
-rw-r--r-- 1 1000000 1000000 52296931 Jul  9 18:14 debian-mirror.s
-rw-r--r-- 1 1000000 1000000 17446169 Jul  9 18:13 debian-mirror.s
-rw-r--r-- 1 1000000 1000000 30235019 Jul  9 18:12 debian-mirror.s
-rw-r--r-- 1 1000000 1000000    44066 Aug 18 17:20 debian-mirror.s
-rw-r--r-- 1 1000000 1000000     9833 Jun 12 04:58 debian-mirror.s
drwx------ 2 _apt root 6  8月 18 11:48 /mnt/containers/nextcloud/rootfs/var/lib/apt/lists/partial/

なんかおかしいので、 全部消して、作り直した。

sudo rm -rf /mnt/containers/nextcloud/rootfs/var/lib/apt/lists 
sudo mkdir /mnt/containers/nextcloud/rootfs/var/lib/apt/lists 
sudo chown 1000000 :1000000  /mnt/containers/nextcloud/rootfs/var/lib/apt/lists 

パーミッションエラーは出なくなった。

なぜ、パーミッションがおかしくなったのかは謎だけど。バックアップとか書き戻しでミスしてたかもしれない。

まとめ

lxc のストレージは直接サワれるっていうことをメモとして残しておく。

パーミッションエラーでもなんとかなるんだなというのが学び。

コマンドの結果をJSONの配列やオブジェクトにする。- jc コマンド

コマンドの結果をいい感じに、配列にしたい

コマンドの実行結果をJSONにしたいと思った。

https://dev.classmethod.jp/articles/jc-tool/

jc コマンド

jc コマンドは、STDINを読み取って JSONに変換してくれる。

数多くのパーサーが用意されてプリインいる、パーサーがなくてもjc はそこそこ便利。

jc コマンドを試す。

$ id | jc --id --pretty

結果はJSONになる。

{
  "uid": {
    "id": 1000,
    "name": "takuya"
  },
  "gid": {
    "id": 1000,
    "name": "takuya"
  },
  "groups": [
    {
      "id": 1000,
      "name": "takuya"
    },
    {
      "id": 4,
      "name": "adm"
    }
  ]
}

プリインストールのパーサーがいっぱいある

(略
    --ls                `ls` command parser
    --ls-s              `ls` command streaming parser
    --lsblk             `lsblk` command parser
    --lsmod             `lsmod` command parser
    --lsof              `lsof` command parser
    --lsusb             `lsusb` command parser
    --nmcli             `nmcli` command parser
    --passwd            `/etc/passwd` file parser
    --ping              `ping` and `ping6` command parser
    --systemctl         `systemctl` command parser
    --systemctl-lj      `systemctl list-jobs` command parser
    --systemctl-ls      `systemctl list-sockets` command parser
    --systemctl-luf     `systemctl list-unit-files` command parser
    --systeminfo        `systeminfo` command parser
    --top               `top -b` command parser
(略

プリインなパーサがなくても大丈夫。いい感じにJSON配列になる。

サクッと行ごとに出力して

$ apt list vim* 2>/dev/null | grep vim | cut -d '/' -f 1  | sort | head -n 5
vim
vim-addon-manager
vim-addon-mw-utils
vim-airline
vim-airline-themes

jc / jq でサクッと整形する。

apt list vim* 2>/dev/null | grep vim | cut -d '/' -f 1  | sort | head -n 5 | \
    jc --kv | \
    jq keys

行ー>配列になる。

[
  "vim",
  "vim-addon-manager",
  "vim-addon-mw-utils",
  "vim-airline",
  "vim-airline-themes"
]

Key/Valueを使ってオブジェクトにする

プリインストールがないなら、脳死で考えるのをやめて キーバリューに仕立てる

Key/Valuegrep / sed /awk で作る

cat /etc/php/8.0/cli/php.ini | grep -v \; | grep = | grep session | sed 's/ = /:/'
session.save_handler:files
session.use_strict_mode:0
session.use_cookies:1
session.use_only_cookies:1
session.name:PHPSESSID
session.auto_start:0
session.cookie_lifetime:0
session.cookie_path:/
session.cookie_domain =
session.cookie_httponly =
session.cookie_samesite =
session.serialize_handler:php
session.gc_probability:0
session.gc_divisor:1000
session.gc_maxlifetime:1440
session.referer_check =
session.cache_limiter:nocache
session.cache_expire:180
session.use_trans_sid:0
session.sid_length:26
session.trans_sid_tags:"a=href,area=href,frame=src,form="
session.sid_bits_per_character:5

jc にかける

cat /etc/php/8.0/cli/php.ini | grep -v \; | grep = | grep session | sed 's/ = /:/' | jc --kv --pretty
{
  "session.save_handler": "files",
  "session.use_strict_mode": "0",
  "session.use_cookies": "1",
  "session.use_only_cookies": "1",
  "session.name": "PHPSESSID",
  "session.auto_start": "0",
  "session.cookie_lifetime": "0",
  "session.cookie_path": "/",
  "session.cookie_domain": "",
  "session.cookie_httponly": "",
  "session.cookie_samesite": "",
  "session.serialize_handler": "php",
  "session.gc_probability": "0",
  "session.gc_divisor": "1000",
  "session.gc_maxlifetime": "1440",
  "session.referer_check": "",
  "session.cache_limiter": "nocache",
  "session.cache_expire": "180",
  "session.use_trans_sid": "0",
  "session.sid_length": "26",
  "session.trans_sid_tags": "a=href,area=href,frame=src,form=",
  "session.sid_bits_per_character": "5"
}

jc コマンドでシェルの実行結果を JSONに変えられる

jc コマンドは対応コマンドはきれいにJSONに代わる。非対応のものは、--kv で Key/Value として処理すれば簡単に jq で解釈できる。

インストール

pip3 install jc

パーサーが無いコマンド

パーサーがない場合は、jc --csv jc --kvJSONにすればいいので、awk コマンドでいい感じに整形すればいい。

整形結果をフィルタするときは、 ` jc --kv | jq .で jq と組み合わせできるのでよい。

Laravel でイベントとハンドラを使って通知を送る

Laravel でイベントとハンドラを使って通知を送る

イベント・リスナ

イベント定義を使って、「なにか」が起きたとき「これをやる」を定義出来る。

ジョブキューで分割すると思いがちだけど、別にシングルスレッドで実行しても構わない。

シングルスレッドでイベントとリスナを使った場合でもソースコードがスッキリするので、積極的に使っていきたい。

業務系のプログラミングの登録・削除や、セキュリティ監査プログラミングなど、複数箇所で似たようなコード(動作ログの保存など)が一箇所にまとまるので便利かもしれない。業務系のワークフローなどはイベント発生連鎖で整理して、イベントハンドラを定義したほうがスッキリすると思う。

イベントとリスナを作って使う

イベントとハンドラ(リスナ)をまとめて定義する。

php artisan event:generate
php artisan make:event SampleEvent
php artisan make:listener SampleListener

イベントを起動するコマンドも定義しておく。

php artisan make:command DispatchEventSample

作成されるファイル

イベントとリスナをペアで作ったので、次のファイルとディレクトリ(フォルダ)が作成される。

app/Events/SampleEvent.php
app/Listeners/SampleListener.php

Eventの定義

いまは、特に何もしない。クラスを作ればイベントとして使える。

<?php

namespace App\Events;

use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class SampleEvent {
  use Dispatchable, SerializesModels;
}
?>

リスナの定義

リスナでは、イベント発生時の動作を定義する

<?php

namespace App\Listeners;

class SampleListener {
  public function __construct () {
    //
  }  
  public function handle ( $event ) {
    dump(get_class($event).'が発生し、リスナが起動しました');
  }
}

イベントとリスナを紐づける。

イベントとハンドラの紐づけは、event:generateでServiceProviderが作成され、その中で行われる。

EventServiceProvider$lisnen変数に配列で登録してあげればいい。

リスナ紐づけは、キー・バリューで登録を書いていくわけだ。

app/Providers/EventServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use App\Events\SampleEvent;
use App\Listeners\SampleListener;

class EventServiceProvider extends ServiceProvider {
  protected $listen = [ // ここに登録する。
    SampleEvent::class => [SampleListener::class],
  ];
  public function boot () {}
  public function shouldDiscoverEvents () {
    return false;
  }
}

イベントを発生させる

イベントを発生させてみる。

メイン関数の代わりに、コンソール・コマンドを次のように作成した。

app/Console/Commands/DispatchEventSample.php

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Events\SampleEvent;

class DispatchEventSample extends Command {
  protected $signature = 'my_event:sample';
  public function handle () {
    SampleEvent::dispatch();
    return 0;
  }
}

イベントを発生させる。

takuya@host $ php artisan event:sample
^ "App\Events\SampleEventが発生し、リスナが起動しました"

これで、イベントが発生し、Laravelがソースコードを飛び回ってイベントに対応したハンドラを起動してくれることがわかる。

リスナに情報を渡す。

Eventが発生したときに、詳細情報を渡す必要があるが、この点においては、詳細情報をどこにいれたり、イベント・クラスの構造には特に定義がない。自分で定義する必要がある。コンストラクタで渡すことだけが決まっている。

イベントの発生箇所

<?php
class DispatchEventSample extends Command {
  protected $signature = 'my_event:sample';
  public function handle () {
    SampleEvent::dispatch(['aaaaaa']); // 引数を渡す。
    return 0;
  }
}

イベント・クラス定義

イベント発生時に保存しておく変数をイベント・クラスに定義して保存する。

<?php

namespace App\Events;

use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class SampleEvent {
  use Dispatchable, SerializesModels;
    // イベント発生時の変数を保存しておく  
  protected $args;
  
  public function __construct (array $arr) {
        //コンストラクタで保存する
    $this->args = $arr;
  }
  public function getEventArg(){
    return $this->args;
  }
  
}

イベント・リスナ定義

イベントクラスから取り出す。

<?php

namespace App\Listeners;

class SampleListener {  
  public function handle (  $event ) {
    $args = $event->getEventArg();//イベント・クラスから取り出す。
    dump($args);
  }
}

注意点

LaravelではEventの構造に特に指定がないので、ハンドラ側でイベントを受け取るときに、クラス指定ができない。

そのため、IDE補完ができない。 もちろん、クラス指定してもいいのだろうが、今後Laravel本体で指定されるかもしれないので、ソースコードに引数の型を直接に指定するのはちょっと怖いと思ったり。

リスナで次のように書きたくなるが、、、Laravelが公式にEventクラスやFacadeを導入してくると困るので

  public function handle ( SampleEvent $event ) {

doc comment で@paramを使うほうが無難かもしれない。

<?php
    /** 
       @param SampleEvent
   */
  public function handle (  $event ) {

イベント発火・通知

イベント発生から通知の送信は切っても切れない関係にある。

そのために、通知を使うと思う。リスナのなかで通知してもいいし、リスナ自身を通知として登録してもいい。

リスナ・通知でクラスを分ける例

<?php

namespace App\Listeners;


use Illuminate\Support\Facades\Notification;
use App\Notifications\MyFirstNotification;

class SampleListener {
  
  public function handle ( $event ) {
        ///イベント・ハンドラの内部で通知を送る。
    Notification::route( 'mail', 'takuya@example.com' )
                ->notify( new MyFirstNotification() );
  }
}

または、通知を直接にイベントハンドラとして登録する。

<?php

class EventServiceProvider extends ServiceProvider {
  protected $listen = [];// ここでは登録せずに
  public function boot () {
      // boot内部でstatic に登録する。
    \Event::listen( SampleEvent::class, function( $event ) {
        Notification::route( 'mail', 'takuya@9b-p.com' )
                    ->notify( new MyFirstNotification() );
      },
    );
  }
    ///略
}

まとめ

イベント・ハンドラに切り分けることで、フックを簡単に作れる。Wordpressでよく見るイベント・フック系の関数をクラス定義で作れるので、知っていればどこを見ればいいかわかりやすくなる。イベント連鎖や、変化があったときの記録、何がが起きたときに何かするという、人間の行動に近いプログラミングが可能になり、ソースコードの再利用性とメンテナンス性が高まる。オブジェクト指向プログラミングの基本だったりする。

イベント・ハンドラは、Slackやメール通知など、通知機能と組み合わせて使うから役に立つ。

さらなる発展

イベント・リスナが複数のイベントを受け取るときはObserverを使うといい。

イベントの処理をキューイングしてあとに回したり、キュー処理のワーカーと、キューイングを別のインスタンスに分けて強靭化できるかもしれない。