PHP でファイルシステムを作ろう
五⼗嵐 進⼠ / sji / sj-i / @sji_ch
⾃⼰紹介
@sji_ch
SNS 上でのアイコンは GitHub が⾃動⽣成した奴
⽣まれも育ちも仙台
PHP カンファレンス仙台とかやった
ふつうのサラリーマン
株式会社インフィニットループ仙台⽀社所属
スマホゲーのサーバサイドプログラマ
⼀昨年娘ができた
かわいい
絵本好き
Agenda
ファイルシステムとは
FUSE とは
PHP で FUSE を利⽤する実例
ファイルシステム
ファイルシステムってこういうやつ
ファイル
コンピュータで扱う⼀まとまりのデータ
⽤途に合わせて⾊々なのを沢⼭使う
⼈間はへっぽこ→分類整理が要る
⼈間の脳容量は有限
沢⼭ファイルあるとわけわかに
ディレクトリ(フォルダ)で分類整理
⼊れ⼦にして整理できる
このへんのふわっとした仕組み全体=ファイルシステム
コンピュータ上でファイルをどう扱うか
何をファイルとして扱うか
どういう仕組みで管理するか
通常は OS がアプリケーションに提供
余談︓任意階層のファイルシステムは Multics 由来︖
https://softwareengineering.stackexchange.com/questions/103487/what-was-the- rst-hierarchical- le-system
雑談︓元を辿ればアインシュタインのしわざ︖
Peter Neumann さんが Multics のファイルシステムを設計
「アインシュタインとは2時間以上に渡り⾯会し<中略>こ
の会話がきっかけとなって、私は階層的な抽象化やその他の
形の抽象化に⽣涯魅了されるようになりました」
http://www.csl.sri.com/users/neumann/
無駄話︓Multi → Uni
https://ja.wikipedia.org/wiki/Unixの歴史
OS と ファイルシステム
仮想ファイルシステムと具体的なファイルシステム
ファイルシステムと⼀⼝に⾔ってもですよ
どういう記憶媒体をどう利⽤してファイルやディレクトリ階層を表現するか
ファイルやディレクトリへのアクセス権をどうする
シンボリックリンクやハードリンク、ロックといった機構をどうする
ファイルの破損に備えたデータの冗⻑化やジャーナリングの仕組みもどうしよう
各⽅式の間にトレードオフ
最適な仕組みは状況により異なる
仮想ファイルシステム
アプリケーションがファイルを扱う際のインターフェースを抽象化して提供
アクセス先に応じ具体的なファイルシステム実装が切り替わる
1 つのアプリケーションコードで⾊々な仕組みに対応できる
これアレだよね、制御の反転て奴では︖︖︖
具体的なファイルシステム(FAT とか EXT-4 とか)へのアク
セス処理が、VFS のインターフェースを実装
アプリケーションからの扱いが統⼀できる
カーネル的な扱いも⼀部統⼀できる
しかしカーネルモードのプログラムを作るのはむずい
もうわりと修羅の世界
カーネルと同じ権限でプログラムが動作
つまりちょっとした不具合でシステム全体がクラッシュ
クラッシュしたらマシンの再起動が必要
セキュリティ的なリスクも勿論上がる
OS よく分かりません
OS はアプリケーションを楽に作って動かすためのもの
アプリケーションプログラマに対して低レベルの実装詳細を隠蔽
多くのアプリケーションプログラマーは OS 内部の機構をよく知らない
そしてカーネルといえばネイティブコード
何もかもアプリケーションと違う︕怖い︕
ここで FUSE ですよ
Filesystem in USEr space ですよ
FUSE は(雑に分けたら) 2 パート
libfuse
libc
libc
FUSE
Ext3
...
VFS
ls -l /tmp/fuse
./hello /tmp/fuse
Kernel
Userspace
NFS
CC BY SA 3.0 Unported ( )
FUSE structure.svg ( )
User:Sven@Wikipedia ( )
https://creativecommons.org/licenses/by-sa/3.0/deed.en
https://commons.wikimedia.org/wiki/File:FUSE_structure.svg
https://commons.wikimedia.org/wiki/User:Sven
C の関数呼べばファイルシステムが作れるよ︕
つまり C を使わなくてもいいってことだよ︕
Python:
Ruby:
Rust:
Go:
https://github.com/libfuse/python-fuse
https://github.com/lwoggardner/rfusefs
https://github.com/zargony/fuse-rs
https://github.com/hanwen/go-fuse
利⽤例は⾊々
SSHFS:
s3fs:
Docker Desktop:
https://github.com/libfuse/sshfs
https://github.com/s3fs-fuse/s3fs-fuse
https://www.docker.com/blog/new- lesharing-implementation-in-docker-desktop-windows/
Linux 以外でもいけるよ︕
dokany (Windows)
Windows 上で動くユーザモードでのファイルシステム作成⽤ライブラリ
Linux の FUSE 互換のラッパーライブラリを持つ
元々は未踏ユースで淺川浩紀さんという⼈が dokan というのを作っていた
dokan の fork の dokanx の fork で今も更新されてるのが dokany
https://github.com/dokan-dev/dokany
macFUSE (Mac OS X)
Mac 版の FUSE
Linux の FUSE の API のスーパーセットを提供
Linux ⽤の FUSE 実装がわりと簡単に移植できる
https://osxfuse.github.io/
PHP で FUSE を叩く
sj-i/php-fuse
https://github.com/sj-i/php-fuse
あなたと FUSE,
今すぐダウンロー
ド
composer require sj-i/php-fuse
備考: PHP 5 ⽤だと C 拡張もあったよ
https://github.com/gree/php-fuse
API 紹介
そもそもの VFS の話
アプリケーションがファイルアクセス⽤のシステムコールを
呼び出す
open とか read とか write とか
VFS はアクセス先に応じ使うファイルシステム実装を振り分
け
FUSE では OS とユーザプロセスが通信
あらためてさっきの図
libfuse
libc
libc
FUSE
Ext3
...
VFS
ls -l /tmp/fuse
./hello /tmp/fuse
Kernel
Userspace
NFS
CC BY SA 3.0 Unported (https://creativecommons.org/licenses/by-sa/3.0/deed.en)
FUSE structure.svg (https://commons.wikimedia.org/wiki/File:FUSE_structure.svg)
User:Sven@Wikipedia (https://commons.wikimedia.org/wiki/User:Sven)
FUSE では VFS の処理振り分け先がユーザプロセスと通信
相⼿は FUSE の C ライブラリを使って作られたデーモン
VFS からの open とか read とかの要求へ応答する
実際の C のコード
fuse_operations という構造体へ open や read 等のコールバックを登録
fuse_operations を fuse_main() へ渡すとカーネル側からの通信を待つループに
PHP から FFI でも基本は⼀緒
⽣の FFI が使い勝⼿びみょいので PHP 側でラッパー⽤意
たとえば、CData を避ける
FFI で C の構造体へアクセスする際には FFICData を通す
stdClass ばりに型としては何もない奴
実⾏時 C の構造体のメンバが全部 FFICData のインスタン
スの動的プロパティとして⼊る
IDE や静的解析などによる型の⽀援を殆ど受けられない
そうだラップしよう︕
fuse_operations と同じメンバを定義したクラスを⽤意
CData の fuse_operations を⽣成してメンバの中⾝を詰めら
れるメソッドを持つ
各メンバには psalm の callable ⽤型アノーテーションを付け
る
間違ったシグネチャは静的に怒られるよ︕
でもやっぱり IDE の補完も欲しいよ︕
オブジェクト指向ってやつでなんとかして︕
FuseOperations を取り出せるインターフェースとして、
Mountable を⽤意
FuseOperations ⾃体も Mountable の実装、$this を返すだけ
マウントをとりまくろう︕
Mounter を⽤意
Mountable をとって指定されたパスへマウントする
ザ・リッチインターフェース
FilesystemInterface を⽤意
Mountable を継承
fuse_operations へ詰めるコールバックと同じシグネチャのメ
ソッドを全て持つ
50 メソッドくらいある
めっちゃ補完される嬉しい
余計な⼿間は trait で誤魔化す
fuse_operations は使うコールバックだけ詰めれば良い
FilesystemInterface の全メソッド実装は⼤変
デフォルト実装を埋めるだけの trait を定義
メソッドの実装がこのデフォルト trait に由来するものの場合
fuse_operations に詰めない(Re ection で気合判定)
数メソッド実装すればファイルシステムが作れる︕
FS の実装例
何か PHP らしいのをファイルシステムにしたい
php-variable-fs (PHP といえば連想配列だよね︕)
連想配列とかオブジェクトは⼊れ⼦にできる
階層構造を持つデータということは⽐較的ファイルシステム
向き(︖)
ライブラリとして composer install できる
https://github.com/sj-i/php-variable-fs
var_dump みたいなノリで変数をマウントしよう︕
連想配列をマウントした時点でプログラムの実⾏が停⽌
OS からのファイルアクセス⽤通信を待ち受ける状態に
もちろん phpstorm からも開けるよ︕ファイルだからね︕
wordpress-fs (PHP といえば WordPress だよね︕)
WordPress の記事データをファイルシステムへマウント
https://github.com/sj-i/wpfs
めんどくさいから細かいところは省くよ︕
WordPress の記事データはタクソノミーで階層化して⾒れる
タクソノミーをディレクトリ扱いするとよさそう
なのだが、準備時間の都合で今回は割愛
ID と slug にもとづいたファイル名でずらっと記事データを並べる
脱線︓PHP 以外はみんな WordPress をマウントしてるよ︕
https://github.com/abhiyerra/wpfs
https://github.com/kmbt/wpmount
昨今はもう猫も杓⼦も Eloquent
https://github.com/corcel/corcel
phpstorm で記事が読み書きできるよ︕
WordPress implements Filesystem
ファイルなので grep 記事データを検索できる
ファイルなので sed で⼀括置き換えが可能
ファイルなので phpstorm から記事データを開ける
ファイル操作をする全てのツールが WordPress の記事データへのアクセスに使える
まとめ
ね、簡単でしょう︖(実際に)
かんたん(FUSE) × カンタン(FFI) = 超簡単︕
すべてが F(ilesystem) になる
全てをマウントする⾔語、PHP
おしまい

PHP でファイルシステムを作ろう