この記事は「LITALICO Engineers Advent Calendar 2024」の5日目の記事になります!
はじめに
こんにちは!
元・福祉ソフト所属1で現在LITALICOでエンジニアやっております、@ogifsと申します!
「日本の福祉をもっとよくしたい」x「障害のない社会をつくる」を心に日々頑張っております!
なぜファイルパスについて調べようと思ったのか
とあるシステムでFTPでファイルを送信する処理を実装していたのですが、FTPでファイル送信をする前にローカルファイルを指定するパスをmb_convert_encoding
でUTF-8からShift-JISにエンコードをして、addslashes
でエスケープ処理をしていました。
addslashes(mb_convert_encoding($filePath, 'SJIS-win', 'UTF-8'));
該当のシステムはWindowsのローカルファイルを指定していたので、パスをダンプしてみると以下のようにバックスラッシュで記載されていました。
C:\hogehoge\fugafuga\piyopiyo\テストだよーん.txt
何故ローカルパスをエンコードをする必要があったのか・そもそもエンコードが必要ないのでは?・なんでスラッシュではなくバックスラッシュ?と疑問が出てきたので、調べることとしました。
エンコードの必要性について
PHPには過去「5C問題」と呼ばれる問題があったようです。
「5C問題」とは、日本語環境におけるPHPで文字エンコーディングを扱う際に発生する問題で、特にShift_JIS(CP932)で「¥(円記号)」が「\(バックスラッシュ)」に変換される現象を指します。
- 具体的な問題点
- ファイルパスの誤認識
Windows環境でファイルパスをC:\path\to\fileのように記述すると、C:¥path¥to¥fileとして認識されることが期待されます。しかし、¥が\に置き換わることで、エスケープ処理が誤解される可能性があります。- 文字列処理の不整合
日本語文字列を含む場合、特定の文字が不正にエンコード・デコードされ、データの整合性が失われることがあります。
この問題が発生していたのがPHP5系の頃だったそうなので、この問題に対応する為に、エンコード処理を入れていたのかもと思いました。(該当のシステムは当時PHP5系で動作していました。)
現在ではPHPはUTF-8を文字コードとして利用することを推奨していますが、Windowsファイルを利用する際は、現在でもエンコードをしてファイルシステムとの互換性を持たせるために、適切にエンコードを行う必要があるとのことでした。
$remote_file = '/hogehoge/fugafuga/piyopiyo/テストだよーん.txt';
$path = 'C:\hogehoge\fugafuga\piyopiyo\';
// 日本語ファイル名なのでエンコードしてあげる
$tmp = mb_convert_encoding('テストだよーん.txt', 'SJIS-win', 'UTF-8');
$filePath = $path . $tmp;
// ファイル送信!
ftp_put($ftp, $remote_file, $filePath, FTP_BINARY);
念の為、手元のPHP7.1環境でパスをエンコードしない処理を作成してみて検証してみました。
エンコードしない状態でも問題なくファイルが参照され、アップロードされました!
// どちらのパスもエンコードしない
$remote_file = '/hogehoge/fugafuga/piyopiyo/テストだよーん.txt';
$path = 'C:\hogehoge\fugafuga\piyopiyo\テストだよーん.txt';
// ファイル送信!
ftp_put($ftp, $remote_file, $filePath, FTP_BINARY);
各OSでの区切り文字の取り扱いについて
各OSで利用できる区切り文字に大きな違いはないと思っていたので、改めて調べてみると以下の取り扱いでした。
- Winodws系:
\・¥
(バックスラッシュ・円マーク)又は/
(スラッシュ)が利用できる - UNIX系(LinuxやMacOSなど):
/
(スラッシュ)のみ利用ができる
この前提があったからこそ、Windowsといえどスラッシュで良いのでは?と思っていました。
何故Windowsだけバックスラッシュを採用しているの?
なんでWindowsだけ独自にバックスラッシュを採用しているのか、がふと気になったので、ChatGPTに聞いたところ以下の回答が得られました。
- 初期のMS-DOSの設計
1970年代から1980年代初頭にかけて、ファイルシステムの設計は多様でした。UNIXではスラッシュ(/)がファイルパスの区切り文字として使用されていました。一方、MS-DOS(Microsoft Disk Operating System)の初期バージョンは、ファイルシステムの階層構造を持たず、ファイルパスの概念がシンプルでした。- スイッチ文字の競合
MS-DOSがコマンドラインのオプション(スイッチ)を指定するために、スラッシュ(/)を使用していました。例えば、dir /wのように、スラッシュをコマンドオプションの区切りに使っていました。このため、UNIXのようにスラッシュをファイルパスの区切り文字として採用することはできなかったのです。- バックスラッシュの採用
Microsoftは階層構造を持つディレクトリを導入する際に、スラッシュの代わりにバックスラッシュ(\)をファイルパスの区切り文字として使用することにしました。これにより、スイッチ文字としてのスラッシュとファイルパスの区切り文字の競合を回避できました。- 互換性維持のための継続
その後、WindowsはMS-DOSの設計を引き継いだため、現在でもバックスラッシュがファイルパスの区切り文字として使用されています。Windowsはスラッシュ(/)をファイルパスとしてもある程度サポートしますが、公式にはバックスラッシュが標準とされています。
Windowsはスラッシュ(/)をファイルパスとしてもある程度サポートしますが、公式にはバックスラッシュが標準とされています。
Windowsとしての標準はバックスラッシュである!と決まっているからこそパスの指定はバックスラッシュにしているのだなとわかりました。
まとめ
- PHPでWindowsのファイルを利用する際は、エンコードするのが安全である
- Windowsのパスはバックスラッシュ・スラッシュどちらも利用できるが、標準としてはバックスラッシュであるので、バックスラッシュを利用する
最後に
今回ファイルパスについて調べてみましたが、思ったよりも歴史が深く、すごく勉強になりました。
日頃あまり意識しない事がバグに繋がったりもするので、コードや処理の意味をしっかり考える・検証するというのも非常に大事だと感じました。
割と意識をしていない部分で、詰まったり問題になったりすることは多々あると思っているので、気をつけようと思います(慢心はいけない)
日々勉強!を意識しながら引き続き頑張っていきます!!!
明日は自分の先輩である@k_kitamuraの記事です!
お楽しみに!
-
2021年2月に子会社化・2023年1月に吸収合併されました。 ↩