20210908のPHPに関する記事は8件です。

結構使うけどすぐ忘れる関数の使い方

結構使うけどすぐ忘れる関数の使い方 phpの関数で使用頻度高いけど毎回ググってる関数の使い方を記憶に定着させるためにメモ。 str_replace 特定の文字列を置換。 $zip = "123-4567"; $zip = str_replace("-", "", $zip); echo $zip; //123456 郵便番号や電話番号などをDBに保存する際に”-”を除いた文字列で保存したい場合など。 私は悪い使い方として 古いプログラムを改修する際に誰が作ったか知らない俺俺関数で吐き出されたhtmlの一部だけ変更したい場合使ったりする。 function OreOreFunction(){ return "<p>俺俺俺俺だよ!</p>"; } $text = OreOreFunction();//俺俺関数 $change_text = str_replace("だよ!", "ですよ!", $text); echo $change_text; //<p>俺俺俺俺ですよ!</p> idをつけたい時などは function OreOreFunction(){ return "<p>俺俺俺俺だよ!</p>"; } $text = OreOreFunction();//俺俺関数 $change_text = str_replace("<p>", "<p id='oreore'>", $text); echo $change_text; //<p id='oreore'>俺俺俺俺だよ!</p> 本当はコードを読んでしっかり理解して関数の中身を理解した方がいいのだろうけど、そんな時間もないし古いコードの癖が強い関数のソースなんて興味が湧かない・・・ explode,implode 特定の文字で文字列を切り分けて配列にしてくれる関数 $ids = array(); $ids[] = 123; $ids[] = 456; $ids[] = 789; $id_text = implode(",", $ids); //どこかに渡す //ゴニョゴニョされる //返ってきた時 $id_array = explode(",", $id_text); var_dump($id_array); //array(3) { [0]=> string(3) "123" [1]=> string(3) "456" [2]=> string(3) "789" } $id_text = implode(",", $id_array); $SQL = "UPDATE SET ********* WHERE id IN({$id_text})"; var_dump($SQL); //string(45) "UPDATE SET ********* WHERE id IN(123,456,789)" データベースから抜き取ったレコードをどっかに渡してゴニョゴニョして返してもらう時、どのidのレコードを編集したか覚えておきたい時に使ったりした。 strpos 文字列の中に特定の文字列が含まれるかどうか こちらも初心者の頃にはどう役に立つかいまいち分からなかったな 注文番号やお問合せ番号などの業務的に使用する番号の頭にアルファベットなどをくっ付けて意味を持たすことはよくある話。 注文番号の一例 T00001 /電話注文 F00001 /FAX注文 W00001 /WEB注文.... $number = "F1234-001"; if(strpos($number, 'F') !== false){ echo "FAX注文です!"; }elseif(strpos($number, 'T') !== false){ echo "電話注文です!"; }elseif(strpos($number, 'W') !== false){ echo "WEB注文です!"; }else{ echo "???"; } //FAX注文です! なぜ条件式が !==false なのかというと、先頭の文字列でマッチしてしまうと0を返してしまうから $number = "F1234-001"; if(strpos($number, 'F')){ echo "FAX注文です!"; }elseif(strpos($number, 'T')){ echo "電話注文です!"; }elseif(strpos($number, 'W')){ echo "WEB注文です!"; }else{ echo "???"; } //??? Fが先頭にあるにも関わらず条件式の最後まで流れていってしまった 0文字目で見つかったから0を返したんですが 0はfalseと見なされてしまうので厳密チェックでしっかり判別しないといけないですね。 strposよく使うのですが、忘れがちなのが引数にどちらを入れていいか分からなくなることがよくある。 逆に入れてしまうともちろん上手く検索されませんので何か良い覚え方は無いものか・・・・・ 疲れたので今回は以上です。 よく忘れる関数はまだまだあると思うのでちょくちょく追加していきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows 10 のWSL2でDocker CEを使ってHello PHP

