- 投稿日:2020-07-14T19:30:37+09:00
【学習記録(PHP)】ハッシュ化のいろいろ
日々の学習を忘れないように記録をつけています。
ハッシュ関数って?
ハッシュ関数は、「ある引数を渡すしたら、でたらめな値を返す関数」です。
このとき、返される値をハッシュ値と呼びます
返ってくる値はでたらめですが、同じ値を渡すと必ずそれに対応した
同じ値が返ってくるという特徴があります。
ハッシュ化には、hash関数、md5関数、sha1関数、password_hash関数がよく用いられます。
暗号化とすこし似ていますが、仕組みは異なるようです。
ハッシュ化は不可逆な変換で、元のデータへの復元ができないのに対して、
暗号化は適切な鍵を使うことで元データを復元することができます。sha1関数
sha1関数は、16進数・40文字のハッシュ値を生成するハッシュ関数です。
qiita shal1.php$str = "yahhooo"; $number = sha1($str);第一引数で入力文字列、
第二引数で返り値の形式を指定できます。sha1の脆弱性
sha1は便利な関数である反面、脆弱性が知られています。
2017年、googleは2つの異なるファイルを使って同じハッシュ値の生成する
“衝突実験”に成功しました。
sha1衝突が悪用されれば、ハッシュ値に依存するシステムで、
ファイルを不正なものに入れ替えられる可能性があるとのこと。
現在ではよりハッシュ値の長いsha2の利用が推奨されています。
- 投稿日:2020-07-14T16:37:40+09:00
Class env does not exist in... の原因を一瞬で突き止める方法
わりとハマったのでメモを残します。
環境
- Laravel 5.8
- PHP 7.1
方法
App\Exceptions\Handler::report()内にdd($exception)を追記し、ダンプしたとこで処理を止めてエラーの内容を確認する。
Class env does not exist in...
Laravelを使っていると、ごく稀に以下のような感じで "Class env does not exist in..." というエラーに遭遇します。
このエラーが出ると、artisanコマンドもcomposerコマンドも動かないし、何をやっても "Class env does not exist" というエラーメッセージが出力されるだけの状態になり、結構面倒です。// こんな感じのエラー。滅多にないが、すごくたまに出る Fatal error: Uncaught ReflectionException: Class env does not exist in /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php on line 788 ReflectionException: Class env does not exist in /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php on line 788 Call Stack: 以下スタックトレース...なおかつこのエラーはenvが無いことが原因でもなければ、よくあるcomposer周りのミスが原因でもない場合が結構あるので、エラーメッセージを額面通り受け取ると、本来不具合がある部分に気づけないまま時間を溶かす恐れがあります。
dd()で本来のエラー原因を探す
「envが無いとか言われたけどenvはある。たぶん何かやらかしたんだろうけど、こんなエラーを引き起こすようなことをした覚えはないし、心当たりもない」
みたいな時は、一旦"Class env does not exist..."から離れてもっとマシなエラーメッセージが得られるようにすれば、解決に近づくことがあります。app/Exceptions/Handler.phppublic function report(Exception $exception) { dd($exception) //<-この1行を追加 parent::report($exception); }これでダンプされた時点で処理が止まり、もっとマシなエラーメッセージとスタックトレースが確認できるようになります。
これで得られたエラーメッセージをもとに探ってゆけば、割と楽に解決にたどり着けるんじゃないかなと思います。
- 投稿日:2020-07-14T13:58:17+09:00
PHP トレイトとは
トレイト
class内の中にclassを追加してそのメゾットなどを再利用することができる
サンプルコード.
sample.php<?php class Car { public function run() { echo "running by Car\n"; } public function horn() { echo "beeeep!! by Car\n"; } } trait Honda { public function run() { parent::run(); parent::horn(); echo "running!! by Honda\n"; } } class Machine extends Car { use Honda; } $machine = new Machine(); $machine->run(); # running by Car # beeeep!! by Car # running!! by Honda参考
- 投稿日:2020-07-14T12:40:39+09:00
PHP - 偶数or奇数を判定する方法
対象の数字が偶数なのか、奇数なのか、if文で判定します。
・パターン1
対象の数字($A)を2で割った時、余りがないかを確認しています。
余りがない場合は「偶数」です。
<?php if($A % 2 == 0){ <偶数の場合> }else{ <奇数の場合> } ?>ビット演算子を使用します。
ビットは2進数なので、2で繰り上がっていきます。
なので、2で割り切れない数字(奇数)は2進数化した10進数の末尾が1となります。
<?php if($A & 1){ <奇数の場合> }else{ <偶数の場合> } ?>◼︎用語の詳細
・A % B
代数演算子。剰余。AをBで割った時の余り。・A & B
ビット演算子。ビット積。
- 投稿日:2020-07-14T11:37:54+09:00
ブラウザの戻るボタンを無効化する
javascriptでブラウザの履歴を操作する
やりたかったことは、
一日一度押せるボタンがあって押したらページ遷移して、遷移先のページから戻るボタンでは前のページに戻らせない(ブラウザバックで戻るとサイドボタンを押せてしまうため)
window.addEventListener('DOMContentLoaded', function () { // 戻るボタンを制御 history.pushState(null, null, location.href); window.addEventListener('popstate', (e) => { history.go(1); }); });history.pushState(null, null, location.href);
まずこの記述で、偽の履歴を追加しています。
window.addEventListener('popstate', function(e){...}
ブラウザバックのイベントを取得して
history.go(1);
で偽装したページに遷移させます。
以上です。
参考にさせてい頂いたサイト
https://pisuke-code.com/javascript-prohibit-browser-back/
ありがとうございました。
- 投稿日:2020-07-14T09:36:07+09:00
本番運用しているサーバーにgit/GitHubを導入するときのベストプラクティスとマサカリ
本番運用しているphp/Laravelサーバーにgitを入れたいんだけどこれであってるのかな。
こんにちは! @ykhirao です!本番サーバーにFTPでぽちぽちアップロードしている現状ですが、
ssh production
git pull origin master
とかで本番コードを反映させたいなと夢みたいなことを考えています。適当に考えたベストプラクティス(たたき台ともいう)を投稿するのでマサカリください!!!!!手順の大まかなながれ
だいたいこんな状況を仮定すると
- ローカルブランチにgitを導入して、GitHubにpushする
- GitHubのレポジトリがあるし、masterブランチで最新になっている
- 本番コードはだいたいmasterブランチと同じ、一部デプロイ忘れとかある
こんなものかなと思う。それに対してやることは
- サーバー上で
ssh key-gen
して公開鍵・秘密鍵を作成する- GitHubでデプロイキーを登録する(GitHubにデプロイユーザーとか作らなくていいよ!)
- .gitignoreが適切に設定されているか確認する
- 特に.envファイルとか、storageフォルダとか、DB以外に本番とローカル環境で違うものがないかよく確認する
とかでしょうか。
実際の動作で確認する
GitHubにレポジトリ作るのを省略するためにlocalにremote addする。
GitHubとして読み解いてほしい。yk@yk local % git remote add origin /Users/yk/server/project.git yk@yk local % cd .. yk@yk server % ls local prod yk@yk server % git clone --bare local project.git Cloning into bare repository 'project.git'... done. yk@yk server % cd local yk@yk local % git push origin master Everything up-to-dateprodフォルダにcloneしてくる!!で
.git
フォルダを削除して、git管理下に置かれてない状況に戻す。yk@yk server % cd prod yk@yk prod % ls yk@yk prod % git clone /Users/yk/server/project.git . Cloning into '.'... done. yk@yk prod % ll total 8 drwxr-xr-x 5 yk staff 160 7 11 01:54 . drwxr-xr-x 5 yk staff 160 7 11 01:51 .. drwxr-xr-x 12 yk staff 384 7 11 01:54 .git -rw-r--r-- 1 yk staff 16 7 11 01:54 .gitignore -rw-r--r-- 1 yk staff 0 7 11 01:54 README.md yk@yk prod % rm -fr .git # 本番ではstorageフォルダに画像がたくさん溜まっていることを仮定 yk@yk prod % mkdir storage yk@yk prod % touch storage/image-prod.jpg yk@yk prod % touch storage/image-prod2.jpg yk@yk prod % touch storage/image-prod3.jpg # .envファイル を本番用に書き換え yk@yk prod % vim .env yk@yk prod % cat .env APP=prodいろいろlocalで作業した!
yk@yk server % cd local yk@yk local % echo "# README.md を追記" >> README.md yk@yk local % git diff diff --git a/README.md b/README.md index e69de29..7039b84 100644 --- a/README.md +++ b/README.md @@ -0,0 +1 @@ +# README.md を追記 yk@yk local % git commit -am "Update" [master 1b9e11d] Update 1 file changed, 1 insertion(+) yk@yk local % git push origin master Enumerating objects: 5, done. Counting objects: 100% (5/5), done. Delta compression using up to 4 threads Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 298 bytes | 298.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) To /Users/yk/server/project.git cf9a63e..1b9e11d master -> masterさてこの状況で本番にgitを導入しようと思います。
- 少しlocalのほうが進んでいる
- .envファイルが書き換わるとまずい
- storage配下に画像たくさんあるけど.gitignoreしているから大丈夫なんだっけ?
とかそんな感じの疑問を抱えつつ、見てみます。
サーバーで
ssh-keygen
をして 公開鍵を GitHubのこのあたりhttps://github.com/XXXXX/XXXXX/settings/keys に登録すると、privateレポジトリでも、いい感じにpullとかできるようになります。デプロイユーザーとかをGitHubに登録しなくても大丈夫です。本番のプロジェクトにはgit initされていない状態を確認する。
yk@yk prod % git log fatal: not a git repository (or any of the parent directories): .gityk@yk prod % git init Initialized empty Git repository in /Users/yk/server/prod/.git/ yk@yk prod % git log fatal: your current branch 'master' does not have any commits yetmasterブランチもないし、操作できない状態なので、remoteでGitHubとかを指定して、pullしてきましょう。
yk@yk prod % git remote add origin /Users/yk/server/project.git yk@yk prod % git pull origin master remote: Enumerating objects: 7, done. remote: Counting objects: 100% (7/7), done. remote: Compressing objects: 100% (4/4), done. remote: Total 7 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (7/7), 516 bytes | 103.00 KiB/s, done. From /Users/yk/server/project * branch master -> FETCH_HEAD * [new branch] master -> origin/master error: The following untracked working tree files would be overwritten by merge: .gitignore README.md Please move or remove them before you merge. Abortingいい感じ。
yk@yk prod % git log fatal: your current branch 'master' does not have any commits yet yk@yk prod % git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore README.md nothing added to commit but untracked files present (use "git add" to track)まだlogも見れないのでブランチを切り替えて一旦コミットする
yk@yk prod % git checkout -b master-backup Switched to a new branch 'master-backup' yk@yk prod % git add . yk@yk prod % git commit -m "マスターのバックアップ" [master-backup (root-commit) a6fd38c] マスターのバックアップ 2 files changed, 3 insertions(+) create mode 100644 .gitignore create mode 100644 README.md yk@yk prod % git branch * master-backup大丈夫そう。masterにチェックアウトしてみる
yk@yk prod % git checkout master Branch 'master' set up to track remote branch 'master' from 'origin'. Switched to a new branch 'master' yk@yk prod % git log commit 1b9e11d15bda2ecdec75a8d205bdbdec185a9388 (HEAD -> master, origin/master) Author: Yuki <yuki.dees39th@gmail.com> Date: Sat Jul 11 02:01:05 2020 +0900 Update commit cf9a63ebb4676faa949aa29a9fdf56244ed313d0 Author: Yuki <yuki.dees39th@gmail.com> Date: Sat Jul 11 01:48:36 2020 +0900 init
Update
という変更はまだprod
フォルダには適応させてなかったのに、きちんとpullされていることがわかりますね!
これで最新ブランチの適応はOKかと思います。yk@yk prod % cat .env APP=prod yk@yk prod % ls storage image-prod.jpg image-prod2.jpg image-prod3.jpg
.env
ファイルとかstorege/
とか.gitignore
に記述しているものは書き換わってないことがわかります。
もちろんlocalファイルはAA=local
ときちんとなったままです。yk@yk prod % cat ../local/.env APP=localまとめ
こんな感じでサーバー上で
git init
してremote add
checkout -b
git add .
git commit
git checkout master
って流れでいい感じにgit適応できる気がしたのです。あまりこのあたりちゃんとまとめられてなかったので、サーバー運用得意な方いたら編集リクエスト、
コメント欄記入、自分なりのやつを投稿、何でも構いませんので後世に残せるいいドキュメントになればと思ってます。。
- 投稿日:2020-07-14T09:12:18+09:00
Laravel ルートパラメータ持ちのURLにリダイレクトする
目的
- ルートパラメータが設定されているURLにパラメータ値を指定してリダイレクトする方法をまとめる
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.5) ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) プロセッサ 2 GHz クアッドコアIntel Core i5 メモリ 32 GB 3733 MHz LPDDR4 グラフィックス Intel Iris Plus Graphics 1536 MB
- ソフトウェア環境
項目 情報 備考 PHP バージョン 7.4.3 Homwbrewを用いて導入 Laravel バージョン 7.0.8 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする 前提条件
- 実施環境と同じ環境が構築されていること。
前提情報
- 公式ドキュメントを読んでおくと理解が早いかもしれない。
/user/{user_id}
にリダイレクトする方法を記載する。ルーティングには下記の様な記載があるとする。ルーティング設定部分のみ記載する。
web.phpRoute::get('/user/{user_id}', 'UserHomeController@index');読後感
- ルートパラメータが設定されているURLへのリダイレクト方法が分かる。
- 名前付きルーティングにパラメータを渡す方法が分かる。
実施(一つのルートパラメータ)
ルーティングファイルを開きリダイレクト先のルーティングの記載を編集し、ルーティング情報に名前をつける。
web.phpRoute::get('/user/{user_id}', 'UserHomeController@index')->name('user.index');リダイレクト処理を行いたい場所に下記の記載を行う。(下記が実行されると
/user/1
にリダイレクトする。)return redirect(route('user.index', [ 'user_id' => 1, ]));実施(複数のルートパラメータ)
ルーティングファイルを開きリダイレクト先のルーティングの記載を編集し、ルーティング情報に名前をつける。
web.phpRoute::get('/user/{user_id}/{age_id}', 'UserHomeController@index')->name('user.index');リダイレクト処理を行いたい場所に下記の記載を行う。(下記が実行されると
/user/1/2
にリダイレクトする。)return redirect(route('user.index', [ 'user_id' => 1, 'age_id' => 2, ]));簡単なまとめ
ルーティング情報に名前をつける方法
//ルーティング情報->name('ルーティング情報の名前') Route::get('/user/{user_id}/{age_id}', 'UserHomeController@index')->name('user.index'); //上記の記載でルーティング情報に「user.index」という名前が付けられた。名前付きのルーティング情報にリダイレクトする方法
return redirect('ルーティング情報に付けた名前');リダイレクト先のURLにルートパラメータがあるときにリダイレクトする方法
return redirect('ルーティング情報に付けた名前', [ 'ルートパラメータ' => リダイレクト時にルートパラメータに入る値, );参考文献
- 投稿日:2020-07-14T08:49:22+09:00
【Laravel】実行されたDBクエリの確認ができるやつを書いた
はじめに
自分はここ最近Laravelを使ったお仕事をしているのですが、こういった悩みがありました。
- QueryBuilder便利だけど、実行されるクエリが全くわからん
- レビューする側だと共感していただけるのでは??
- 狙ったクエリが書かれているのか自信が無い
- このメソッド叩くと無駄なクエリが流れるんだが
- etc
今までは、PHPUnitでseederなどでデータを用意してクエリを実行するメソッド書いてアサーションして、お、期待するレコード取れたよね、やったやった!!で済ましていたのですが、いちいち条件網羅するデータを用意するのが難儀だったりします。
(実行されるクエリだけ分かればいいケースも中にはあると思うのです)くぅ、実行されるクエリだけ見たい、見せたい、それさえ出来れば、、そんな悩みを抱えていたのであります。1
実行されるクエリの確認をなるべく楽にサクッとできるものが欲しい!! というわけで、Laravelで実行されたDBクエリの確認できるやつ書いた
— しまぶ (@shimabox) July 8, 2020
shimabox/laqu: Laqu is Laravel Db Query Helper. https://t.co/vYLHeXUxukLaravelで実行されたDBクエリの確認ができるやつを書いてみました。2
(よく分からない前フリですいません。。)何を解決するか
Laqu(ラク?レイク?) は、
- 期待するクエリが流れているかPHPUnitでアサーション可能です
- traitを用意しています
- クエリが流れるメソッドを渡すと、どのようなクエリが流れるか確認することができます
- 実行されたクエリの中から一番早かったもの、一番遅かったものが抽出できます
- ソートも可能です
- ビルド後(パラメータがバインド済み)のクエリが確認できます
を行うことができます。実行時間のところはBasic Database Usage - Laravel - The PHP Framework For Web Artisansで出された値(time)を利用しているので、そこまで当てにならないかもしれませんが実行されたクエリの確認は行なえます。
なお、開発中に利用されることを想定していますのであしからずです。
要件
PHP7.2
以上- Laravel
5.8.x
,6.x
,7.x
とりあえずなるべく最新のLaravelが動く環境であればOKだと思います。
インストール
composerを使います。
$ composer require --dev shimabox/laqu使い方
QueryAssertion
期待するクエリが流れているかPHPUnitでアサーションするためのものです。
traitです。assertQuery()
を使います。<?php use Laqu\QueryAssertion; use Tests\TestCase; class QueryAssertionTest extends TestCase { use QueryAssertion; private $exampleRepository; protected function setUp(): void { parent::setUp(); $this->exampleRepository = new ExampleRepository(); // 仮 } public function queryTest() { // 基本的な使い方 $this->assertQuery( // クエリが実行される処理をクロージャに渡します function () { $this->exampleRepository->findById('a123'); }, // 期待するクエリを書きます 'select from user where id = ? and is_active = ?', // バインドされる値を配列で定義 // (bindするものがない場合は空配列を渡すか、引数は渡さないでOK) [ 'a123', 1, ] ); // 複数のクエリを確認 // 基本的には 1メソッド1クエリの確認 を推奨しますが、中には1メソッドで複数クエリが流れる場合も // あると思います。 // その場合は下記のようにクエリとバインド値を配列で対になるように定義してください。 $this->assertQuery( function () { // 例えばこの処理で複数のクエリが流れるとします $this->exampleRepository->findAll(); }, // 期待するクエリをそれぞれ配列で定義 [ 'select from user where is_active = ?', // ※1 'select from admin_user where id = ? and is_active = ?', // ※2 'select from something', // ※3 ], // バインドされる値を二次元配列で定義(bindするものがない場合は空配列を渡してください) [ [ // ※1. 1, ], [ // ※2. 'b123', 1, ], // ※3 はバインド無しの想定なので空配列を渡します [], ] ); } }こんな感じで、クエリのアサーションが行えます。
QueryAnalyzer
クエリが流れるメソッドを渡して、どのようなクエリが流れたのか確認することができます。
QueryAnalyzer::analyze()
で実行されたクエリの結果(Laqu\Analyzer\QueryList
)が取得できます。
※ QueryListの中身はLaqu\Analyzer\Query
です<?php use Laqu\Facades\QueryAnalyzer; /** @var Laqu\Analyzer\QueryList */ $analyzed = QueryAnalyzer::analyze(function () { // クエリが実行される処理をクロージャに渡します $author = Author::find(1); $author->delete(); }); /* Laqu\Analyzer\QueryList {#345 -queries: array:2 [ 0 => Laqu\Analyzer\Query {#344 -query: "select * from "authors" where "authors"."id" = ? limit 1" -bindings: array:1 [ 0 => 1 ] -time: 0.08 -buildedQuery: "select * from "authors" where "authors"."id" = 1 limit 1" } 1 => Laqu\Analyzer\Query {#337 -query: "delete from "authors" where "id" = ?" -bindings: array:1 [ 0 => "1" ] -time: 0.03 -buildedQuery: "delete from "authors" where "id" = '1'" } ] } */ dump($analyzed);この取得結果をもとに
一番実行時間が早かったクエリの抽出
extractFastestQuery()
/* Laqu\Analyzer\Query {#337 -query: "delete from "authors" where "id" = ?" -bindings: array:1 [ 0 => "1" ] -time: 0.03 -buildedQuery: "delete from "authors" where "id" = '1'" } */ dump($analyzed->extractFastestQuery());一番実行時間が遅かったクエリの抽出
extractSlowestQuery()
/* Laqu\Analyzer\Query {#344 -query: "select * from "authors" where "authors"."id" = ? limit 1" -bindings: array:1 [ 0 => 1 ] -time: 0.08 -buildedQuery: "select * from "authors" where "authors"."id" = 1 limit 1" } */ dump($analyzed->extractSlowestQuery());クエリの実行時間でソート
sortByFast()
,sortBySlow()
/* array:2 [ 0 => Laqu\Analyzer\Query {#337 -query: "delete from "authors" where "id" = ?" -bindings: array:1 [ 0 => "1" ] -time: 0.03 -buildedQuery: "delete from "authors" where "id" = '1'" } 1 => Laqu\Analyzer\Query {#344 -query: "select * from "authors" where "authors"."id" = ? limit 1" -bindings: array:1 [ 0 => 1 ] -time: 0.08 -buildedQuery: "select * from "authors" where "authors"."id" = 1 limit 1" } ] */ dump($analyzed->sortByFast()); /* array:2 [ 0 => Laqu\Analyzer\Query {#344 -query: "select * from "authors" where "authors"."id" = ? limit 1" -bindings: array:1 [ 0 => 1 ] -time: 0.08 -buildedQuery: "select * from "authors" where "authors"."id" = 1 limit 1" } 1 => Laqu\Analyzer\Query {#337 -query: "delete from "authors" where "id" = ?" -bindings: array:1 [ 0 => "1" ] -time: 0.02 -buildedQuery: "delete from "authors" where "id" = '1'" } ] */ dump($analyzed->sortBySlow());ビルド後のクエリ取得
getBuildedQuery()
Laqu\Analyzer\QueryList
から要素を特定して利用します。
※ QueryListは配列として扱えます// select * from "authors" where "authors"."id" = 1 limit 1 echo $analyzed[0]->getBuildedQuery(); // delete from "authors" where "id" = '1' echo $analyzed[1]->getBuildedQuery();が利用できます。
Helper
ヘルパーとして以下機能も提供しています。
QueryLog
QueryLogはBasic Database Usage - Laravel - The PHP Framework For Web Artisansの処理をラップしたものです。
QueryLog::getQueryLog()
<?php use Laqu\Facades\QueryLog; $queryLog = QueryLog::getQueryLog(function () { Author::find(1); }); /* array:1 [ 0 => array:3 [ "query" => "select * from "authors" where "authors"."id" = ? limit 1" "bindings" => array:1 [ 0 => 1 ] "time" => 0.12 ] ] */ dump($queryLog);察している人は察していると思いますが、他のメソッドはこの機能をフル活用しています。
そのため、実行時間関連はそこまで精密ではありません。QueryHelper
クエリとバインドパラメータを渡すと、実行されるクエリの確認ができます。
※ pdo-debug/pdo-debug.php at master · panique/pdo-debug を利用しています
QueryHelper::buildedQuery()
<?php use Laqu\Facades\QueryHelper; $now = Carbon::now(); $from = $now->copy()->subDay(); $to = $now->copy()->addDay(); $query = 'select * from authors where id in (?, ?) and name like :name and updated_at between ? and ?'; $bindings = [ 1, 2, '%Shakespeare', $from, $to, ]; $buildedQuery = QueryHelper::buildedQuery($query, $bindings); // select * from authors where id in (1, 2) and name like '%Shakespeare' and updated_at between '2020-07-07 00:37:55' and '2020-07-09 00:37:55' echo $buildedQuery;Formatter
その他機能として、クエリのフォーマット機能も提供しています。
こちらは、doctrine/sql-formatter: A lightweight php class for formatting sql statements. Handles automatic indentation and syntax highlighting. を利用しています。
デフォルトはNullHighlighter
を利用していますが、Cli、HTMLでのフォーマットも可能です。詳しくは https://github.com/shimabox/laqu#queryformatter を参照してください。
その他
Facade
Laravelっぽく?ファサードを使ってみました。
実際に作ってみると、どういう仕組で動いているのかなんとなく理解が深まった気がします。GitHub Actions
みようみまねでGitHub Actionsを使ってみました。
laqu/run-tests.yml at master · shimabox/laqu
これで、PHPのバージョンやLaravelのバージョンごとにテストの実行やlintができているのでそこそこ自信をもって提供できています。おわりに
というわけで長くなってしまいましたが、Laravelで実行されたDBクエリの確認ができるやつを書いた話は以上となります。
shimabox/laqu: Laqu is Laravel Db Query Helper.
もしよかったら使ってみてください。
(使ってみて、ここはこうしたらいいとか、こうすべきだとかあればプルリクください)
QueryBuilderなどを使ってクエリを作るところと実行するところを分けている設計ならば、toSql()を使って確認することも可能でしょうがそんなフェーズはとうに過ぎていた。。 ↩
Basic Database Usage - Laravel - The PHP Framework For Web Artisans のように、
enableQueryLog()
などを書くのも方法としてはありますが、それをいちいち書くのも面倒だったのでライブラリで吸収しちゃおうという作戦です ↩
- 投稿日:2020-07-14T08:44:42+09:00
Laravel で作ったシステムに Excel をアップロードしたら拡張子が zip になった
環境
環境は
ローカルのHomestead
と本番のVPS
の2つで、どちらでも発生。
- Laravel Framework 5.8.24
- PHP 7.3.4 (PHP 7.2.16)
- Ubuntu 18.04.2 LTS (CentOS Linux release 7.6.1810)
- Homestead (ConoHa VPS Laravelイメージ)
何がどうなった?
エクセルをアップロードし、
$file->guessExtension()
で拡張子を取得したところzip
となってしまった。
さらに$file->getMimeType()
をみたところapplication/zip
となっていた。クライアントの情報を取得する関数を使うと、それぞれ
$file->guessClientExtension()
が
xlsx
となり、$file->getClientMimeType()
はapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet
となった。
表にまとめると以下。
extension mime_type larval zip application/zip client xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet エクセルの mime_type なっが。
どうして?
finfo
がうまく判定できないらしい。
ライブラリをガンガン追って行ったところ、Laravel の問題というよりは、内部で使用している Symfony のクラスの内部で使用しているfinfo
に行きついた。
実際にfinfo
で mime_type を判定させてみると、application/zip
となってしまった。
application/zip
から拡張子を推測するとzip
になるという流れである。
StackOverflow の関連記事
上記記事を読むと、ほかの Microsoft の Office 系ファイルもダメらしい。どうする?
解決策はいくつかあると思う。
- Symfony の該当クラスは複数の guesser を扱えるので、Laravel で頑張って拡張する
- finfo を修正する
- 素直にクライアントの拡張子を利用する
などなど。
自分は 上記のどれも採用せず、拡張子を推測するための関数をつくった(理由は後述)。
作ったと言っても、Laravel/Symfonyが用意したものと、先人の知恵を組み合わせただけだが。基本方針
- 拡張子に関してはクライアント(ファイルアップロード者)を信じるという選択は取りたくない
- Laravel を拡張するのはしんどそう
- finfo を修正するのは移植性がない
- 不具合報告があったのはとりあえず excel だけ
参考ソース
public function guessExtension(UploadedFile $file): string { $extension = $file->guessExtension(); if ($extension !== 'zip') { return $extension; } $guessedMimeType = $file->getMimeType(); $clientMimeType = $file->getClientMimeType(); if ($guessedMimeType === $clientMimeType) { return $extension; } $map = [ 'application/vnd.ms-word.document.macroEnabled.12' => 'docm', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx', 'application/vnd.ms-powerpoint.template.macroEnabled.12' => 'potm', 'application/vnd.ms-powerpoint.addin.macroEnabled.12' => 'ppam', 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' => 'ppsm', 'application/vnd.ms-powerpoint.presentation.macroEnabled.12' => 'pptm', 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx', 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx', 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx', 'application/vnd.ms-excel.template.macroEnabled.12' => 'xltm', 'application/vnd.ms-excel.addin.macroEnabled.12' => 'xlam', 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' => 'xlsb', 'application/vnd.ms-excel.sheet.macroEnabled.12' => 'xlsm', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx', ]; return $map[$clientMimeType] ?? $extension; }作った後に「 Symfony の同様の力技を行使する guesser を使う手もあったな」などと思ったけど、いろんな mime_type を検討するのはしんどいので、とりあえずこれで。
- 投稿日:2020-07-14T00:27:06+09:00
Dockerで構築したLaravel環境に、VSCodeでステップ実行デバッグを仕掛ける
元記事 Dockerで構築したLaravel環境に、PHPStormでステップ実行デバッグを仕掛ける
← 元元記事 最強のLaravel開発環境をDockerを使って構築する【新編集版】目的
Dockerで構築したLaravel環境で実行されているPHPコードに対して、
Microsoft製の「VSCode」からステップ実行デバッグができるようにしたい。概要
PHP定番デバッグツール xdebug を使用して実現していきます。
元記事でサーバサイド側の設定まで設定済みであることが前提。
本記事ではクライアント側であるVSCodeの設定のみ記載します。
(元記事、元元記事共に大変参考になりました。感謝。。)環境
- 冒頭に書いた元記事および元元記事の手順でLaravel環境を構築済であること。
- VSCode最新版を使って開発している方。(Mac版で動作確認済。)
方法
クライアント側作業(お手元のMac)
VSCodeを起動する。
PHP Debugをインストールしていない場合は PHP Debug - Visual Studio Marketplace をインストールしておく
VSCodeでdocker-laravel プロジェクトを開く。
メインメニューからデバッガ設定画面を開く。
launch.jsonを作成後、以下の設定を貼り付ける
- portはサーバ側で設定した値と同じになるようにする
- pathMappings
- サーバ側のアプリケーション配置場所:"/work/backend"
- クライアント側のアプリケーション配置場所:"${workspaceRoot}/backend"
{ "version": "0.2.0", "configurations": [ { "name": "Listen for XDebug", "type": "php", "request": "launch", "port": 9000, "pathMappings": { "/work/backend": "${workspaceRoot}/backend" } } ] }.vscode/launch.jsonが下記画像のように作成されていればOK
* ドキュメントルートがDocker-backendの下にブレークポイントの設定
- 一時停止した時の挙動が確認ができるように、変数に文字をセットするようコードを修正。
-- Web.php <?php use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', function () { $hoge = 'welcome'; return view($hoge); });デバッグ開始
ブラウザでPHPサーバーにアクセスしてみる。
18行目で処理が一時停止、左側のタブで変数に格納された文字列を確認できる
変数にマウスオーバーしても値を確認できる
参考リンク