- 投稿日:2020-09-23T22:43:07+09:00
引数の渡し方
今日は
laravel
においてview
ファイルへの引数の渡し方について記録していくよ~TestController.phppublic function test(){ $data1 = 1; $data2 = 2; return view('test_view' , ['data1' => $data1 , 'data2' => $data2 ]); }//1 2
compact
関数を使って同じ出力になる書き方:TestController.phppublic function test(){ $data1 = 1; $data2 = 2; return view('test_view' , compact('data1' , 'data2')); }//1 2以上だよお~
- 投稿日:2020-09-23T21:43:02+09:00
php ファイルのアップロード
up.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>img_upload</title> </head> <body> <?php var_dump($_FILES['file']); $filename = './img/'.date('Y-m-d H:m:s') . '.jpg'; if ($_FILES['file']) { move_uploaded_file($_FILES['file']['tmp_name'], $filename); } echo "<img src = '$filename'>" ?> //enctypeは決まり文句 <form action="up2.php" method="POST" enctype="multipart/form-data"> <input type="file" name="file" accept="image/*"> <input type="submit" value="ファイルをアップロードする"> </form> </body> </html>
- 投稿日:2020-09-23T21:30:07+09:00
PHP Laravel 6 おすすめ映画投稿サイト作成過程 4:投稿機能作成編
ルーティング
登録画面にはcreateを使用します。
| GET|HEAD | recommends/create | recommends.create | App\Http\Controllers\RecommendController@create | web |DBへの登録にはstoreを使用します。
| POST | recommends | recommends.store | App\Http\Controllers\RecommendController@store | web |viewファイルの作成
登録が必要な内容をビューファイルに設定しましょう。
今回は、以下の内容を登録対象とします。
※画像のアップロードに関しては、後日編集します
登録する内容 映画タイトル 映画URL 映画概要 感想 formタグで入力データをrecommends.store へ送る
@csrfを忘れないようにしましょう。※不正な動作を防ぐために記載します。詳細はGoogleで検索してください。
recommend/resources/views/recommends/create.blade.php<form method="POST" action="{{route('recommends.store')}}"> @csrf <table> <thead> <tr> <th>タイトル</th> <th>映画URL</th> <th>概要</th> <th>感想</th> </tr> <tr> <th><input type="text" name='title'></th> <th><input type="URL" name='url'></th> <th><textarea name="description" id="" cols="30" rows="10"></textarea></th> <th><textarea name="Impressions" id="" cols="30" rows="10"></textarea></th> </tr> </thead> </table> <button type="submit">登録</button> <a href="{{route('recommends.index')}}">一覧に戻る</a> </form>レコードの保存処理
レコードの保存には、Modelのクリエイトメソッドを使用します。
登録後はindexページにリダイレクトさせました。recommend/app/Http/Controllers/RecommendController.phppublic function store(Request $request) { Recommend::create($request->all()); return redirect()->route('recommends.index'); }一方、allで情報を取得した場合、そこには全ての情報が含まれているため、どの情報を登録するかModelで指定する必要があります。
recommend/app/Models/Recommend.phpclass Recommend extends Model { protected $fillable = [ 'title', 'image_file_name', 'image_title', 'url', 'description', 'Impressions' ]; }
- 投稿日:2020-09-23T19:43:02+09:00
PDOを接続とプリペアドステートメント説明
PDOの接続方法
僕はmysqliに比べて、分かりづらかったので、PDOの接続の仕方を書いてみました。
<?php try { $pdo = new PDO("mysql:dbname=データベース名; host=localhost; charset=utf8mb4", "ユーザー名", "パスワード"); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo "接続完了"; } catch (PDOException $e) { exit(); echo "接続失敗"; } ?>プリペアドステートメントについて
プリペアドステートメントって、初めは分かりづらいですよね。
プリペアドステートメントとは、SQL文を最初に用意しておいて、その後はクエリ内のパラメータの値だけを変更してクエリを実行できる機能のことです。
この機能を利用することでクエリの解析やコンパイル等にかかる時間は最初の一回だけで良くなり、より高速に実行することができます。
また、SQLインジェクション対策に必要なパラメータのエスケープ処理も自動で行ってくれるため、安全かつ効率の良い開発が出来ます。プリペアドステートメントを利用してクエリを実行する流れ
PDOオブジェクトの作成
↓
prepareメソッドでSQL文をセット
↓
bindValue or bindParamでパラメータに値をセット
↓
executeメソッドでクエリを実行
↓
fetch or fetchAllメソッドで結果を配列で取得INSERTだとこんな感じで使います。
$stmt = $pdo -> prepare("INSERT INTO blog (title, text) VALUES (:title, :text)"); $stmt->bindParam(":title", $_POST['title'], PDO::PARAM_STR); $stmt->bindParam(":text", $_POST['text'], PDO::PARAM_STR); $stmt->execute();参考にしたサイト
PDO prepare プリペアドステートメントの使い方
- 投稿日:2020-09-23T19:22:40+09:00
忙しい人のためのphp.ini設定大全
vpsでやっているとphp.iniの設定で苦しめられることがあるので、
備忘録としてまとめておきます
よかったら参考にしてください目次
忙しい方のための推奨設定一覧
allow_url_fopen
allow_url_include
default_charset
disable_functions
display_errors
enable_dl
expose_php
file_uploads
log_errors
magic_quotes_gpc
max_execution_time
max_input_time
memory_limit
register_globals
safe_mode
session.cookie_httponly
session.cookie_lifetime
session.cookie_secure
session.entropy_file
session.gc_maxlifetime
session.hash_function
session.use_cookies
session.use_only_cookies
session.use_trains_sid忙しい方のための推奨設定一覧
設定 推奨値 初期値 allow_url_fopen On On allow_url_include Off Off default_charset UTF-8 disable_functions display_errors Off On enable_dl Off On expose_php Off On file_uploads Off On log_errors On Off magic_quotes_gpc Off On max_execution_time 30 30 max_input_time 60 -1 memory_limit 32MB 128MB register_globals Off Off safe_mode On Off session.cookie_httponly On Off session.cookie_lifetime 0 0 session.cookie_secure On Off session.entropy_file /dev/urandom session.gc_maxlifetime 1440 1440 session.hash_function 1(SHA1) 0(MD5) session.use_cookies On On session.use_only_cookies On On session.use_trains_sid 0 0 allow_url_fopen
Onの場合外部のURLをローカルファイルと同じように読み込むことができる
example.phpfile_get_contents('https://satorunooshie.net');しかしPHP5.2よりも前の場合は
include
やrequire
などでも外部URLを読み込めるためインクルード攻撃を受ける可能性がある
PHP5.2以降ではallow_url_includeに分けられた
インクルード攻撃はexample.phpinclude $_GET['text'];のようにすると攻撃者が自由にサーバー上でコードを実行することができるようになるらしい
またinclude
文の引数にユーザーの入力値を含まない場合でも
ファイルの中身がブラウザから確認できる場合、インクルードファイルの流出によって攻撃の手がかりを与えることになるので注意
インクルードファイルをドキュメントルートよりも上に配置すれば大丈夫allow_url_include
Offのままで良い、理由は前述
default_charset
HTTPヘッダーでの文字エンコードの指定
指定しない場合XSSの危険性があるのでUTF-8
に指定する
特定のページのみ変更する場合はini_set()
関数かheader()
関数を使うdisable_functions
無効にする内部関数の設定
コーディング規約などで利用を禁止している場合や危険な関数を利用しない場合に設定するphp.inidisable_functions = exec,popen,passthru,systemdisplay_errors
Offに設定する
エラーメッセージはHTMLエスケープされずに出力されるのでXSSが可能なことがあるためenable_dl
PHPの拡張モジュールをスクリプト内からロードできる
PHP5.3以降では非推奨expose_php
OnにするとHTTPヘッダーにPHPのバージョンが表示される
が、隠してもセキュリティが向上するわけではないらしいfile_uploads
ファイルアップロード機能を使わない場合はOff
log_errors
必ずOn
magic_quotes_gpc
自動的にバックスラッシュでエスケープ処理が行われる
addslashed()
関数と同じ
PHP5.3で非推奨でPHP5.4で削除max_execution_time
PHPスクリプトの最大実行時間を秒で指定
この値を長く設定するとDoS攻撃のリスクを高めるので注意max_input_time
スクリプトを実行する前の入力データを解析する最大秒数
同様にDoS攻撃のリスクが高まるmemory_limit
PHPが使用可能なメモリの上限を指定する
DoS攻撃やバグによるメモリの課題消費を抑えるために32GBが適切っぽいregister_globals
Onにすると意図しない変数の初期化が行われる可能性がある
以下のコードは同じような危険がある
PHP5.3で非推奨に、PHP5.4で削除されたexample.phpextract($_POST); foreach ($_POST as $key => $val) { $key = $val; }safe_mode
共有サーバーではOn
PHP5.3で非推奨、PHP5.4で削除されたsession.cookie_httponly
セッションCookieにHttpOnly属性を指定
session.cookie_lifetime
セッションCookieの有効期限を指定
0の場合はブラウザを終了するまでsession.cookie_path
セッションCookieを送信するパスの指定
共有サーバーの場合自分のディレクトリを指定しないと、
他のユーザーのディレクトリにアクセスした場合にセッションIDが漏洩したり、セッションが共有される可能性があるsession.cookie_secure
全ページSSLで保護されている場合はOn
session.entropy_file
セッションIDを作成する時のエントロピーソースを指定
存在する場合は/dev/urandom
session.gc_maxlifetime
セッションデータの生存時間を秒で指定
初期値は24分になっている
ただすぐに削除されるとは限らない
session.gc_probabilityとsession.gc_divisorの設定による確率で削除される
長くするとセッションハイジャックのリスクが高まるsession.hash_function
セッションIDを生成するハッシュ関数を指定する
MD5
は128ビット、SHA1
は160ビットなのでビット数の多い分だけセッションIDの推定が難しくなるので安全性が上がるsession.save_path
セッションファイルを保存するフォルダ
デフォルトでtmpになるが共有サーバーの場合は他のユーザーがセッションハイジャックする可能性があるsession.use_cookies
Cookieによるセッション管理を行うかどうか
必ずOnにする
session.use_only_cookies
URLによるセッション管理を禁止するかどうか
必ずOnにする
session.use_trains_sid
透過的なセッションIDをするかどうか
セキュリティ上の問題があるので0
まとめ
安全性を高めれば高めるほど制限が増えるので、ケースバイケースで変更するのがいいと思います!!
- 投稿日:2020-09-23T19:06:34+09:00
PHP の GD ライブラリを使って、「たくさんある画像の中で微妙に色違うやつ当てるゲーム」を少しでもチートされないように作りたい
ソースコードはこちら
「たくさんある画像の中で微妙に色違うやつ当てるゲーム(名前長い)」がよくあると思います。
これをフロントの世界 (HTML/CSS, JavaScript) だけで実装すると、目で答えがわからなくてもチートされて正解できてしまう懸念があります。
それを少しでもチートしにくくしようというのが今回の企画です。今回行う対策
開発者ツールから色の情報がわからないようにする
CSS で背景色を指定すると、開発者ツールから見えてしまいます。
そのため、バックエンドで画像を動的に生成したのち、フロントではそれをただ読み込むだけにします。その他、「正誤判定をフロントエンドで実装しない」などの対策も必要ですが、記事では省略します。
参考ページ
PHP を使います
今回は PHP の GD 拡張モジュールを用いて画像を生成します。
GD 拡張モジュールはよほど古い PHP でなければ同梱されているはずです。
php.ini
を開き、以下のような行があったら先頭のセミコロンを消去して有効にします。;extension=gd2
お手持ちの PHP で
phpinfo()
を実行し、 GD に関する情報が見えていることを確認してください。
次のように見えていれば成功です。実際に作ってみる
GD ライブラリを使って 100×100 の単色画像(色はランダム)を作成し、出力する
$width = 100; $height = 100; // 画像リソースを作成する $img = imagecreate($width, $height); // 色を付ける imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255)); // 出力する $filename = 'images/image.png'; imagepng($img, $filename);
imagecreate
関数で新規画像を作成し、imagecolorallocate
関数で着色、imagepng
関数でファイルに出力します。3 枚の単色画像(1 枚だけ色が異なる)を出力し、正解データを保持する
画像を 1 枚出力するパターンを上でやったので、次は複数枚(今回は 3 枚)出力して、正解を示すものも出力します。
着色パターンは正解・不正解の 2 通り用意すれば OK で、正解データはテキストファイルに数字だけ書いておきます。メインのロジックです。
create_images.php<?php require_once __DIR__ . '/Image/ImageResourceFactory.php'; require_once __DIR__ . '/Image/Image.php'; use Image\Image; function main($imageCount) { $correctColor = array( 'red' => getRandomInt(0, 255), 'green' => getRandomInt(0, 255), 'blue' => getRandomInt(0, 255), ); $incorrectColor = array( 'red' => getRandomInt(0, 255), 'green' => getRandomInt(0, 255), 'blue' => getRandomInt(0, 255), ); $collectImageIndex = getRandomInt(0, $imageCount - 1); $width = 100; $height = 100; for ($i = 0; $i < $imageCount; ++$i) { $filename = __DIR__ . '/../data/image' . $i . '.png'; if ($i === $collectImageIndex) { outputImage($width, $height, $correctColor, $filename); } else { outputImage($width, $height, $incorrectColor, $filename); } } file_put_contents(__DIR__ . '/../data/answer.txt', $collectImageIndex); } function getRandomInt(int $min, int $max = null, int $fixed = null): int { if (isset($fixed)) { return $fixed; } return rand($min, $max); } function outputImage(int $width, int $height, array $color, string $filename) { try { $image = new Image($width, $height); $image->paintIn($color['red'], $color['green'], $color['blue']); $image->saveAs($filename); } catch (Exception $e) { } }メインのロジックを実行するにあたりクラスとして切り出した、画像の処理類です。
Image/Image.php<?php namespace Image; use Exception; use Image\ImageResourceFactory; /** * Class Image * 画像の作成・編集・保存 */ class Image { /** * @var resource $resource - 画像リソース */ public $resource; /** * Image constructor. * @param int $width - 画像の幅 * @param int $height - 画像の高さ * @throws Exception - 画像作成に失敗した場合、例外を返します。 */ public function __construct(int $width, int $height) { $this->resource = ImageResourceFactory::getImageResource($width, $height); } public function paintIn(int $red, int $green, int $blue) { imagecolorallocate($this->resource, $red, $green, $blue); } public function saveAs(string $filename) { imagepng($this->resource, $filename); } }Image/ImageResourceFactory.php<?php namespace Image; use Exception; /** * Class ImageResourceFactory * 画像リソース作成用 */ class ImageResourceFactory { /** * 画像のリソースを取得します。 * * @param int $width - 画像の幅 * @param int $height - 画像の高さ * @return resource - 画像リソース * @throws Exception - 画像作成に失敗した場合、例外を返します。 */ public static function getImageResource(int $width, int $height) { $resource = imagecreate($width, $height); if ($resource === false) { throw new Exception('Failed to create image resource.'); } return $resource; } }正誤判定についても、フロントの JavaScript ではなく PHP で実装し、画面にリンクを貼ります(ここでは省略します)。
感想
この類のゲームは前から作ろうと思っていたのですが、広告系の業務をしていたことから base64 形式を使った方法を探っていました。
base64 では 1 x 1 の黒色・透明画像の作り方ぐらいしか見つからなくて、いろいろ途方に暮れていたのですが、今回 PHP の中に画像を作れるライブラリが同梱されていたことに気づけたのは大きいです。正直なところ、オンライン対戦やインターネットランキングなどを含まないゲームなら、チートするかどうかはエンドユーザの自由であって、わざわざバックエンドに処理をやらせる必要もないでしょう。
ですが、「フロントはすべて見えていて、いくらでも改造できる」という観点から、チートされたくない部分をバックエンドにより実装できたことは、今後の業務にも活かせそうな部分ではあると思います。今回の記事がみなさんの何かのきっかけにでもなれば幸いです。
- 投稿日:2020-09-23T16:02:48+09:00
PHP Laravel 6 おすすめ映画投稿サイト作成過程 3:詳細ページ作成編
showページの追加
recommendsディレクトリにshow.blade.phpを作成します。
コントローラー追加
ルーティングは以下の通りです。
| | GET|HEAD | recommends/{recommend} | recommends.show | App\Http\Controllers\RecommendController@show | webshowメソッドで{recommend}を受け取り、show.blade.phpへ投げます。
なお、recommends/{recommend}の{recommend}にはDBのid番号が入ります。recommend/app/Http/Controllers/RecommendController.phppublic function show(Recommend $recommend) { return view('recommends.show', compact('recommend')); }showで受け取ったデータの出力方法
{{$recommend-> カラム名}}で各データを表示できます。
recommend/resources/views/recommends/show.blade.php<table> <tr> <th>{{$recommend->title}}</th> <th>{{$recommend->image_file_name}}</th> <th>{{$recommend->url}}</th> <th>{{$recommend->description}}</th> <th>{{$recommend->Impressions}}</th> </tr> </table>リンクの作成
showページへのリンクは、以下の方法で設定できます。
なお、今回はindexページのタイトルにリンク設定しました。recommend/resources/views/recommends/index.blade.php<a href="{{route('recommends.show', $recommend->id)}}">{{$recommend->title}}</a>ちなみに、以下でも同じ動作を実現できます。
この表記では、該当するデータを{recommend}としてコントローラー側で受け取っていることが分かり易い反面、ルーティングを変更した場合に機能しなくなってしまうので注意が必要です。recommend/resources/views/recommends/index.blade.php<a href="recommends/{$recommend->id}">{{$recommend->title}}</a>
- 投稿日:2020-09-23T15:50:49+09:00
【Laravel】GuzzleでEUC-JPのページを受信すると文字化け→cURLの受信結果を適切にエンコードして解決
現象
- LaravelでHTTP通信するならGuzzleらしい
- 試す。受信結果が文字化けする
- cURLベースで書き直す
- やっぱり文字化けする
- EUC-JPのページで発生している
解決
以下のエントリを参考に、cURLでの受信結果を適切に変換することで文字化けを防ぐことができた。
- PHPのcurlでeuc-jpのページを取得したときに文字化けするのを防ぐ | 俺日記
- unicode - PHP: Convert curl_exec output to UTF8 - Stack Overflow
コード
function curl_get_contents($url, $timeout = 60) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_ENCODING, 'gzip'); $result = $this->curl_exec_utf8($ch); curl_close($ch); return $result; } function curl_exec_utf8($ch) { $data = curl_exec($ch); if (!is_string($data)) return $data; unset($charset); $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); /* 1: HTTP Content-Type: header */ preg_match('@([\w/+]+)(;\s*charset=(\S+))?@i', $content_type, $matches); if (isset($matches[3])) $charset = $matches[3]; /* 2: <meta> element in the page */ if (!isset($charset)) { preg_match( '@<meta\s+http-equiv="Content-Type"\s+content="([\w/]+)(;\s*charset=([^\s"]+))?@i', $data, $matches ); if ( isset( $matches[3] ) ) { $charset = $matches[3]; /* In case we want do do further processing downstream: */ $data = preg_replace('@(<meta\s+http-equiv="Content-Type"\s+content="[\w/]+\s*;\s*charset=)([^\s"]+)@i', '$1utf-8', $data, 1); } } /* 3: <xml> element in the page */ if (!isset($charset)) { preg_match( '@<\?xml.+encoding="([^\s"]+)@si', $data, $matches ); if ( isset( $matches[1] ) ) { $charset = $matches[1]; /* In case we want do do further processing downstream: */ $data = preg_replace('@(<\?xml.+encoding=")([^\s"]+)@si', '$1utf-8', $data, 1); } } /* 4: PHP's heuristic detection */ if (!isset($charset)) { $encoding = mb_detect_encoding($data); if ($encoding) $charset = $encoding; } /* 5: Default for HTML */ if (!isset($charset)) { if (strstr($content_type, "text/html") === 0) $charset = "ISO 8859-1"; } /* Convert it if it is anything but UTF-8 */ /* You can change "UTF-8" to "UTF-8//IGNORE" to ignore conversion errors and still output something reasonable */ if (isset($charset) && strtoupper($charset) != "UTF-8") $data = iconv($charset, 'UTF-8', $data); return $data; }gzipオプションについて
curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
はサーバー依存です。筆者の環境では、gzipで圧縮されたレスポンスが返ってくるため必要でした。
(参考:PHPのcurl()でレスポンスが文字化けしたら確認すること2つ - PHP | ゆるりの足あと)
- 投稿日:2020-09-23T14:41:26+09:00
PHP Laravel 6 おすすめ映画投稿サイト作成過程 2:一覧ページ作成編
viewファイルの追加
一覧を表示するため、今回は新たに作成したrecommensディレクトリにindex.blade.phpを作成しました。
コントローラーの設定
コントローラー側では$recommendsをviewに渡します。
recommend/app/Http/Controllers/RecommendController.phppublic function index() { $recommends = Recommend::all(); return view('recommends.index', ['recommend' => $recommends]); }ダミーデータの入力
5件のダミーデータを入力します。
今回は、未入力が許されないタイトルとURLのみ指定します。useにコントローラーモデルを追加
recommend/database/seeds/RecommendSeeder.phpuse Illuminate\Database\Seeder;//元から記載されている。 use App\Models\Recommend; //追加。for文で5件のダミーデータを作成
recommend/database/seeds/RecommendSeeder.phppublic function run() { for($i = 1; $i < 6; $i++){ Recommend::create([ 'title' => 'test{$i}', 'url' => 'url{$i}' ]); }; }上記をデータベースシーダーに流し込む
recommend/database/seeds/DatabaseSeeder.phppublic function run() public function run() { $this->call(RecommendSeeder::class); }シーダーを実行
$php artisan db:seed
以上でダミーデータ作成完了です。
index.blade.phpでDB一覧の表示
今回は、タイトルとタイトル画像を一覧で表示することにします。
手段はforEachでの展開です。recommend/resources/views/recommends/index.blade.php@foreach($recommends as $recommend) <tr> <th>{{$recommend->title}}</th> <th>{{$recommend->url}}</th> </tr> @endforeachページネーション
表示数が多くなった場合には、以下の方法でページを分割することができます。
(加筆予定)
- 投稿日:2020-09-23T13:52:21+09:00
core dump と gdb コマンドがあればバイナリ再ビルドしなくてもデバッグできます(PHPコマンドでSegmentation faultになった原因調査)
発生した問題
ある Laravel Artisan のコマンドを実行しようとしたら、即座に他に出力もなく
Segmentation fault
が出て終了しました。
1回目は実行できるのだけど、2回目は実行できない(結論からいうとキャッシュ周りの問題なので、キャッシュがあると死んでました)。$ php artisan command:hogehoge Segmentation faultバージョン情報
$ php -v PHP 7.2.22 (cli) (built: Sep 11 2019 01:44:09) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.2.22, Copyright (c) 1999-2018, by Zend Technologies$ gdb -v GNU gdb (GDB) Amazon Linux (7.6.1-64.33.amzn1)$ uname -r 4.14.146-93.123.amzn1.x86_64問題解決に使えるツール
- gdb
- 昔からデバッグに使われている有名なツールです
- https://www.gnu.org/software/gdb/
- core dump
- プロセスが死んだ時のメモリの状態を吐き出しておくためのファイルは core dump file と呼ばれます
- https://man7.org/linux/man-pages/man5/core.5.html
どうやって問題を追っていくか?
- core dump ファイルを吐き出させる
- Segmentation fault を吐いたコマンドのパスを確認する
- gdb に両方セットで食わせます
gdb <実行コマンドのパス> -c <core dump file>
- 一般的にはPHPコマンド自体を再ビルドしてgdbコマンド経由で実行することでデバッグする方法をよく見ますが、手間がかかりハードルが高いです
- バイナリを再ビルドしなくてもcore dumpさえあれば簡易的なデバッグができるというところで表題の通りです
- gdb コンソールで
where
コマンドを実行するとスタックトレースが表示されます- スタックトレースをとっかかりにググるなり調査します
詳細
1. core dump ファイルを吐き出させる
Segmentation fault が起こった時には core dump を吐き出すことができます
現状の設定を確認。
0
になっているので吐き出されません。$ ulimit -a | grep core core file size (blocks, -c) 0設定を変更します。これを入れると性能劣化すると思うので、出来るだけ本番環境は避けた方が無難です。私の環境では開発環境で問題を再現していたので開発環境にいれました。
※以下のコマンドで反映するのは、sshで接続している1セッションだけです。このセッションでコマンド実行する場合には永続化は不要ですが、他のバックグラウンドプロセスに実行させたり、ApacheによるPHP実行のcore dumpを吐き出させる時には他の記事を参照してください。$ ulimit -c unlimited $ ulimit -a | grep core core file size (blocks, -c) unlimited以下のように改めて問題が起こるコマンドを実行すると
(core dumped)
が追加されているのが確認できます。# php artisan command:hogehoge Segmentation fault (core dumped)core dump file の出力先は以下のファイルに書かれています。参考: https://www.suse.com/ja-jp/support/jp/kb/tids/00100037/
下記の出力は、コマンド実行したカレントディレクトリを示しています。$ cat /proc/sys/kernel/core_pattern core以下のようなファイル出力がされているはずです。
$ ls core.6888 core.68882. Segmentation fault を吐いたコマンドのパスを確認する
$ which php /usr/bin/php3. gdb に両方セットで食わせます
gdb <実行コマンドのパス> -c <core dump file>
最終的に
_emalloc ()
で落ちているのはわかりましたが、情報がまだ足りません。[root@ip-172-31-6-236 html]# gdb /usr/bin/php -c core.6888 GNU gdb (GDB) Amazon Linux (7.6.1-64.33.amzn1) Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-amazon-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /usr/bin/php-7.2...(no debugging symbols found)...done. warning: core file may not match specified executable file. [New LWP 6888] [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Core was generated by `php artisan command:hogehoge'. Program terminated with signal 11, Segmentation fault. #0 0x00000000005a0c11 in _emalloc () Missing separate debuginfos, use: debuginfo-install php72-cli-7.2.13-1.7.amzn1.x86_64 (gdb)4. gdb コンソールで
where
コマンドを実行するとスタックトレースが表示されます上記の状態でgdbの対話状態になっていると思うので、以下のように
where
コマンドを発行します。
今回のケースだと、memcachedのクライアントに問題があり、igbinary という module を使って unserialize する時に問題が発生することがわかりました。(gdb) where #0 0x00000000005a0c11 in _emalloc () #1 0x00007fdbe5286735 in ?? () from /usr/lib64/php/7.2/modules/igbinary.so #2 0x00007fdbe528ce46 in igbinary_unserialize () from /usr/lib64/php/7.2/modules/igbinary.so #3 0x00007fdbe260f829 in ?? () from /usr/lib64/php/7.2/modules/memcached.so #4 0x00007fdbe261247c in ?? () from /usr/lib64/php/7.2/modules/memcached.so #5 0x00007fdbe2613a68 in ?? () from /usr/lib64/php/7.2/modules/memcached.so #6 0x000000000066bd55 in execute_ex () #7 0x00000000005b719b in zend_call_function () #8 0x00000000004fd263 in ?? () #9 0x000000000066b77a in execute_ex () #10 0x000000000066c5f3 in zend_execute () #11 0x00000000005c6d54 in zend_execute_scripts () #12 0x0000000000565af0 in php_execute_script () #13 0x000000000066e9b6 in ?? () #14 0x000000000042d18e in ?? () #15 0x00007fdbf9e14445 in __libc_start_main () from /lib64/libc.so.6 #16 0x000000000042d213 in _start ()5. スタックトレースをとっかかりにググるなり調査します
今回のケースだと この記事 を参考対応を取りました。igbinary シリアライザを使う必然性がなかったので設定の変更を行って解決しました。この対応についての詳細については本記事のスコープから外れるので詳しくは記述しません。
before
$ php -i | grep memcached.serializer memcached.serializer => igbinary => igbinaryafter
$ php -i | grep memcached.serializer memcached.serializer => php => php参考
http://sarface2012.hatenablog.com/entry/20101027
2010年の記事ですが色褪せないです。ありがとうございました。
- 投稿日:2020-09-23T12:30:51+09:00
PHP Laravel 6 おすすめ映画投稿サイト作成過程 1:準備編
備忘録としての制作過程を記録
Laravel を実装(バージョン6)
composer create-project --prefer-dist laravel/laravel recommend "6.*"一部内容の修正
.env
DBはsqliteを使用する。
recommend/.envDB_CONNECTION=sqliteapp.php
ロケーション等の変更。
recommend/config/app.php'timezone' => 'Asia/Tokyo', 'locale' => 'ja', 'faker_locale' => 'ja_JP',DB作成
touch database/database.sqliteDB関連の資料作成
必要なファイルを一括生成
$ php artisan make:model Models/Recommend -aテーブル内容を設定
今回は、以下の6項目
カラム ID 映画タイトル 映画の画像 映画イメージの名前 映画URL 映画概要(空投稿可) 感想(空投稿可) recommend/database/migrations/2020_09_23_105017_create_recommends_table.phppublic function up() { Schema::create('recommends', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('title'); $table->string('image_file_name', 100)->nullable(); $table->string('image_title',100)->nullable(); $table->string('url'); $table->string('description')->nullable(); $table->string('Impressions')->nullable(); $table->timestamps(); }); }マイグレーション
マイグレーションファイルがデータベースに反映されます。
$ php artisan migrateautuの実装
以下の作業で、認証に必要なすべてのビューがresources/views/authディレクトリに生成されます。
ルート定義
composer require laravel/ui "^1.0" --devphp artisan ui vue --auth
- 投稿日:2020-09-23T11:49:42+09:00
画像付きExcelをkintoneに移行してみる
Excelのデータをkintoneに登録したい!!
もちろん、Excelの文字データのみであれば、kintoneに一括登録することはブラウザ上からもできます。が、そのExcelに画像が貼られたファイルの場合はどうでしょう。
文字データと、それに関連付けて画像ファイルも一括登録させたいですね。文字データと、それに関連付けて画像ファイルも一括登録させたい
これを実現してくれるツールがあるということで、separator-excel と cli-kintone を使って試してみました。
流れ
- separator-excel で、CSVファイルと画像ファイルに分離する
- kintoneで、アプリを作成する
- cli-kintone で、kintoneにデータをインポートする
ツールやサービスはこんな流れで進めてみます。
それでは早速、やってみよう。separator-excelで、CSVファイルと画像ファイルに分離する
PHPなどの環境を構築する
separator-excel のREADME を参考に、「必須環境」をインストールしていきます。
// MacOSの場合 brew install git // パッケージを探す brew search php74 brew install php@7.4 brew install composerインストールされていることも兼ねて、Version確認しましょう。
git --version php -v composer -VPHPインストール後にVersionが変わらない場合には、PATHを通そうを参考に。
$ php -v PHP 7.4.10 (cli) (built: Sep 3 2020 18:20:30) ( NTS )よし、PHPのVersion7.4なので、動くはず。
separator-excelをインストールする
インストール手順に沿って、インストールしていきます。
実行してCSVファイルと画像ファイルを作る
インポートする ramen.xlsx Excelファイルを以下のディレクトリにダウンロードしておきましょう。
- ramen.xlsx: separator-excel/tests/_data
separator-excelに指定する出力パラメータは以下のようにしました。
- csv: tests/_output/export.csv
- files directory: tests/_output
$ php ./bin/separator-excel tests/_data/ramen.xlsx -o tests/_output/export.csv -b tests/_output/実行結果を確認する
export.csv と 画像ファイル一式が無事に出力されました〜〜。
kintoneアプリを作る
kintoneの環境を用意し、出力したCSVファイルからkintoneアプリを作成してみます。
kintoneの環境がない方は「kintone開発者ライセンス」を申し込んでください。CSVファイルを読み込んでkintoneアプリを作成する
読み込む文字コード、読み込むフィールドタイプなどをこのように設定しましょう。
添付ファイルフィールドを追加する
添付ファイルフィールドの設定を変更する
特にフィールドコードは重要です。これをCSVのフィールド名と合わせておかないと、インポートできなくなります。
他のフィールドコードも設定する
フィールド名 フィールドコード 場所 場所 感想 感想 星 星 写真 写真 APIトークンを作成する
kintoneアプリの設定画面で、APIトークンを生成し、レコード追加をチェックします。(保存とアプリ更新も忘れずに)
ドメイン、アプリID、APIトークンはcli-kintoneで利用しますので、テキストエディタなどに記録しておきましょう。データを削除する
いまは、画像ファイルが関連付けられていないデータが登録されているため、レコード一覧から削除する か、レコードを一括で削除する かの方法で、データを削除しましょう。
cli-kintone を使ってインポートする
OS環境にあった cli-kintone ファイルをダウンロードします。
separator-excel > tests に、cli-kintoneを配置しました。
次のように実行していきます。※MacOSの場合$ cd separator-excel/tests // <APP_ID>, <FQDN>, <API_TOKEN> は各環境で異なるので、値を設定してください。 $ ./cli-kintone --import -a <APP_ID> -d <FQDN> -e utf-8 -t <API_TOKEN> -f _output/export.csv -b _output // 僕の環境ではAPIトークンはマスクしていますが、次のように実行しました //$ ./cli-kintone --import -a 27 -d sample.cybozu.com -e utf-8 -t G7e~~~~~ -f _output/export.csv -b _output実行すると、ログではインポートされている様子。
[2020-09-21 10:13:47]: Start from lines: 1 - 6 => SUCCESS [2020-09-21 10:13:48]: DONEkintoneアプリでも無事にデータとファイルがインポートされていました〜〜!!
さいごに
今回は、Excelの中にあるデータと画像ファイルを、きちんと関連付けてkintoneにインポートしてみました。Excelで管理しているものはとても多いと思います。
社員名簿、不動産や賃貸リスト、建設現場、備品管理などなど。今あるExcelをクラウド共有サービスのkintoneに移行して、いつでも、どこでも、最新情報にアクセスできる環境づくりはいかがでしょうか。
- 投稿日:2020-09-23T11:06:54+09:00
PHP Laravelのmake:modelオプション
make:modelに用意された様々なオプション
モデルの作成時には、コントローラやマイグレーションファイル等の作成が必要となりますが、いちいち作成するのは面倒です。
make:modelには以下のオプションが用意されているので、状況によって使い分けましょう。$ php artisan make:model -h
オプションの内容は、コンソールに php artisan make:model -h を入力することで確認できます。
内容は以下の通りです。基本的には -a で全て作成することになると思いますが、必要に応じてそれ以外を使い分けましょう。Options: -a, --all Generate a migration, seeder, factory, and resource controller for the model -c, --controller Create a new controller for the model -f, --factory Create a new factory for the model --force Create the class even if the model already exists -m, --migration Create a new migration file for the model -s, --seed Create a new seeder file for the model -p, --pivot Indicates if the generated model should be a custom intermediate table model -r, --resource Indicates if the generated controller should be a resource controller --api Indicates if the generated controller should be an API controller -h, --help Display this help message -q, --quiet Do not output any message -V, --version Display this application version --ansi Force ANSI output --no-ansi Disable ANSI output -n, --no-interaction Do not ask any interactive question --env[=ENV] The environment the command should run under -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
-a で作成されたコントローラー
以下のように遷移先も自動で作成してくれます。
できる限りオプションを使用して作成しましょう。****Controller.phpclass RecommendController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { // } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { // } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { // } /** * Display the specified resource. * * @param \App\Models\Recommend $recommend * @return \Illuminate\Http\Response */ public function show(Recommend $recommend) { // } /** * Show the form for editing the specified resource. * * @param \App\Models\Recommend $recommend * @return \Illuminate\Http\Response */ public function edit(Recommend $recommend) { // } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param \App\Models\Recommend $recommend * @return \Illuminate\Http\Response */ public function update(Request $request, Recommend $recommend) { // } /** * Remove the specified resource from storage. * * @param \App\Models\Recommend $recommend * @return \Illuminate\Http\Response */ public function destroy(Recommend $recommend) { // } }
- 投稿日:2020-09-23T11:05:52+09:00
Laravel バリデーションを設定する ~フォームリクエスト編~
目的
- リクエストにバリデーションを記載する方法をまとめる
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.3) ハードウェア MacBook Pro (16-inch ,2019) プロセッサ 2.6 GHz 6コアIntel Core i7 メモリ 16 GB 2667 MHz DDR4 グラフィックス AMD Radeon Pro 5300M 4 GB Intel UHD Graphics 630 1536 MB
- ソフトウェア環境
項目 情報 備考 PHP バージョン 7.4.3 Homwbrewを用いて導入 Laravel バージョン 7.0.8 commposerを用いて導入 MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いて導入 前提情報
- ターミナルコマンドは基本的に
$ laravel new
コマンドで作成されたアプリ名ディレクトリで実行するものとする。- ブラウザ上でコンテンツを新規作成する際にコンテンツタイトルとコンテンツ詳細に入力がない場合にバリデーションで弾く処理を追加する。
概要
- フォームリクエストクラスの作成
- コントローラの修正
- ビューファイルの修正
- 確認
詳細
フォームリクエストクラスの作成
下記コマンドを実行してフォームリクエストクラファイルの作成を行う。
$ php artisan make:request StoreContentPost下記コマンドを実行して先に作成したフォームリクエストクラスファイルを開く。
$ vi app/Http/Requests/StoreContentPost.php開いたフォームリクエストクラスファイルを下記の様に修正する。
アプリ名ディレクトリ/app/Http/Requests/StoreContentPost.php<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class StoreContentPost extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ //バリデーションルールを記載する //'チェックするデータ名' => 'ルール1|ルール2'の様にルールを定義する 'title' => 'required', 'detail' => 'required', ]; } }コントローラファイルの修正
下記コマンドを実行してコントローラファイルを作成する。
$ vi app/Http/Controllers/ContentController.php開いたコントローラファイルを下記の様に修正する。
アプリ名ディレクトリ/app/Http/Controllers/ContentController.php<?php namespace App\Http\Controllers; //use Illuminate\Http\Request; //下記を追記する use App\Http\Requests\StoreContentPost; use App\Content; class ContentController extends Controller { public function index() { $contents = Content::all(); return view('contents.index', ['contents' => $contents]); } public function create() { return view('contents.create'); } //下記を修正する public function save(StoreContentPost $request) { $new_content = new Content(); $new_content->title = $request['title']; $new_content->detail = $request['detail']; $new_content->save(); return redirect(route('content.index')); } public function detail($content_id) { $content = Content::find($content_id); return view('contents.detail', ['content' => $content]); } public function edit($content_id) { $content = Content::find($content_id); return view('contents.edit', ['content' => $content]); } //下記を修正する public function update($content_id, StoreContentPost $request) { $content = Content::find($content_id); $content->title = $request['title']; $content->detail = $request['detail']; $content->save(); return redirect(route('content.index')); } public function destroy($content_id) { $content = Content::find($content_id); $content->delete(); return redirect(route('content.index')); } }ビューファイルの修正
下記コマンドを実行して新規登録する時に表示しているビューファイルを開く。
$ vi resources/views/contents/create.blade.php開いたビューファイルを下記の様に修正してエラー文を表示できる様にする。
アプリ名ディレクトリ/resources/views/contents/create.blade.php<h1>create</h1> <form action="{{route('content.save')}}" method="post"> @csrf <h3>タイトル</h3> {{-- 下記を追記する --}} @if ($errors->has('title')) @foreach ($errors->get('title') as $title_errors) <p>{{$title_errors}}</p> <br> @endforeach @endif {{-- 上記までを追記する --}} <input type="text" name="title"> <br> <h3>内容</h3> {{-- 下記を追記する --}} @if ($errors->has('detail')) @foreach ($errors->get('detail') as $detail_errors) <p>{{$detail_errors}}</p> <br> @endforeach @endif {{-- 上記までを追記する --}} <textarea name="detail" cols="30" rows="10"></textarea> <br> <input type="submit" value="保存"> <input type="button" onclick="location.href='{{ route('content.index') }}'" value="一覧に戻る"> </form>確認
- 各種情報入力画面でバリデーションルールと異なる情報を入力した時にエラーが表示されれば作業完了である。
- 投稿日:2020-09-23T10:31:55+09:00
front-end に Nuxt.js と Laravel が混在するシステムで Cookie を用いてクライアントを識別する
前回の記事の続きですが、 A/B テストのような文脈でクライアント(ブラウザ)ごとに一意なランダムの ID を割り当て、 Cookie を用いてリクエストごとにクライアントを分類できるようにする処理を実装する機会がありました。この記事では front-end に Nuxt.js と Laravel (の view )が混在するシステムで、両方から ID を生成・利用できるようにする方法について紹介します。
方針と注意
基本的な方針は以下の通りです。
- リクエストの Cookie ヘッダから ID が得られなかった場合(初回訪問)は ID を生成して Cookie に追加 1 する。
- リクエストの Cookie ヘッダから ID を得られた場合(2回目以降の訪問)は、それを利用する。
今回の実装では Nuxt.js と Laravel のどちら側でも Cookie から ID を読み取れるようにするため、 HttpOnly 属性を外し、暗号化を行いません。このため、ログインセッション用の Cookie など、第三者による Cookie の盗聴や改ざんがセキュリティ上のリスクとなるような情報に関しては本記事中の実装方法を避け、フレームワークが提供する機能などを利用してください。
また front-end がすべて Nuxt.js で構成されている場合には Laravel 側の処理は不要なので、 Laravel のセクションを読み飛ばしてください。
実装
Nuxt.js
以下の
useClientId
は ID の生成および Cookie 操作の処理を集約した composition です。app/compositions/client-id.tsimport { Store } from 'vuex/types' import { IncomingMessage } from 'connect' import Cookie from 'universal-cookie' import { name, StoreAbTesting, ACTIONS, GETTERS } from '~/store/abTesting' export function useClientId(store: Store<StoreAbTesting>, req: IncomingMessage) { const CLIENT_ID_KEY = 'client_id' const cookie = process.server ? new Cookie(req.headers?.cookie ?? '') : new Cookie() const getIdFromStore = (): string => { return store.getters[`${name}/${GETTERS.clientId}`] } const prepareId = async (): Promise<void> => { if (process.client) { throw new Error('CSR でのメソッド呼び出しは不正です') } const generateId = (): string => { // ランダムな ID を生成して返す } await store.dispatch( `${name}/${ACTIONS.setClientId}`, getIdFromCookie() ?? generateId() ) } const getIdFromCookie = (): string|undefined => { return cookie.get(CLIENT_ID_KEY) } const isIncludedInCookie = (): boolean => { return getIdFromCookie() !== undefined } const setCookie = (): void => { if (process.server) { throw new Error('SSR でのメソッド呼び出しは不正です') } if (isIncludedInCookie()) { return } cookie.set(CLIENT_ID_KEY, getIdFromStore()) } return { prepareId, setCookie, getId: getIdFromStore } }主要な処理について解説します。
まず Cookie の操作に関しては universal-cookie というライブラリを利用しています。
const cookie = process.server ? new Cookie(req.headers?.cookie ?? '') : new Cookie()このライブラリを利用すると Nuxt の SSR モードでもリクエストヘッダから Cookie を取得することができます。使用例に関しては以下の記事が参考になります。
Nuxt は Context から
context.req
のようにしてリクエストの情報が取得できるので、useClientId
の利用時はこの変数を引数に入力します。この変数からreq.headers?.cookie
のようにして Cookie ヘッダの値を文字列として取得できるので、これをCookie
コンストラクタの引数に渡してインスタンス生成しています。次に Cookie から ID を取得、または生成を行う
prepareId
メソッドです。const prepareId = async (): Promise<void> => { if (process.client) { throw new Error('CSR でのメソッド呼び出しは不正です') } const generateId = (): string => { // ランダムな ID を生成して返す } await store.dispatch( `${name}/${ACTIONS.setClientId}`, getIdFromCookie() ?? generateId() ) }最初の処理で CSR でのメソッド呼び出しを禁止しています。これは直アクセス時の SSR の時点で ID を用意しておけば以降の処理で ID を使い回せるので、 CSR での呼び出しが不要になるためです。
getIdFromCookie() ?? generateId()
の部分で Cookie から ID を取得するか、取得できなければ生成を行っています。取得した ID は Vuex の Store を用いて保存しておきます 2。 Store に関しても Nuxt の Context からcontext.store
のように取得できます。何となくcookie.set(id)
のようにしておけば後からcookie.get()
で ID を取得できそうな気もしてしまいますが、 SSR でcookie.set()
を呼び出してもブラウザ上の Cookie には影響がないため、このような方法では実現できません。次に Cookie を設定する
setCookie
メソッドです。const setCookie = (): void => { if (process.server) { throw new Error('SSR でのメソッド呼び出しは不正です') } if (isIncludedInCookie()) { return } cookie.set(CLIENT_ID_KEY, getIdFromStore()) }前述の通り SSR で
cookie.set()
を呼び出しても意味がないので、 SSR でのメソッド呼び出しを禁止しています。そして ID が Cookie に含まれていない場合のみ Store に保存しておいた ID を取得して Cookie に設定します。このように実装した
useClientId
の諸々の処理は plugins から呼び出します。app/plugins/ab-testing.tsimport { Context } from '@nuxt/types' import { useClientId } from '~/compositions/libs/ab-testing/client-id' export default (context: Context) => { if (process.server) { useClientId(context.store, context.req).prepareId() } else { useClientId(context.store, context.req).setCookie() } }前述した通り、
prepareId
は SSR で、setCookie
は CSR で呼び出します。これらの処理を Middleware から呼び出しても問題ないように思えてしまいますが、 Middleware の場合は直アクセス時に SSR の処理しか実行されないため Cookie が設定されません。 Nuxt のライフサイクルに関しては以下の記事が参考になります。後は
nuxt.config.js
に plugins を追加し、 Vue コンポーネント等からuseClientId(store, req).getId()
のような形で ID を取得する流れになります。Laravel
大まかな内容は Nuxt.js と変わりませんが、 Laravel の場合は Store の代わりに単なる singleton インスタンスとしてメモリに保持する方針で実装します 3。
まず singleton インスタンスの元となるクラス
AbTestingCookie
を以下のように作成します。app/Http/Cookie/AbTestingCookie.php<?php namespace App\Http\Cookie; use Illuminate\Http\Request; class AbTestingCookie { private $id; public function __construct(Request $request) { $key = 'client_id'; if ($request->hasCookie($key)) { $this->id = $request->cookie($key); return; } $this->id = $this->make(); } public function make() { // ランダムな ID を生成して返す } public function id() { return $this->id; } }コンストラクタでは
Request
を引数に取り、 Cookie から ID を取得するか、取得できなければ生成し、 ID をプロパティに保持しておきます。singleton を生成するための ServiceProvider の実装は以下のとおりです。
app/Providers/AbTestingServiceProvider.php<?php namespace App\Providers; use App\Http\Cookie\AbTestingCookie; use Illuminate\Http\Request; use Illuminate\Support\ServiceProvider; class AbTestingServiceProvider extends ServiceProvider { public function boot(Request $request) { $this->app->singleton(AbTestingCookie::class, function ($app) use ($request) { return new AbTestingCookie($request); }); } }次にレスポンスの Set-Cookie ヘッダに ID を付与する処理を Middleware で実装します。
app/Http/Middleware/IssueAbTestingClientId.php<?php namespace App\Http\Middleware; use App\Http\Cookie\AbTestingCookie; use Closure; class IssueAbTestingClientId { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { $key = 'client_id'; if ($request->hasCookie($key)) { return $next($request); } return $next($request)->withCookie(cookie( $key, app(AbTestingCookie::class)->id(), 0, null, null, false, false, // front-end からも利用するため httpOnly にしない false, null )); } }この Middleware は ID の生成・利用が必要なすべてのルートで呼び出されるように Kernel.php に設定しておきます。また
withCookie()
メソッドはデフォルトで HttpOnly 属性を付与しますが、 Nuxt.js からも読み出せるように HttpOnly 属性を外しています。最後に Nuxt.js から Cookie を読み出せるように EncryptCookies の暗号化の処理対象から除外しておきます。
app/Http/Middleware/EncryptCookies.php<?php namespace App\Http\Middleware; use Illuminate\Cookie\Middleware\EncryptCookies as Middleware; class EncryptCookies extends Middleware { /** * The names of the cookies that should not be encrypted. * * @var array */ protected $except = [ 'client_id', // front-end からも利用するため暗号化しない ]; }後は view 等から
app()->make(AbTestingCookie::class)->id()
のようにして ID を取得する流れになります。
サーバー側ではレスポンスに Set-Cookie ヘッダを、クライアント側では document.cookie を利用します。 ↩
単に State に clientId を保存するだけなので、 Store の実装は割愛します。 ↩
Stack Overflow の Q&A を参考にしています。 ↩
- 投稿日:2020-09-23T10:01:53+09:00
気象庁の雨量データをスクレイピングしM5Stackで表示する
概要
- 集合住宅では雨の様子が窓からは分からず、エントランスに出てから雨に気づき傘を取りに戻ることが良くあった
- 一部のオフィスビル(30階建て以上)では、エレベーターホールに傘のサインで降水を知らせてくれていた
- ベランダに降水センサーを設置しアラームを通知する方法もあるが、電源確保、センサーの耐久性の面から難しい
- 気象庁の観測データページを、常時起動しているサーバー(Raspberry Pi)でスクレイピングした結果をM5Stackで表示する
https://www.data.jma.go.jp/obd/stats/data/mdrr/pre_rct/alltable/pre1h00.html用意するもの
- Raspberry Pi3 Model B (4でもおそらく大丈夫)
- Raspberry Pi用のケース
- Raspberry Pi用のヒートシンク
- Raspberry Pi3 Model B B+ 対応 電源セット(5V 3.0A)
- M5Stack
- M5Stack用の電源
- Arduino IDEが動くPC (Mac/Windows)
※サーバーは前回作成した来客通知システム(常時起動)を兼用する
https://qiita.com/cami_oshimo/items/b4e4002f7c47dd00ba84環境構築
(1)Arduino IDEをインストール
https://www.arduino.cc/en/main/software
(2)M5Stack関連のライブラリをインストール
https://mag.switch-science.com/2018/02/28/getting-started-with-m5stack/
(3)Raspberri PiにApache(httpサーバー)をインストールしておく
※Raspberry Pi側のセットアップは割愛準備
- 気象庁の降水量のページをChromeのDeveloperToolで開き、該当地区の降水量が出力されている場所を確認する
プログラミング
- 常時起動したRaspberry PiでPythonを使ったコードでスクレイピングを行い
index.html
に出力する- M5Stackで
index.html
を表示するメインプログラム
広島(ヒロシマ)
を他地域に変更すればその土地の降水量が取得できます。/var/www/html/rain.php<?php mb_language("Japanese");//処理言語の設定 mb_internal_encoding("UTF-8");//文字コードの指定 date_default_timezone_set('Asia/Tokyo');//timezoneをセット $url = "http://www.data.jma.go.jp/obd/stats/data/mdrr/pre_rct/alltable/pre1h00.h tml"; //webページの読み込み $contents= file_get_contents($url); //文字コードの変換 $contents_utf = mb_convert_encoding($contents,"UTF-8","auto"); //指定した文字列で囲まれた部分を取り出す。 $startString = '<td style="white-space:nowrap">広島(ヒロシマ)*</td><td style=" text-align:right;white-space:nowrap;">'; $endString = '</td>'; $startPoint = mb_strpos($contents_utf,$startString) ;//$startStringが現れる位置を調べる $startPoint = $startPoint + mb_strlen($startString);//開始位置 //echo $startPoint; //echo""; $endPoint = mb_strpos($contents_utf,$endString,$startPoint );//$endStringが次に 現れる位置を調べる //echo $endPoint; //echo""; $length = $endPoint - $startPoint;//文字列の長さを求める。 $rainfallString = mb_substr($contents_utf,$startPoint,$length)." mm"."\n\n"."GET Time"."\n".date("H:i:s")."\n"; echo $rainfallString; file_put_contents("/var/www/html/index.html", $rainfallString); ?>PHP実行用のシェルスクリプト
/var/www/html/cron.php#!/bin/sh /usr/bin/php /var/www/html/rain.php
PHP実行結果
/var/www/html/index.html0.0 mm GET Time 09:30:02自動起動の設定(この例では1時間毎に降水量を取得)
*/60 * * * * root sudo sh /var/www/html/cron.sh
M5Stack側のプログラミング
rain.ino#include <M5Stack.h> #include <WiFi.h> #include <HTTPClient.h> const char* ssid = "XXXXXXXXXXXXX"; // Wifi SSID const char* password = "XXXXXXXXXXXXX"; // Wifi Password const long gmtOffset_sec = 3600; const int daylightOffset_sec = 3600; void setup() { M5.begin(); M5.Lcd.setBrightness(10); M5.Lcd.printf("Connecting to %s ", ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); M5.Lcd.printf("."); } M5.Lcd.println(" CONNECTED"); M5.Lcd.fillScreen(BLACK); } void getrain() { M5.Lcd.fillScreen(BLACK); HTTPClient http; http.begin("http://xxx.xxx.xxx.xxx/index.html"); // Raspberry PiのIPアドレス int httpCode = http.GET(); String result = http.getString(); M5.Lcd.setTextSize(5); M5.Lcd.setCursor(0,0); M5.Lcd.print(result); http.end(); } void loop() { getrain(); delay(xxxxxxx); // raspberryPiから `index.html` を取得する間隔(ミリ秒) }注意点
- スクレイピングの頻度を上げすぎると相手側のサーバーの負荷となり、問題になるので気をつける
使ってみて
- スマホアプリでも降水量が見られるが、玄関先などに置いておくと簡単に確認できるので便利
- 当初さくらのVPS(Linux)で2年ほど稼働させていたが、月額固定費用がかかることもあり廃止し、Raspberry Piに移行した(1ヶ月程度経過したが問題なし)
- 投稿日:2020-09-23T09:45:05+09:00
PHP: 連想配列とDB操作(初心者向け)
初心者向けにDB接続の記事を書いたのですが、初心者の方に
わからない
と言われました笑
PHP: らくらくPDOクラスヒアリングをしてみると間に配列を挟んでるところがごちゃつくようです。
DB -> PDOでDBからデータGet -> なんかわからんけどforeach??
正解は「連想配列に一旦、格納する!!」です。
DB -> PDOでDBからデータGet ->
連想配列に格納
-> foreachで展開配列変数 -> 1つの変数で複数の値を格納できるスグレモノ。
変数と配列変数の比較普通の変数 //入る場所は一つ $b = 1; echo $b; //-> 1 配列変数(連想配列) //入る場所が複数, 添字キーで格納 key: value //連想配列を1つつくってみます(生成): key => value $bar = ['id' => 1, 'name' => 'CakePHP', 'birthday' => '2005-04-01']; var_dump($bar); //一度確認しましょう! //呼び出し方 echo $bar['id']; //-> 1 echo $bar['birthday']; //-> 2000-02-03 //foreachで呼び出す foreach ($bar as $val) { echo $val . ','; } //-> 1,CakePHP,2000-02-03多次元配列(配列の中に配列が入ります。)
多次元配列(連想配列)データベースと連動してよく使われます。 -> DBテーブル名 -> key -> DBデータ -> value //多次元配列を生成してみます: key => value $bars = [['id' => 1, 'name' => 'CakePHP', 'birthday' => '2005-04-01'], //0 ['id' => 2, 'name' => 'Laravel', 'birthday' => '2011-06-01'], //1 ['id' => 3, 'name' => 'Ruby on Rails', 'birthday' => '2004-07-01'], //2 ]; var_dump($bars); //必ず確認します! 呼び出し方 echo $bars[0]['birthday']; //-> 2005-04-01 echo $bars[1]['name']; //-> Laravel echo $bars[2]['id']; //-> 3 foreachで呼び出す: //1ループ目: $bar <- ['id' => 1, 'name' => 'CakePHP', 'birthday' => '2005-04-01'] //2ループ目: $bar <- ['id' => 2, 'name' => 'Laravel', 'birthday' => '2011-06-01'] //3ループ目: $bar <- ['id' => 3, 'name' => 'Ruby on Rails', 'birthday' => '2004-07-01'] foreach ($bars as $bar) { echo $bar['id'] . ':'; echo $bar['name'] . ','; } //-> 1:CakePHP,2:Laravel,3:Ruby on Rails, 多重ループで全て呼び出す。 //$bars -> [id, name, birthday] -> $bar 以下繰り返し foreach ($bars as $bar) { foreach ($bar as $val) { echo $val . ','; } echo '<br>'; } //-> 1,CakePHP,2005-04-01, // 2,Laravel,2011-06-01, // 3,Ruby on Rails,2004-07-01,PDOとの連携
例の中で連想配列・多次元配列を生成している部分をPDOがやってくれます。
その後はforeach
でデータをブン回して処理しましょ!LGTMお願いします!
モチベーションがあがります!
- 投稿日:2020-09-23T09:31:49+09:00
CentOSでRabbitMQ+PHPを試す
PHPで非同期処理を導入するために、RabbitMQを試した際のメモ。
※この記事ではCentOS7、PHP7.3を使用しています。MQの準備
RabbitMQインストール
yum --enablerepo=epel -y install rabbitmq-server systemctl start rabbitmq-server systemctl enable rabbitmq-serverユーザ追加
rabbitmqctl add_user mquser password rabbitmqctl list_usersバーチャルホスト追加
rabbitmqctl add_vhost myhost rabbitmqctl list_vhostsパーミッション設定
rabbitmqctl set_permissions -p myhost mquser ".*" ".*" ".*" rabbitmqctl list_permissions -p myhostPHPプログラムの準備(送信側&受信側)
phpライブラリインストール
yum -y install --enablerepo=epel,remi-php73 php php-bcmath composercomposerインストール
※一般ユーザにて実施
cd mkdir mq cd mq composer require php-amqplib/php-amqplib composer install送信側
送信アプリ作成
vi send_msg.php<?php require_once __DIR__ . '/vendor/autoload.php'; use PhpAmqpLib\Connection\AMQPStreamConnection; use PhpAmqpLib\Message\AMQPMessage; $connection = new AMQPStreamConnection('127.0.0.1', 5672, 'mquser', 'password', 'myhost'); $channel = $connection->channel(); $channel->queue_declare('Hello_World', false, false, false, false); $msg = new AMQPMessage('Hello RabbitMQ World!'); $channel->basic_publish($msg, '', 'Hello_World'); echo " [x] Sent 'Hello_World'\n"; $channel->close(); $connection->close(); ?>送信
php send_msg.php受信側
受信アプリ作成
vi receive_msg.php<?php require_once __DIR__ . '/vendor/autoload.php'; use PhpAmqpLib\Connection\AMQPStreamConnection; $connection = new AMQPStreamConnection('127.0.0.1', 5672, 'mquser', 'password', 'myhost'); $channel = $connection->channel(); $channel->queue_declare('Hello_World', false, false, false, false); echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; $callback = function($msg) { echo " [x] Received ", $msg->body, "\n"; }; $channel->basic_consume('Hello_World', '', false, true, false, false, $callback); while(count($channel->callbacks)) { $channel->wait(); } ?>受信
php receive_msg.php (CTRL+Cで停止)
- 投稿日:2020-09-23T03:27:14+09:00
CentOS8にベータ版php8.0をインストールする方法
経緯
php8.0が2020年11月26日にリリースされる予定らしい。
remi氏のリポジトリでもベータ版が出ているのでインストールして動作確認する。インストール方法
百番煎じくらいの、CentOS8でPHP8環境作るコピペ
Centos8 php8.0をインストールする方法
php80-php-8.0.0~beta4-32.el8.remi.x86_64.rpmphpの本体を入れる。
dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm dnf install yum-utils dnf --enablerepo=remi install php80-phpデータベースの接続も試したいのでmariadbを入れる
dnf install mariadb-server systemctl start mariadb.service mysql_secure_installationphpのモジュールを入れる。
dnf install --enablerepo=remi php80-php-mysqlnd php80-php-mysqli php80-php-gd php80-php-cli php80-php-opcache dnf install --enablerepo=remi php80-php-pecl-zip php80-php-devel php80-php-pearphp、pear、peclなどのコマンドはインストールするだけではパスが通らないので、findで探してフルパスで呼び出す。
find / -name pear find / -name pecl /opt/remi/php80/root/usr/bin/pecl install APCu /opt/remi/php80/root/usr/bin/pear install Mail_MimeDecode /opt/remi/php80/root/usr/bin/pear install HTTP_Client /opt/remi/php80/root/usr/bin/pear install Net_IPv6alternativesを利用してバージョンを切り替えると上品だけど、今回はテストなのでしない。
複数のバージョンの PHP を切り替えて使うhttpdとphpを起動。最近はphp-fpmが入るのが普通らしい。
systemctl start httpd.service systemctl start php80-php-fpm.servicephpinfo();を実行し「Loaded Configuration File」を調べてphp.iniのパスを確認する。
php.iniに追記。php.iniextension=apcu.sophpを再起動。
systemctl restart php80-php-fpm.service結果
gdやmysqliは異常なし。
OPcacheもapcuも普通に動いてる。
Net_IPv6がFatal errorを吐くので修正。/opt/remi/php80/root/usr/share/pear/Net/IPv6.php# 806行目付近 # $ip{$pos} = '_'; #修正前 $ip[$pos] = '_'; #修正後
- 投稿日:2020-09-23T00:08:55+09:00
【備忘録】Laravel 6でユーザー認証作る時につまづいて解決した方法共有!!
はじめに
こんばんは!
私は、毎週、「1週間1制作計画」っていうのをやっています。
最近は、PHPの勉強を始めたので、今週は、PHP及びLaravelの練習を兼ねてLaravelでアプリケーションを制作してみようと思っていました。そこで、認証機能をつけようと思い、Laravelに標準装備されている認証機能Authを使ってみようと考え、トライしてみました!!
...すると、めっちゃつまづいた!!
今日の投稿では、そのつまづいた部分について、解決した方法を、認証機能の実装方法と共に記録していきます!
また、その方法自体は最後に記載されている参考資料・文献を参考に実装しました。まずはLaravel 6をインストールしたい!!
Laravel 6をなぜ選んだかというと、サポート期間が最新のLaravel 8よりも長いためです(LTS = Long-Term-Support)。
一般的なインストール方法
laravel new プロジェクト名ですよね!
しかし、今回は違います!以下のコードをご覧ください。composer create-project "laravel/laravel=6.*" プロジェクト名バージョンを指定して、Laravelをインストールする方法は、上記のように、composerを用います。
アプリケーションの実行には、通常通り、php artisan serveを使います。
標準装備Auth使っていきましょう!
それでは、いよいよ標準装備のAuthを使っていきます!
まずは、Auth機能を使うために、laravel/uiというパッケージを導入しましょう。composer require laravel/ui:^1.0 --dev上記コードをターミナルに入力して実行すると、認証部分のフロントエンドを制作できるlaravel/uiを導入することができます。
Auth関連のファイルを生成するために、以下の2つをターミナルに記述して実行します。php artisan ui vue --auth php artisan migratephp artisan migrateしたらエラー返ってきた!!
そうです、このphp artisan migrateしたら、以下のようなエラーが返ってきてしまいました。
Illuminate\Database\QueryException : SQLSTATE[HY000] [2002] Connection refused (SQL: select * from information_schema.tables where table_schema = homestead and table_name = migrations) at /Users/username/code/LaravelSample/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664 660| // If an exception occurs when attempting to run a query, we'll format the error 661| // message to include the bindings with SQL, which will make this exception a 662| // lot more helpful to the developer instead of just the database's errors. 663| catch (Exception $e) { > 664| throw new QueryException( 665| $query, $this->prepareBindings($bindings), $e 666| ); 667| } 668| Exception trace: 1 PDOException::("SQLSTATE[HY000] [2002] Connection refused") /Users/username/code/LaravelSample/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 2 PDO::__construct("mysql:host=127.0.0.1;port=3306;dbname=homestead", "homestead", "secret", []) /Users/username/code/LaravelSample/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 Please use the argument -v to see more details.正直、まだデータベースいじってないのに、何なんだ!!
「よくわからん!!調べても、Dockerを利用している人が多くて、自分の開発環境とは違うので参考にならん!!」
って感じでした。
なので、自分の手持ちのテキストに書いてあったように、そのまま以下をターミナルに記述して実行しちゃいました(笑)npm install && npm run devそして、先ほど、アプリケーションを実行した時のように、
php artisan serveを実行。
アプリケーションを開くことができる状態になります。
http://localhost:8000/
を開いてみてください。
上の画像のようなページが開きます。最初に開いた時と変わらないようにも消えますが、
右上を見て頂くと、きちんと、LOGINとREGISTERと書かれていることがわかります。つまり、きちんと、Auth機能、実装できたんじゃないかと思いますよね!!
これが実は落とし穴...テキストでは触れられていない落とし穴だった。REGISTER(ユーザー登録)できない!?
まず、初めて利用するので、ユーザー登録をしてみます。
REGISTERを押して、ユーザー登録画面に遷移します。
そして、ユーザー名やメールアドレス、パスワードと言った、登録に必要なデータを入力します。
さぁ、REGISTERボタンを押して、登録だ!Database name seems incorrect You're using the default database name laravel. This database does not exist. Edit the .env file and use the correct database name in the DB_DATABASE key.なんか出てきた...泣
先ほど、php artisan migrate失敗した時もデータベース関連のエラーが出てきましたね。
今回も同じく、データベース関連のエラーです。データベース名が違うんですって。(incorrect)次項から、この問題を解決していきます。
まず、データベースに接続しよう!
Laravelプロジェクト作ってから全くいじっていなかったデータベースを、ついにいじっていきます!
MySQLをインストール
MySQLをまずはインストールしましょう。以下をターミナルに記述して実行してください。
brew install mysqlインストールできたら、起動してください。
mysql.server start --skip-grant-tables上記の方法で起動すると、パスワード不要でした!
その後、ルートユーザーで接続。mysql -urootデータベースを作っていきましょう
それでは、続いて、データベースを作っていきます。
最初に、以下のコマンドを記述して、現在のデータベースの様子をチェックします。mysql> show databases;ダッシュ(途切れ途切れのあの線)で囲われて、Databaseっていうのが出てきます。出てきたらオッケーです。
次に、新しくデータベースを作成するコマンドを記述して実行してください。create database sample;私が、上記でsampleと記述した部分は、みなさんの任意の名前です。
そしてもう一度、show databases;で、データベースの状況を確認します。出力結果に、ここでいう、sampleが含まれていたら問題ないです!rootユーザーのパスワードの変更
MySQLのrootユーザーのパスワードの変更を行います。
mysql> FLUSH PRIVILEGES; mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'secret';上記を実行してください。
それぞれ、実行直後には、Query OK, なんちゃらとかいうのが返ってきたらOKです!
ここでは、rootユーザーのパスワードを、「secret」に変更しています。
こちらも特に決まりはないので、任意の言葉で。
まぁ、サンプルコードでよく見るのは、secretですね(笑)この処理が終了すれば、一旦MySQLを終了してから再起動させます。以下のコマンドを入力。
mysql> exit再起動
mysql.server restart認証プラグインの変更
ここで、すいません、MySQLをまた違う方法で開きたいので、またまたexitをお願いします。
以下のコマンドでMySQLに接続してください。mysql -uroot -pその後、Enter Passwordと言われ、パスワードの入力を求められます。
先ほど、変更したルートユーザーのパスワード、secretを入力してください。
接続できたら、次のコマンドを実行。SELECT user, host, plugin FROM mysql.user;SELECT文が出てきたら、ようやくデータベースいじってる感出てきましたねー!
上記コマンドで現状の認証プラグインを調べることができます。
おそらく、caching_sha2_passwordとなっているかと思います。
今回は、mysql_native_passwordを使いたいので、変更しましょう。以下のコマンドを実行してください。ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'secret';それでは、再度、以下のコマンドで確認してみましょう。
SELECT user, host, plugin FROM mysql.user;root項目のプラグインが、caching_sha2_passwordだったのに、mysql_native_passwordに変更されているかと思います。
あと少し!.envファイルの編集をしよう!
一旦、ターミナルからは離れて、エディタに移動してください。
作成したプロジェクトのフォルダ内に、「.env」という名前のファイルがあると思います。そのファイルを開きましょう。
※examapleがついている.envファイルは違います!!今回編集するのは、ただの.envファイルです!開いたらなんだかデータベースに関連しそうな内容がたくさん書かれています。
その中でも、以下のコメントアウトした部分を変更します。DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel // ココ変更するよ! DB_USERNAME=root DB_PASSWORD= // ココ変更するよ!まず、DB_DATABASE=laravelの、laravelの部分を、sampleにします。
このsampleは、新しくデータベースを作った時に使った名前です。DB_PASSWORD= は、元々空欄になっていますが、rootユーザーのパスワードとして設定した、secretを空欄部分に入れます。
最終的には、以下のような状態になるかと思います。
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=SampleApp DB_USERNAME=root DB_PASSWORD=secret編集後、またターミナルに帰りましょう。
キャッシュの削除を行います。php artisan config:cache最初できなかったphp artisan migrateしてみる!
この時点で、
php artisan migrateしてみると、成功します。
REGISTERリベンジ!
もう一度、Laravelアプリケーションを実行してみましょう。
php artisan serveですね。
そしてもう一度、REGISTERに挑戦してみてください!!
すると、エラーは特に出ず、登録できるようになっています!!まとめ
長くなりましたが、数時間かけて格闘していたので、備忘録としてまとめてみました!
ここまでお読みくださり、ありがとうございました。
理解不足等による不備があれば、コメントお願い致します。参考資料・文献
【Laravel】MySQLの接続方法を徹底解説【コピペでOK】
更新!!Laravel6/7 (laravel/ui)でのLogin機能の実装方法〜MyMemo
【Laravel】Laravelでバージョンを指定してインストールする方法
掌田津耶乃著『PHPフレームワーク Laravel入門 第2版』(2020)
- 投稿日:2020-09-23T00:08:55+09:00
【備忘録】Laravel 6でユーザー認証作る時につまづいたけど解決した方法共有!!
はじめに
こんばんは!
私は、毎週、「1週間1制作計画」っていうのをやっています。
最近は、PHPの勉強を始めたので、今週は、PHP及びLaravelの練習を兼ねてLaravelでアプリケーションを制作してみようと思っていました。そこで、認証機能をつけようと思い、Laravelに標準装備されている認証機能Authを使ってみようと考え、トライしてみました!!
...すると、めっちゃつまづいた!!
今日の投稿では、そのつまづいた部分について、解決した方法を、認証機能の実装方法と共に記録していきます!
また、その方法自体は最後に記載されている参考資料・文献を参考に実装しました。まずはLaravel 6をインストールしたい!!
Laravel 6をなぜ選んだかというと、サポート期間が最新のLaravel 8よりも長いためです(LTS = Long-Term-Support)。
一般的なインストール方法
laravel new プロジェクト名ですよね!
しかし、今回は違います!以下のコードをご覧ください。composer create-project "laravel/laravel=6.*" プロジェクト名バージョンを指定して、Laravelをインストールする方法は、上記のように、composerを用います。
アプリケーションの実行には、通常通り、php artisan serveを使います。
標準装備Auth使っていきましょう!
それでは、いよいよ標準装備のAuthを使っていきます!
まずは、Auth機能を使うために、laravel/uiというパッケージを導入しましょう。composer require laravel/ui:^1.0 --dev上記コードをターミナルに入力して実行すると、認証部分のフロントエンドを制作できるlaravel/uiを導入することができます。
Auth関連のファイルを生成するために、以下の2つをターミナルに記述して実行します。php artisan ui vue --auth php artisan migratephp artisan migrateしたらエラー返ってきた!!
そうです、このphp artisan migrateしたら、以下のようなエラーが返ってきてしまいました。
Illuminate\Database\QueryException : SQLSTATE[HY000] [2002] Connection refused (SQL: select * from information_schema.tables where table_schema = homestead and table_name = migrations) at /Users/username/code/LaravelSample/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664 660| // If an exception occurs when attempting to run a query, we'll format the error 661| // message to include the bindings with SQL, which will make this exception a 662| // lot more helpful to the developer instead of just the database's errors. 663| catch (Exception $e) { > 664| throw new QueryException( 665| $query, $this->prepareBindings($bindings), $e 666| ); 667| } 668| Exception trace: 1 PDOException::("SQLSTATE[HY000] [2002] Connection refused") /Users/username/code/LaravelSample/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 2 PDO::__construct("mysql:host=127.0.0.1;port=3306;dbname=homestead", "homestead", "secret", []) /Users/username/code/LaravelSample/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 Please use the argument -v to see more details.正直、まだデータベースいじってないのに、何なんだ!!
「よくわからん!!調べても、Dockerを利用している人が多くて、自分の開発環境とは違うので参考にならん!!」
って感じでした。
なので、自分の手持ちのテキストに書いてあったように、そのまま以下をターミナルに記述して実行しちゃいました(笑)npm install && npm run devそして、先ほど、アプリケーションを実行した時のように、
php artisan serveを実行。
アプリケーションを開くことができる状態になります。
http://localhost:8000/
を開いてみてください。
上の画像のようなページが開きます。最初に開いた時と変わらないようにも消えますが、
右上を見て頂くと、きちんと、LOGINとREGISTERと書かれていることがわかります。つまり、きちんと、Auth機能、実装できたんじゃないかと思いますよね!!
これが実は落とし穴...テキストでは触れられていない落とし穴だった。REGISTER(ユーザー登録)できない!?
まず、初めて利用するので、ユーザー登録をしてみます。
REGISTERを押して、ユーザー登録画面に遷移します。
そして、ユーザー名やメールアドレス、パスワードと言った、登録に必要なデータを入力します。
さぁ、REGISTERボタンを押して、登録だ!Database name seems incorrect You're using the default database name laravel. This database does not exist. Edit the .env file and use the correct database name in the DB_DATABASE key.なんか出てきた...泣
先ほど、php artisan migrate失敗した時もデータベース関連のエラーが出てきましたね。
今回も同じく、データベース関連のエラーです。データベース名が違うんですって。(incorrect)次項から、この問題を解決していきます。
まず、データベースに接続しよう!
Laravelプロジェクト作ってから全くいじっていなかったデータベースを、ついにいじっていきます!
MySQLをインストール
MySQLをまずはインストールしましょう。以下をターミナルに記述して実行してください。
brew install mysqlインストールできたら、起動してください。
mysql.server start --skip-grant-tables上記の方法で起動すると、パスワード不要でした!
その後、ルートユーザーで接続。mysql -urootデータベースを作っていきましょう
それでは、続いて、データベースを作っていきます。
最初に、以下のコマンドを記述して、現在のデータベースの様子をチェックします。mysql> show databases;ダッシュ(途切れ途切れのあの線)で囲われて、Databaseっていうのが出てきます。出てきたらオッケーです。
次に、新しくデータベースを作成するコマンドを記述して実行してください。create database sample;私が、上記でsampleと記述した部分は、みなさんの任意の名前です。
そしてもう一度、show databases;で、データベースの状況を確認します。出力結果に、ここでいう、sampleが含まれていたら問題ないです!rootユーザーのパスワードの変更
MySQLのrootユーザーのパスワードの変更を行います。
mysql> FLUSH PRIVILEGES; mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'secret';上記を実行してください。
それぞれ、実行直後には、Query OK, なんちゃらとかいうのが返ってきたらOKです!
ここでは、rootユーザーのパスワードを、「secret」に変更しています。
こちらも特に決まりはないので、任意の言葉で。
まぁ、サンプルコードでよく見るのは、secretですね(笑)この処理が終了すれば、一旦MySQLを終了してから再起動させます。以下のコマンドを入力。
mysql> exit再起動
mysql.server restart認証プラグインの変更
ここで、すいません、MySQLをまた違う方法で開きたいので、またまたexitをお願いします。
以下のコマンドでMySQLに接続してください。mysql -uroot -pその後、Enter Passwordと言われ、パスワードの入力を求められます。
先ほど、変更したルートユーザーのパスワード、secretを入力してください。
接続できたら、次のコマンドを実行。SELECT user, host, plugin FROM mysql.user;SELECT文が出てきたら、ようやくデータベースいじってる感出てきましたねー!
上記コマンドで現状の認証プラグインを調べることができます。
おそらく、caching_sha2_passwordとなっているかと思います。
今回は、mysql_native_passwordを使いたいので、変更しましょう。以下のコマンドを実行してください。ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'secret';それでは、再度、以下のコマンドで確認してみましょう。
SELECT user, host, plugin FROM mysql.user;root項目のプラグインが、caching_sha2_passwordだったのに、mysql_native_passwordに変更されているかと思います。
あと少し!.envファイルの編集をしよう!
一旦、ターミナルからは離れて、エディタに移動してください。
作成したプロジェクトのフォルダ内に、「.env」という名前のファイルがあると思います。そのファイルを開きましょう。
※examapleがついている.envファイルは違います!!今回編集するのは、ただの.envファイルです!開いたらなんだかデータベースに関連しそうな内容がたくさん書かれています。
その中でも、以下のコメントアウトした部分を変更します。DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel // ココ変更するよ! DB_USERNAME=root DB_PASSWORD= // ココ変更するよ!まず、DB_DATABASE=laravelの、laravelの部分を、sampleにします。
このsampleは、新しくデータベースを作った時に使った名前です。DB_PASSWORD= は、元々空欄になっていますが、rootユーザーのパスワードとして設定した、secretを空欄部分に入れます。
最終的には、以下のような状態になるかと思います。
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=SampleApp DB_USERNAME=root DB_PASSWORD=secret編集後、またターミナルに帰りましょう。
キャッシュの削除を行います。php artisan config:cache最初できなかったphp artisan migrateしてみる!
この時点で、
php artisan migrateしてみると、成功します。
REGISTERリベンジ!
もう一度、Laravelアプリケーションを実行してみましょう。
php artisan serveですね。
そしてもう一度、REGISTERに挑戦してみてください!!
すると、エラーは特に出ず、登録できるようになっています!!まとめ
長くなりましたが、数時間かけて格闘していたので、備忘録としてまとめてみました!
ここまでお読みくださり、ありがとうございました。
理解不足等による不備があれば、コメントお願い致します。参考資料・文献
【Laravel】MySQLの接続方法を徹底解説【コピペでOK】
更新!!Laravel6/7 (laravel/ui)でのLogin機能の実装方法〜MyMemo
【Laravel】Laravelでバージョンを指定してインストールする方法
掌田津耶乃著『PHPフレームワーク Laravel入門 第2版』(2020)
- 投稿日:2020-09-23T00:08:55+09:00
【備忘録】Laravel 6でユーザー認証作る時につまずいたけど解決した方法共有!!
はじめに
こんばんは!
私は、毎週、「1週間1制作計画」っていうのをやっています。
最近は、PHPの勉強を始めたので、今週は、PHP及びLaravelの練習を兼ねてLaravelでアプリケーションを制作してみようと思っていました。そこで、認証機能をつけようと思い、Laravelに標準装備されている認証機能Authを使ってみようと考え、トライしてみました!!
...すると、めっちゃつまづいた!!
今日の投稿では、そのつまづいた部分について、解決した方法を、認証機能の実装方法と共に記録していきます!
また、その方法自体は最後に記載されている参考資料・文献を参考に実装しました。まずはLaravel 6をインストールしたい!!
Laravel 6をなぜ選んだかというと、サポート期間が最新のLaravel 8よりも長いためです(LTS = Long-Term-Support)。
一般的なインストール方法
laravel new プロジェクト名ですよね!
しかし、今回は違います!以下のコードをご覧ください。composer create-project "laravel/laravel=6.*" プロジェクト名バージョンを指定して、Laravelをインストールする方法は、上記のように、composerを用います。
アプリケーションの実行には、通常通り、php artisan serveを使います。
標準装備Auth使っていきましょう!
それでは、いよいよ標準装備のAuthを使っていきます!
まずは、Auth機能を使うために、laravel/uiというパッケージを導入しましょう。composer require laravel/ui:^1.0 --dev上記コードをターミナルに入力して実行すると、認証部分のフロントエンドを制作できるlaravel/uiを導入することができます。
Auth関連のファイルを生成するために、以下の2つをターミナルに記述して実行します。php artisan ui vue --auth php artisan migratephp artisan migrateしたらエラー返ってきた!!
そうです、このphp artisan migrateしたら、以下のようなエラーが返ってきてしまいました。
Illuminate\Database\QueryException : SQLSTATE[HY000] [2002] Connection refused (SQL: select * from information_schema.tables where table_schema = homestead and table_name = migrations) at /Users/username/code/LaravelSample/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664 660| // If an exception occurs when attempting to run a query, we'll format the error 661| // message to include the bindings with SQL, which will make this exception a 662| // lot more helpful to the developer instead of just the database's errors. 663| catch (Exception $e) { > 664| throw new QueryException( 665| $query, $this->prepareBindings($bindings), $e 666| ); 667| } 668| Exception trace: 1 PDOException::("SQLSTATE[HY000] [2002] Connection refused") /Users/username/code/LaravelSample/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 2 PDO::__construct("mysql:host=127.0.0.1;port=3306;dbname=homestead", "homestead", "secret", []) /Users/username/code/LaravelSample/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 Please use the argument -v to see more details.正直、まだデータベースいじってないのに、何なんだ!!
「よくわからん!!調べても、Dockerを利用している人が多くて、自分の開発環境とは違うので参考にならん!!」
って感じでした。
なので、自分の手持ちのテキストに書いてあったように、そのまま以下をターミナルに記述して実行しちゃいました(笑)npm install && npm run devそして、先ほど、アプリケーションを実行した時のように、
php artisan serveを実行。
アプリケーションを開くことができる状態になります。
http://localhost:8000/
を開いてみてください。
上の画像のようなページが開きます。最初に開いた時と変わらないようにも消えますが、
右上を見て頂くと、きちんと、LOGINとREGISTERと書かれていることがわかります。つまり、きちんと、Auth機能、実装できたんじゃないかと思いますよね!!
これが実は落とし穴...テキストでは触れられていない落とし穴だった。REGISTER(ユーザー登録)できない!?
まず、初めて利用するので、ユーザー登録をしてみます。
REGISTERを押して、ユーザー登録画面に遷移します。
そして、ユーザー名やメールアドレス、パスワードと言った、登録に必要なデータを入力します。
さぁ、REGISTERボタンを押して、登録だ!Database name seems incorrect You're using the default database name laravel. This database does not exist. Edit the .env file and use the correct database name in the DB_DATABASE key.なんか出てきた...泣
先ほど、php artisan migrate失敗した時もデータベース関連のエラーが出てきましたね。
今回も同じく、データベース関連のエラーです。データベース名が違うんですって。(incorrect)次項から、この問題を解決していきます。
まず、データベースに接続しよう!
Laravelプロジェクト作ってから全くいじっていなかったデータベースを、ついにいじっていきます!
MySQLをインストール
MySQLをまずはインストールしましょう。以下をターミナルに記述して実行してください。
brew install mysqlインストールできたら、起動してください。
mysql.server start --skip-grant-tables上記の方法で起動すると、パスワード不要でした!
その後、ルートユーザーで接続。mysql -urootデータベースを作っていきましょう
それでは、続いて、データベースを作っていきます。
最初に、以下のコマンドを記述して、現在のデータベースの様子をチェックします。mysql> show databases;ダッシュ(途切れ途切れのあの線)で囲われて、Databaseっていうのが出てきます。出てきたらオッケーです。
次に、新しくデータベースを作成するコマンドを記述して実行してください。create database sample;私が、上記でsampleと記述した部分は、みなさんの任意の名前です。
そしてもう一度、show databases;で、データベースの状況を確認します。出力結果に、ここでいう、sampleが含まれていたら問題ないです!rootユーザーのパスワードの変更
MySQLのrootユーザーのパスワードの変更を行います。
mysql> FLUSH PRIVILEGES; mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'secret';上記を実行してください。
それぞれ、実行直後には、Query OK, なんちゃらとかいうのが返ってきたらOKです!
ここでは、rootユーザーのパスワードを、「secret」に変更しています。
こちらも特に決まりはないので、任意の言葉で。
まぁ、サンプルコードでよく見るのは、secretですね(笑)この処理が終了すれば、一旦MySQLを終了してから再起動させます。以下のコマンドを入力。
mysql> exit再起動
mysql.server restart認証プラグインの変更
ここで、すいません、MySQLをまた違う方法で開きたいので、またまたexitをお願いします。
以下のコマンドでMySQLに接続してください。mysql -uroot -pその後、Enter Passwordと言われ、パスワードの入力を求められます。
先ほど、変更したルートユーザーのパスワード、secretを入力してください。
接続できたら、次のコマンドを実行。SELECT user, host, plugin FROM mysql.user;SELECT文が出てきたら、ようやくデータベースいじってる感出てきましたねー!
上記コマンドで現状の認証プラグインを調べることができます。
おそらく、caching_sha2_passwordとなっているかと思います。
今回は、mysql_native_passwordを使いたいので、変更しましょう。以下のコマンドを実行してください。ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'secret';それでは、再度、以下のコマンドで確認してみましょう。
SELECT user, host, plugin FROM mysql.user;root項目のプラグインが、caching_sha2_passwordだったのに、mysql_native_passwordに変更されているかと思います。
あと少し!.envファイルの編集をしよう!
一旦、ターミナルからは離れて、エディタに移動してください。
作成したプロジェクトのフォルダ内に、「.env」という名前のファイルがあると思います。そのファイルを開きましょう。
※examapleがついている.envファイルは違います!!今回編集するのは、ただの.envファイルです!開いたらなんだかデータベースに関連しそうな内容がたくさん書かれています。
その中でも、以下のコメントアウトした部分を変更します。DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel // ココ変更するよ! DB_USERNAME=root DB_PASSWORD= // ココ変更するよ!まず、DB_DATABASE=laravelの、laravelの部分を、sampleにします。
このsampleは、新しくデータベースを作った時に使った名前です。DB_PASSWORD= は、元々空欄になっていますが、rootユーザーのパスワードとして設定した、secretを空欄部分に入れます。
最終的には、以下のような状態になるかと思います。
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=SampleApp DB_USERNAME=root DB_PASSWORD=secret編集後、またターミナルに帰りましょう。
キャッシュの削除を行います。php artisan config:cache最初できなかったphp artisan migrateしてみる!
この時点で、
php artisan migrateしてみると、成功します。
REGISTERリベンジ!
もう一度、Laravelアプリケーションを実行してみましょう。
php artisan serveですね。
そしてもう一度、REGISTERに挑戦してみてください!!
すると、エラーは特に出ず、登録できるようになっています!!まとめ
長くなりましたが、数時間かけて格闘していたので、備忘録としてまとめてみました!
ここまでお読みくださり、ありがとうございました。
理解不足等による不備があれば、コメントお願い致します。参考資料・文献
【Laravel】MySQLの接続方法を徹底解説【コピペでOK】
更新!!Laravel6/7 (laravel/ui)でのLogin機能の実装方法〜MyMemo
【Laravel】Laravelでバージョンを指定してインストールする方法
掌田津耶乃著『PHPフレームワーク Laravel入門 第2版』(2020)