概要 Windows 10でWSL2環境を用意する。 WSL2環境でDocker CEを使用して、PHPの(開発用)動作環境を作る。 動機 近い将来にDocker Desktopが個人・中小企業を除いて有料化される1ので、一応、代替策を検討しておく。 下記の方法で正しくライセンス問題を回避できているかどうかは保証しません。 運営元の経営のためにも、支払える方は支払うことをお勧めします。 動作環境 Windows 10 WSL2環境 基本的なWSL操作 後述の手順で使うかもしれない。 WSLシェルの起動 シェルの起動方法はいくつかある。 Windowアプリ WindowsのアプリにUbuntuがあるので、実行する。  Microsoft Storeから取得した直後や、リセットの直後はこの方法で開始して、インストールを続行する。 2. コマンドプロンプト/Powershell ubuntu を入力するとUbuntu (WSL)のシェルが開始する。 3. Visual Studio Code ターミナルで+ボタンを押して Ubuntu (WSL) を追加すると、WSLのシェルが開始する。 WSLの再起動 WSLの設定を変更しても反映されていない、と感じたら場合は、再起動すると解決することがある。 cmd REM WSLを停止する wsl --shutdown REM シェルを開始しようとすると再度起動する ubuntu WSLのリセット  Ubuntu(WSL)の設定をどう変えたか分からなくなった場合は、リセットした方が早いかもしれない。Windowsアプリの「アプリの設定」→「リセット」で初期化できる。 手順 (Win) WSL2をインストールする  おおむね公式インストールガイド2に準じる。 補足 ここではLinuxディストリビューションとして"Ubuntu" をMicrosoft Storeで取得する。 バージョン 2 に変換した後、WSLを再起動する必要があるかもしれない。 最初のUbuntu起動時に、ユーザーを追加せずに閉じている。そのため、以降のWSL Ubuntuコマンドは root ユーザーで実行している。 (WSL) Dockerをインストールする 基本的には公式ガイド3に準じる。 cmd REM WSLシェルを開始する ubuntu bash sudo apt-get remove docker docker-engine docker.io containerd runc sudo apt-get update sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg \ lsb-release curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg architectureとUbuntuのバージョンを確認する。 bash uname -a # ここでは amd64 lsb_release -cs # ここでは focal 公式ガイドでは次のコマンドが下記のようになっている。しかし実行しても "E: Unable to locate package docker-ce" のエラーが発生して続行できない。 bash # 公式ガイドのコマンド(機能しない?) #echo \ # "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ # $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null #sudo apt-get update #sudo apt-get install docker-ce docker-ce-cli containerd.io 筆者の場合は対象のリポジトリ追加の処理を変更して解決した。なお上述の architecture とUbuntuバージョンでコマンドが変わるので注意。 Unable to locate package docker-ce on a 64bit ubuntu https://unix.stackexchange.com/a/363058  他にも発生原因は何種類かあるらしい4。 bash # 上の代替策 sudo apt install apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable" sudo apt update sudo apt-get install docker-ce docker-ce-cli containerd.io dockerサービスを起動するとイメージのビルドやコンテナの起動ができるようになる。 bash service docker start docker run hello-world # メッセージが出たらOK # Hello from Docker! # This message shows that your installation appears to be working correctly. (Win) ソースコードとDockerfileを用意する cmd mkdir C:\projects\hellophp\docker mkdir C:\projects\hellophp\src Dockerfileを作る。 C\projects\hellophp\docker\Dockerfile FROM php:8.0.10-apache # 本稿では最小限で動作確認する。 # 慣れたら php.ini の変更など、色々とここに書く。 hello.phpを配置する。 https://www.php.net/manual/ja/tutorial.firstpage.php C\projects\hellophp\src\html\hello.php <html> <head> <title>PHP Test</title> </head> <body> <?php echo '<p>Hello World</p>'; ?> </body> </html> (WSL) 開発用のイメージをビルドする bash cd /mnt/c/projects/hellophp/docker docker build -t hphp . 上記のDockerfileは空フォルダに入れておくと処理が速い5。 (WSL) コンテナを作成する bash docker create --name chphp --publish 80:80 --mount source=/mnt/c/projects/hellophp/src,target=/var/www,type=bind,consistency=cached hphp # コンテナ起動 docker start chphp # curlでhtmlファイルらしきものが取得できるはず。 curl http://localhost/hello.php bash # コンテナ停止 docker stop chphp # 停止中にcurlするとエラーになるはず。 curl http://localhost/hello.php # curl: (7) Failed to connect to localhost port 80: Connection refused (Win) WSLをホストするWindowsからアクセスする .wslconfig を作成する、または次の設定を追記する。 %USERPROFILE%\.wslconfig [wsl2] localhostForwarding=true http://localhost/hello.php にアクセスできるようになるはず。設定後にWSLの再起動が必要かもしれない。 完成 以降はWSLでdockerサービスを起動してコンテナを起動すると、最低限の開発はWindowsで可能なはず。 bash service docker start docker start chphp # コンテナ内でシェルを開始する docker exec -it chphp bash # コンテナ内のシェルを終了する exit # コンテナ停止 docker stop chphp その他 比較的よく使うDockerコマンド bash # 停止したコンテナを削除 docker container prune # コンテナ一覧 docker container list # コンテナ内のディレクトリ・ファイルをホストにコピー # 下記はコンテナ内の /tmp/hellophp/logs を # ホストWSLの /mnt/c/tmp/hellophp/ に #(つまりWindowsの C:¥tmp¥hellophp に)コピーする docker cp CONTAINER_ID:/tmp/hellophp/logs /mnt/c/tmp/hellophp/ # イメージ一覧 docker image list # イメージを削除 docker rmi IMAGE [IMAGE...] # タグがないイメージを削除 docker image prune Docker Desktopが有料化へ、ただし250人未満かつ年間売り上げ1000万ドル(約11億円)以下の組織や個人やオープンソースプロジェクトでは引き続き無料で利用可能( https://www.publickey1.jp/blog/21/docker_desktop250100011.html )(公式 https://www.docker.com/blog/updating-product-subscriptions/ ) ↩ Windows 10 用 Windows Subsystem for Linux のインストール ガイド( https://docs.microsoft.com/ja-jp/windows/wsl/install-win10 ) ↩ Install Docker Engine on Ubuntu ( https://docs.docker.com/engine/install/ubuntu/ ) ↩ 本文で挙げていない例は、シェルでy/Nの確認を受けた時に y を入力していない ( https://stackoverflow.com/a/61430137 )、ディストリビューションが Linux Mintだと$(lsb_release -cs) 部分が適切でない、など。 ↩ Dockerfile のベストプラクティス:.dockerignore ファイルを使う( https://docs.docker.jp/engine/articles/dockerfile_best-practice.html#dockerignore ) ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel ドット記法を使用して配列やオブジェクトから値を取得する

目的 laravelのヘルパーメソッドであるdate_get()を使い、ドット記法で配列やオブジェクトから値を取得する方法をまとめる 方法 下記のように記載することで配列やオブジェクトから任意の値を取得する事ができる。 data_get(配列やオブジェクト, ドット記法での値の指定); 参考文献 https://readouble.com/laravel/6.x/ja/helpers.html#method-data-get
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP if文でfalseと判定される値

目的 PHPのif文でfalseと判定される値をまとめる if文の式でfalseと判定される値 下記のPHP公式ドキュメントのif文の記載に下記のように記載されている。 https://www.php.net/manual/ja/control-structures.if.php 「どのような値が false と評価されるかについては論理値への変換 を参照してください。」とあるのでリンク先を見てみる。 https://www.php.net/manual/ja/language.types.boolean.php#language.types.boolean.casting ドキュメントによると下記の値がif文でfalseとして扱われるようだ。 booleanのfalse integerの0 floatの0.0および-0.0 空の文字列、および文字列の0 NULL(値がセットされていない変数も含む) 属性がない空要素から作成された SimpleXML オブジェクト。 参考文献 https://www.php.net/manual/ja/control-structures.if.php https://www.php.net/manual/ja/language.types.boolean.php#language.types.boolean.casting
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】Class名を変数に入れてインスタンス化or静的に呼び出す

概要 変数にClass名を入れて、別クラスでインスタンス化 or 静的に呼び出したい 共通で使用するクラス 適当なクラス 基底クラス(今回は登場しません) バリデーションクラス(今回は登場しませんが、staticなvalidate()という関数を持っています。) の4つが前提として存在します。 詰まった時のコード 適当なクラス extends 共通クラス{ // 省略 // リポジトリのクラス名を返す protected function getValidationClassName(): string { return 'TestClassName'; } // 省略 } 共通クラス extends 基底クラス{ // 省略 $validate_class_name = $this->getValidationClassName(); $error_validate = $validate_class_name::validate(); var_dump($error_validate); // 実行結果//////////////// --> $error_validateを表示しようとするとエラーになる } どうやら、$validate_class_name::validate($row_data);の部分でエラーが出ているようです。 ベタ書きで、 $error_validate = TestClassName::validate($row_data); としたところうまくいきましたが、何が原因なのかわかりませんでした。 クラス名を変数に入れると動かないのか? PHP: クラスの基礎 - Manual を見る限りだと、そういうわけでもなさそうです。 解決したコード 適当なクラス extends 共通クラス{ // 省略 protected function getValidationClassName(): string { return TestClassName::class; } // 省略 } 共通クラス extends 基底クラス{ // 省略 $validate_class_name = $this->getValidationClassName(); $error_validate = $validate_class_name::validate($row_data); var_dump($error_validate); // 実行結果//////////////// --> 正常に$error_validateが表示される // 省略 } 原因は、 return TestClassName::class; の部分でした。 return 'TestClassName'がダメで、 return TestClassName::class;がうまくいく理由は、 名前空間が解決できていないこと が根本の原因でした。 公式に書いてありましたね... PHP: クラスの基礎 - Manual class キーワードでもクラス名の解決を行うことが出来ます。 クラスの名前が ClassName になっているクラスの完全修飾名を取得するには、 ClassName::class を使います。 これは、名前空間付きのクラスに使うと特に便利です。 同じような詰まり方する人少ないと思いますが、どなたか参考になれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

php artisan make:authにて作成したビューがリダイレクトしてしまう。

経緯 僕が作ったアプリでは、管理者のみがユーザー登録できるように、ログイン した人間のみが、画面遷移できるよう"カスタマイズ"したかった。 解決方法 新しく自分でコントローラーを作ってください!! php artisan make:authを実行すると 下記のようなコントローラーが作成される。↓(これはほんの一部) ConfirmPasswordController ForgotPasswordController.php LoginController.php さらに下記のようなビューもが作成される。↓(これもほんの一部) register.blade.php login.blade.php  やりたかったこと 通常は、ログイン画面から、register.blade.phpに遷移するが。 ログイン→メイン画面→ボタン遷移でregister.blade.phpに繋げたかった。  やらかしたこと。。。 上記にあるLoginController.phpなど、php artisan make:auth を使って、遷移させようとしたら、、 →何やってもリダイレクトしてしまう。 なので、新しく自分で画面遷移専用のコントローラーを作成すること。 関連ワード auth リダイレクト ビュー
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

npm run watchを実行したらエラーが出て、localhost:3000が立ち上がらなかった時の話

いつも、laravel+Vue.jsでSPAを作る時、プロジェクトのあるディレクトリに移動してから$ php artisan serveでlocalhost:8000を立ち上げ、ターミナルの別ウィンドウでプロジェクトのあるディレクトリに移動してから$ npm run watchをしてlocalhost:3000を開いて、書いたコードがどの様に反映されるか確認しながらやってた。 でも突然、なぜかエラーがいっぱい出て、localhost:3000がブラウザで開かなかった。 今回は、そんな時の解決法を備忘録として書いておこうと思います。 エラーの詳細 入力後の出力結果はこちら。 $ npm run watch Error: Missing binding /Users/saya/Documents/laravel_Vue_SPA/node_modules/node-sass/vendor/darwin-x64-72/binding.node Node Sass could not find a binding for your current environment: OS X 64-bit with Node.js 12.x Found bindings for the following environments: - OS X 64-bit with Node.js 10.x This usually happens because your environment has changed since running `npm install`. Run `npm rebuild node-sass` to download the binding for your current environment. at module.exports (/Users/saya/Documents/laravel_Vue_SPA/node_modules/node-sass/lib/binding.js:15:13) at Object.<anonymous> (/Users/saya/Documents/laravel_Vue_SPA/node_modules/node-sass/lib/index.js:14:35) at Module._compile (/Users/saya/Documents/laravel_Vue_SPA/node_modules/v8-compile-cache/v8-compile-cache.js:194:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10) at Module.load (internal/modules/cjs/loader.js:986:32) at Function.Module._load (internal/modules/cjs/loader.js:879:14) at Module.require (internal/modules/cjs/loader.js:1026:19) at require (/Users/saya/Documents/laravel_Vue_SPA/node_modules/v8-compile-cache/v8-compile-cache.js:161:20) at Object.<anonymous> (/Users/saya/Documents/laravel_Vue_SPA/webpack.mix.js:11:21) at Module._compile (/Users/saya/Documents/laravel_Vue_SPA/node_modules/v8-compile-cache/v8-compile-cache.js:194:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10) at Module.load (internal/modules/cjs/loader.js:986:32) at Function.Module._load (internal/modules/cjs/loader.js:879:14) at Module.require (internal/modules/cjs/loader.js:1026:19) at require (/Users/saya/Documents/laravel_Vue_SPA/node_modules/v8-compile-cache/v8-compile-cache.js:161:20) at Object.<anonymous> (/Users/saya/Documents/laravel_Vue_SPA/node_modules/laravel-mix/setup/webpack.config.js:12:1) at Module._compile (/Users/saya/Documents/laravel_Vue_SPA/node_modules/v8-compile-cache/v8-compile-cache.js:194:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10) at Module.load (internal/modules/cjs/loader.js:986:32) at Function.Module._load (internal/modules/cjs/loader.js:879:14) at Module.require (internal/modules/cjs/loader.js:1026:19) at require (/Users/saya/Documents/laravel_Vue_SPA/node_modules/v8-compile-cache/v8-compile-cache.js:161:20) at WEBPACK_OPTIONS (/Users/saya/Documents/laravel_Vue_SPA/node_modules/webpack-cli/bin/utils/convert-argv.js:114:13) at requireConfig (/Users/saya/Documents/laravel_Vue_SPA/node_modules/webpack-cli/bin/utils/convert-argv.js:116:6) at /Users/saya/Documents/laravel_Vue_SPA/node_modules/webpack-cli/bin/utils/convert-argv.js:123:17 at Array.forEach (<anonymous>) at module.exports (/Users/saya/Documents/laravel_Vue_SPA/node_modules/webpack-cli/bin/utils/convert-argv.js:121:15) at /Users/saya/Documents/laravel_Vue_SPA/node_modules/webpack-cli/bin/cli.js:71:45 at Object.parse (/Users/saya/Documents/laravel_Vue_SPA/node_modules/webpack-cli/node_modules/yargs/yargs.js:576:18) at /Users/saya/Documents/laravel_Vue_SPA/node_modules/webpack-cli/bin/cli.js:49:8 at Object.<anonymous> (/Users/saya/Documents/laravel_Vue_SPA/node_modules/webpack-cli/bin/cli.js:366:3) at Module._compile (internal/modules/cjs/loader.js:1138:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10) at Module.load (internal/modules/cjs/loader.js:986:32) at Function.Module._load (internal/modules/cjs/loader.js:879:14) at Module.require (internal/modules/cjs/loader.js:1026:19) な…長い。 冷静に、エラーメッセージを読み解いてみる こんなに長いメッセージが出ると萎えちゃってやめたくなりますが、此処はひとつ冷静に読み解いてみます。 まず注目したのは序盤のメッセージです。 Error: Missing binding /Users/saya/Documents/laravel_Vue_SPA/node_modules/node-sass/vendor/darwin-x64-72/binding.node Node Sass could not find a binding for your current environment: OS X 64-bit with Node.js 12.x Found bindings for the following environments: - OS X 64-bit with Node.js 10.x これによると、どうやらNode.jsのバージョンが今は12系になっているけれど、前は10系使ってたじゃん! と言っている模様です。 あまり記憶はないのですが、そういえば最近、nvmで他のアプリケーションを作ろうとNode.jsの環境を変更しました。(浮気者ですね) 今はNode.jsをグローバルのバージョンを見に行く様になっているので、こういうことが起きるんですね。(多分) ローカルでバージョンをそれぞれ分けるとかやれる様になるといいんですがね。今後の課題です。。 さらにその下の行を読み進めます。 This usually happens because your environment has changed since running `npm install`. Run `npm rebuild node-sass` to download the binding for your current environment. nom installをしてから、環境が変わるとこれが起きますよ。 $ npm rebuild node-sassを実行して、現在の環境用のバインディングをダウンロードしてね、とのこと。 なるほど。 それだけでいいのですか。 $ npm rebuild node-sass Binary found at /Users/UserName/Documents/laravel_Vue_SPA/node_modules/node-sass/vendor/darwin-x64-72/binding.node Testing binary Binary is fine node-sass@4.14.1 /Users/UserName/Documents/laravel_Vue_SPA/node_modules/node-sass これで、また$ npm run watchすれば、ちゃんとlocalhost:3000の画面がブラウザで立ち上がりました! よかったよかった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

メールアドレスからサブドメインを抽出するには

PHPでさっくりできそうに思いますが、実はあまり簡単にいかない問題です。 順番に考えていきましょう。 ローカルパート分割の問題 ドメイン名分割の問題 メールアドレスの仕様 メールアドレスの仕様についてはRFC 5322 & 5321に沿ったメールアドレス(local-part)に使える文字まとめ - Qiitaが詳しかったので参照してください。又聞きの情報ではなくRFC 5322 - Internet Message Format 日本語訳を読み込んでみるのも良いでしょう。 メールアドレス検証のために使えるPHP組み込み機能はfilter_var()とFILTER_VALIDATE_EMAIL(PHP: 検証フィルタ - Manual)です。 $email_addr = 'a@b.com'; $filtered = filter_var($email_addr, FILTER_VALIDATE_EMAIL); if (empty($filtered)) { return null; } メールアドレスのローカルパートにUnicode(UTF-8)を許可するときは、filter_var()の引数にフラグとしてFILTER_FLAG_EMAIL_UNICODEを追加してください。 RFCの仕様にそのまま準拠すべきか、RFCに準拠したメールアドレスをすべて受け入れるべきかというのは多少悩ましい問題ですし、サービスごとの裁量でRFCよりも厳しい基準を設けるもの現実的な判断です。現に私が開発しているWebサービスもそうです。 たとえば、かつてdocomoで発行可能だった hogehoge..@example.com のようなRFC違反のメールアドレスはよく知られています。また、quoted-string形式のメールアドレス表現を受け入れるべきかといった問題も悩ましいものがあります。abc@example.comと"abc"@example.comは同じメールアドレスを表しますが、メールアドレスでログインさせるよくある会員制のWebサイトでquoteのあるなしで別のアカウントを登録可能とあっては、ユーザーも、おそらく運営側としても混乱を招きます。また、残念なことにabc@example.comを"abc"@example.comと冗長にquoteすることで正常に受け入れてくれないMTAもあります1。 そのようなややこしい状況に関してメールアドレスをデータベースにどのように保持すべきかというのは大変に興味深い問題ではありますが、ここでは「quoted-stringも受け入れる」として話を進めましょう。 ローカルパートの分割 問題は、hoge@example.comのようなメールアドレスを ['local' => 'hoge', 'domain' => 'example.com'] に分けるのは簡単か、ということです。 雑に思い浮かぶのは以下のようなコードでしょう。 [$local, $domain] = explode('@', $email_address, 2); これはメールアドレスにquoted-stringを許可しない場合には期待通りに動作するでしょう。しかし、許可するならそうはいきません。 以下のメールアドレスは仕様としてすべて正当です。 ""@example.com "\ "@example.com "@"@example.com "(@_@)"@example.com $email_addr = '"(@_@)"@example.com'; $filtered = filter_var($email_addr, FILTER_VALIDATE_EMAIL); if (empty($filtered)) { return null; } if (!preg_match('/\A(?<local>.+)@(?<domain>[^@]+)\z/u', $filtered, $matches)) { return null; // または例外を投げる } var_dump($matches['local'], $matches['domain']); // string(7) ""(@_@)"" // string(11) "example.com" めでたしめでたし(?)ですね。この正規表現はパートの分割のみを目的としているので、必ず正当なメールアドレスチェックとセットで使わないと脆弱性の原因になります。 ドメイン名の問題 ローカルパートの部分が解決したところでドメイン名の問題にとりかかりましょう。世の中にはドメイン名からサブドメインを取り除きたいという需要があるそうです。 ぱっと思いつくのは @mail.example.ac.jp と @stu.example.ac.jp と @example.ac.jp のようなメールアドレスから大学のドメイン名 example.ac.jp を取り出したい、といった感じでしょう。 どうしてこの処理が難しいのかという問題は、徳丸先生の記事に非常に詳しいです。 ざっくり言うと city.machida.kanagawa.jpのサブドメインはcityですが、city.machida.tokyo.jpにはサブドメインは存在せず、www.city.machida.tokyo.jpのサブドメインはwww.cityではなくwwwです。 実はこの構造はMozilla Firefoxを使っていればアドレスバーで判別できます。 細かすぎて伝わりにくいかと思いますが、サブドメインの抽出は簡易的な処理では困難であることをおわかりいただけたかと思います。 というわけで、このようなドメインのルールは https://publicsuffix.org/list/ で管理されており、PHPの良い実装もあるので、ライブラリを使ってください。 上記の説明通りに分解できてますね。やったあ。サブドメインを除いた部分は$result->registrableDomain()->toString()で取れています。 まとめ 一見して初心者でも簡単に書けそうな処理に見えても意外にそんなことはない。 おまけ この記事の内容と直接関係ないのですが、Public Suffix Listという仕組みの運用について興味深い問題が起こっているので、せっかくなので紹介のみしておきます。読んでください。 正規表現でメールアドレスを処理することの困難さは小飼さんの2009年の記事に詳しいです。 僕は正規表現の大好きなPHPerなので、みなさまもご安全に… 私の知る限り、まさにdocomoがそうでした。是正されていると嬉しいのですが… ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む