- 投稿日:2020-07-20T21:15:30+09:00
正規表現を使うときに盛大にやらかした話
最近Minecraft非公式日本ユーザーフォーラム非公式アーカイブの作業を進めているんですが、その過程で正規表現の使い方を間違えてデータがぶっ飛んだので、同じ過ちを再度繰り返さないように書いておきます()
何をしていたか
wayback machineから取得したデータはHTMLのまま保存しているため、各リンクはweyback machineによって置き換えられています。
そのため、リンクをもとに戻そうとしていましたwayback machineの仕様
HTML -> web.archive.org/web/時間/元のURL
CSS -> web.archive.org/web/時間cs_/元のURL
JS -> web.archive.org/web/時間js_/元のURL
画像 -> web.archive.org/web/時間im_/元のURL書いていたコード
$出力 = preg_replace('/http ?:\/\/web\.archive\.org\/web\/.*\/http ?:\/\/forum\.minecraftuser\.jp/', "https://forum.minecraftuser.xyz", "データ");これは当初使用していたコードなのですが、これには重大な欠陥があり、下記のような入力があった際にデータが吹き飛びます。
入力
<a href="https://web.archive.org/web/20190715113557/http://google.com">Google</a> <a href="https://web.archive.org/web/20190715113557/http://forum.minecraftuser.jp/">Minecraft非公式日本ユーザーフォーラム</a>出力
<a href="https://forum.minecraftuser.xyz/">Minecraft非公式日本ユーザーフォーラム</a>だいぶ中身がスッキリしましたね()
原因
このコードを書いた際、仕様に書いたURLの変化に雑に対応させようとして.*を使ってしまったのが原因
対応
$出力 = preg_replace('/http ?:\/\/web\.archive\.org\/web\/...............?.?.?\/http ?:\/\/forum\.minecraftuser\.jp/', "https://forum.minecraftuser.xyz", "データ");不用意に.*を使用すると、予期しない場所が正規表現の対象になる可能性がありますので、お気おつけください
正規表現を使う際に参考になるサイト
- 投稿日:2020-07-20T20:31:09+09:00
AWS SDK のリクエストヘッダを見る方法
- AWS SDK for PHP の場合
例えばsqsクライアントの場合$client = new SqsClient([ 'region' => ***, 'version' => ***, 'credentials' => [ 'key' => ***, 'secret' => *** ], 'debug' => true, # ←コレを追加 ]);debugを
true
にすることで標準出力にログが出力される。
参考: https://docs.aws.amazon.com/ja_jp/sdk-for-php/v3/developer-guide/faq.html
- AWS SDK for JavaScript の場合
例えばsesクライアントの場合new AWS.SES().sendEmail(sesParams, function(err, data){ if(error){ console.log(this.httpResponse); // ← コレを追加 } else { console.log(this.httpResponse); } });
this
というのはAWS.Response
のことらしい。基本的にはレスポンスの情報を参照出来るらしいけど、リクエストした情報(AWS.Request
)も見れる。
参考: https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-a-callback-function.html
- 投稿日:2020-07-20T20:27:06+09:00
【PHP8.0】PHPにヌル安全オペレータが導入される
ユーザの住んでいる国を取得します。
しかし、うっかりユーザがnullでした。
どうなるでしょう。$user = null; echo $user->address->country ?? '';PHPのプロパティは元よりnull安全なので、存在しないプロパティだろうがnullだろうがプリミティブ型だろうがいきなりプロパティを取り出せます。
取れない場合はE_NOTICEが発生しますが、??
を使えば黙殺できます。しかしメソッドはだめです。
echo $user->getAddress()->getCountry() ?? ''; // Fatal error: Call to a member function getAddress()律儀にエラーを潰すとこんなかんじになります。
// 1 if (method_exists($user, 'getAddress')) { if (method_exists($user->getAddress(), 'getCountry')) { echo $user->getAddress()->getCountry(); } } // 2 if ($user instanceof \User) { if ($user->getAddress() instanceof \Address) { echo $user->getAddress()->getCountry(); } }まあ面倒ですね。
ということでPHPにも流行りのnull安全オペレータが導入されることになりました。
以下は該当のRFC、Nullsafe operatorの日本語訳です。
Nullsafe operator
Introduction
このRFCでは、Null安全オペレータ
?->
を提案します。Proposal
メソッドを呼び出したり、計算結果からプロパティを取り出したりといった作業は、nullではない対象にだけ行いたいのが普通です。
現在のPHPではnullチェックを入れる必要があるので、入れ子や繰り返しが多くなります。$country = null; if ($session !== null) { $user = $session->user; if ($user !== null) { $address = $user->getAddress(); if ($address !== null) { $country = $address->country; } } } // $countryが取り出せたnull安全オペレータ
?->
を使うと、このコードは以下のように書けます。$country = $session?->user?->getAddress()?->country;演算子の左側がnullだった場合はチェーン全体の実行が停止され、nullが返ります。
nullでなかった場合は通常の->
と全く同じ動作になります。Short circuiting
Introduction
ショートサーキット、短絡とは、条件に基づいて式の実行をスキップすることです。
よくある例としては&&
と||
などです。null安全オペレータについて、短絡を実装する方法は3種類ありました。
どの方法がよいか、以下の同じコードスニペットで見ていきましょう。null?->foo(bar())->baz();1. Short circuiting for neither method arguments nor chained method calls
・引数もメソッドも全て実行する、短絡しない。
このような例は、今のところHackくらいでしか見られません。
関数bar()
もメソッドbaz()
も両方とも実行されるため、baz()
でCall to a member function on null
のエラーが発生します。
3つの選択肢の中で、最も予想外の結果となるでしょう。
前回のRFCでの大きな問題点でした。2. Short circuiting for method arguments but not chained method calls
・引数は飛ばすけどメソッドは飛ばさない。
これは、通常は短絡しないと呼ばれるものです。
関数bar()
は呼び出されませんが、メソッドbaz()
は呼び出されてCall to a member function on null
のエラーが発生します。3. Short circuiting for both method arguments and chained method calls
・引数もメソッドも全てスルー
これは完全短絡と呼ぶことにしましょう。
関数bar()
もメソッドbaz()
も呼び出されず、エラーは発生しません。Proposal
このRFCでは、完全短絡を実装します。
チェーン内のひとつの要素の評価に失敗した場合、それ以降の全てが中断され、チェーン全体がnullと評価されます。
以下の要素はチェーンの一部とみなされます。・配列
[]
・プロパティ->
・null安全プロパティ?->
・静的プロパティ::
・メソッドコール->
・null安全メソッドコール?->
・静的メソッドコール::
以下はチェーンに含まれず、別のチェーンが発生します。
・関数の引数
・配列[]
内部の式
・{}
にプロパティアクセスした場合->{}
チェーンは最も短くなるように自動判別されます。
以下はその例です。$foo = $a?->b(); // --------------- chain 1 // -------- chain 2 // $aがnullだったらchain2が中断、b()は実行されず$foo=nullになる $a?->b($c->d()); // --------------- chain 1 // ------- chain 2 // $aがnullだったらchain1が中断、b()は実行されず$foo=nullになる。chain2も実行されない。 $a->b($c?->d()); // --------------- chain 1 // -------- chain 2 // $cがnullだったらchain2が中断、d()は実行されない。chain1の$a->b()は実行されて引数はnullになる。Rationale
この動作を選択した理由。
1. It avoids surprises
$foo = null; $foo?->bar(expensive_function());驚きを最小にします。
$foo
がnullであればexpensive_function()
の実行は望ましくないでしょう。2. You can see which methods/properties return null
$foo = null; $foo?->bar()->baz();短絡がないと、チェーン内のメソッド呼び出しやプロパティアクセス全てにnull安全オペレータを使用しなければならなくなります。
不要な部分には使わないことで、どのメソッドやプロパティに問題があるのかを特定することもできます。3. Mixing with other operators
$foo = null; $baz = $foo?->bar()['baz']; var_dump($baz); // 短絡がない場合:Notice: Trying to access array offset on value of type null // このRFC:null短絡によって配列アクセス
['baz']
は完全にスルーされるので、E_NOTICEは発生しません。Other languages
Stack Overflow 2020 surveyによる人気の高水準言語、および姉妹言語Hackについて、null安全オペレータの実装状況を確認してみます。
言語 有無 表記 短絡 JavaScript 〇 ?. 〇 Python Java C# 〇 ?. 〇 TypeScript 〇 ?. 〇 Kotlin 〇 ?. × Ruby 〇 &. × Swift 〇 ?. 〇 Rust Objective-C ※1 Dart 〇 ?. × Scala ※2 Hack 〇 ?→ ※3 ※1:nilへのプロパティやメソッド呼び出しは常に無視される
※2:DSL経由で可能
※3:メソッド引数も評価する13のうち8言語がnull安全オペレータを持っており、そのうち4言語は短絡評価します。
Syntax choice
?
が、短絡が発生する正確な場所を表します。
これは、null安全オペレータを実装している他の全ての言語と似ています。Forbidden usages
Nullsafe operator in write context
null安全オペレータを代入に使用することはできません。
$foo?->bar->baz = 'baz'; // Can't use nullsafe operator in write context foreach ([1, 2, 3] as $foo?->bar->baz) {} // Can't use nullsafe operator in write context unset($foo?->bar->baz); // Can't use nullsafe operator in write context [$foo?->bar->baz] = 'baz'; // Assignments can only happen to writable valuesこのRFCの以前のバージョンでは、
=
の左側にnull安全オペレータを使った場合、nullであれば代入をスキップすることが提案されていました。
しかし、技術的な問題からこの仕様は外されました。
今後のRFCで追加されるかもしれません。References
リファレンスは許可されません。
参照するためには変数やプロパティのメモリ上の値が必要となりますが、null安全オペレータはnullを返すことがあるからです。$x = &$foo?->bar; // おおむね↓と同じ if ($foo !== null) { $x = &$foo->bar; } else { $x = &null; // Only variables should be assigned by referenceのエラー }従って、以下のような例は禁止となります。
$x = &$foo?->bar; // Compiler error: Cannot take reference of a nullsafe chain takes_ref($foo?->bar); // Error: Cannot pass parameter 1 by reference function &return_by_ref($foo) { return $foo?->bar; // Compiler error: Cannot take reference of a nullsafe chain }引数に参照を渡せるかどうかはコンパイル時にはわからないため、2番目の例は実行時エラーになります。
Backward Incompatible Changes
既知の後方互換性のない変更はありません。
Future Scope
PHP7.4以降、nullに配列アクセス
null["foo"]
するとE_NOTICEが発生します。
そのため、演算子?[]
も許可し、$foo?["foo"]
と書けるようになると有用かもしれません。
しかし三項演算子$foo?["foo"]:["bar"]
と曖昧になってしまうため、このRFCには配列へのnull安全オペレータは含まれていません。投票
期間は2020/07/31まで、投票者の2/3の賛成で受理されます。
2020/07/20時点では賛成44反対2で、まず確実に導入されます。感想
正直null安全があまり理解できてないので、このRFCがどのくらい有用なのかもよくわかっていません。
個人的には、メソッドチェーンが書きやすくなるくらいしかメリットを感じられていないです。
そもそもPHPでは元々型がアバウトなうえ、うっかり下手にぬるぽが起こってたとしてもメモリを壊したりなんてことはまずできませんからね。
最悪でもせいぜい500 Internal Server Errorになる程度です。しかし投票を見るに、union型やstr_containsを遥かに超え、JITやmatch式に匹敵するほどの圧倒的賛成多数です。
それだけ有用であると皆に判断されたということで間違いないでしょう。
PHPにおけるnull安全の優秀さはきっと誰かが教えてくれるはず。
- 投稿日:2020-07-20T18:08:54+09:00
PHPで性格診断アプリを作ってみた
性格診断アプリ
前書き
こんにちは。業務としてWebAppエンジニアリングをかじっている者です。今回はXAMPPで性格診断アプリを作ってみました。今まで自分一人でログイン機能付きのアプリを作ったことがなかったのでいい勉強になりました。XAMPPのインストール手順に関しては別記事をご参照ください。ソースコードはこちらです。
構成
XAMPPをインストールすると
C:\xampp\htdocs
がドキュメントルートになります。例えばこのhtdocs
直下にindex.html
やindex.php
などのindex
という名前のファイルを置いた状態で、ブラウザにhttp://localhost
と入力するとローカル環境でAppを起動することができます。(事前にXAMPP Control Panel
内のApacheをStart
させておいてください)htdocs下の構成は以下のようになります。
C:\xampp\htdocs> ls ディレクトリ: C:\xampp\htdocs Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 2020/07/13 16:41 css d----- 2020/06/22 10:47 dashboard d----- 2020/07/13 12:45 img d----- 2020/06/22 10:47 webalizer d----- 2020/06/22 10:47 xampp -a---- 2020/07/14 16:19 152 config.php -a---- 2015/07/17 0:32 30894 favicon.ico -a---- 2020/07/15 22:28 5965 home.html -a---- 2020/07/14 16:14 3241 index.php -a---- 2020/07/15 22:29 1524 login.php -a---- 2020/07/14 19:35 909 logout.php -a---- 2020/07/15 22:29 1971 result-list.php -a---- 2020/07/15 22:30 11434 result.php -a---- 2020/07/15 22:03 2174 signUp.phpC:\xampp\htdocs\css> ls ディレクトリ: C:\xampp\htdocs\css Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2020/07/15 13:44 1394 style.cssデータベースのテーブル構成については以下のようになります。
dbname=pd_login テーブル: userdata email varchar(255) utf8mb4_unicode_ci unique key password varchar(255) utf8mb4_unicode_ci テーブル: diagnosis-result email varchar(255) utf8mb4_unicode_ci execution_time datetime friendly int(11) extrovert int(11) emotional int(11) positive int(11) leader int(11)ログイン
htdocs
直下にindex.html
やindex.php
などのindex
という名前のファイルを置いた状態で、ブラウザにhttp://localhost
と入力するとindex.php
が表示されます。これがログイン機能の大元になるファイルです。新規登録機能と通常ログイン機能を持たせています。新規登録処理について、メールアドレスとパスワードにはフィルターをかけている。パスワードに関しては正規表現で適切でないものをはじいています。メールアドレスに関してはfilter_var
を利用して、RFC822
で判定しています。RFC822
は判定がガバガバ説があるので、ビジネスで使うときは注意です。パスワードはセキュアにデータベースに保存するためpassword_hash()
関数を使ってhash化しています。if (!$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) { echo '入力された値が不正です。'; return false; }if (preg_match('/\A(?=.*?[a-z])(?=.*?\d)[a-z\d]{8,100}+\z/i', $_POST['password'])) { $password = password_hash($_POST['password'], PASSWORD_DEFAULT); } else { echo 'パスワードは半角英数字をそれぞれ1文字以上含んだ8文字以上で設定してください。'; return false; }通常ログイン機能では以下のようにパスワードを処理します。登録時にhash化したパスワードを
password_verify()
で照合しています。# login.php if (password_verify($_POST['password'], $row['password'])) { session_regenerate_id(true); //session_idを新しく生成し、置き換える $_SESSION['EMAIL'] = $row['email']; echo 'ログインしました'; session_write_close(); header( "Location: ./home.php" ) ; exit(); } else { echo 'メールアドレス又はパスワードが間違っています。'; return false; }性格診断
index.php内のinputタグから
password
をpostするとそれをlogin.php
がデータを受け取り、本人確認が済んだらhome.html
に遷移します。以下がhome.html
の質問文の箇所です。value
には質問ごとの性格要素の値が入っています。<form method="post" action="result.php"> <h2>第一問</h2> <p>あなたは外で遊ぶより、家で過ごすほうが好きですか?</p> <input type="radio" name="q1" value="1"> かなりそう思う<br> <input type="radio" name="q1" value="2"> そう思う<br> <input type="radio" name="q1" value="3"> どちらとも言える<br> <input type="radio" name="q1" value="4"> あまりそう思わない<br> <input type="radio" name="q1" value="5" required> そう思わない<br>診断結果
home.html
で入力した値はresult.php
にpostされます。以下はvalue
ごとの性格要素の値を集計して、4分岐でユーザの性格を判定している箇所です。この例だと質問2と質問6はユーザの興味の対象に関する要素を決定する項目です。この2つの質問のvalue
を合算してその合計でユーザの性格を4つに分類しています。# result.php if($q2+$q6>8){ $hito_mono = "非常にモノよりヒト"; $hito_mono_comment = "あなたは特にこの傾向が強く、ヒトと関わっている時間を楽しみに感じています。 仕事を選ぶ際にもなるべくヒトと関わる仕事を選ぶとよいでしょう。"; }elseif($q2+$q6>5){ $hito_mono = "どちらかといえば、モノよりヒト"; $hito_mono_comment = "あなたはヒトと関わる時間に楽しみを感じる一方で、モノにも興味をもてます。 何か自分の興味のあるモノやコトに打ち込みながら、それをヒトとシェアできるバランスの良さをもっています。"; }elseif($q2+$q6>3){ $hito_mono = "どちらかといえば、ヒトよりモノ"; $hito_mono_comment = "あなたはモノやコトに打ち込んでいる瞬間に楽しみを感じる一方で、他人にも興味をもてます。 何か自分の興味のあるモノやコトに打ち込みながら、それをヒトとシェアできるバランスの良さをもっています。"; }else{ $hito_mono = "非常にヒトよりモノ"; $hito_mono_comment = "あなたは特にこの傾向が強く、モノやコトに打ち込んでいる瞬間を楽しみに感じています。 仕事を選ぶ際にもなるべく一人で集中できる仕事を選ぶとよいでしょう。"; }判定結果の履歴
home.html
に以下のボタンを設置しています。<button class="fixed_btn" onclick="location.href='./result-list.php'">過去の診断結果</button>実は
result.php
内でデータベースにそれぞれの結果を保存しています。home.html
はresult-list.php
に遷移した後、このデータベースに接続します。result-list.php
では以下のような処理を行っており、ログインしているユーザの# result-list.php function createHtmlTable($result) { $html = "<table border='3' cellspacing='4' cellpadding='4'>"; $ffields = $result->fetch_fields(); $html .= "<tr>"; foreach ($ffields as $val) { $html .= "<th>" . $val->name . "</th>"; } $html .= "</tr>"; foreach ($result as $row) { $html .= "<tr>"; foreach ($ffields as $val) { $value = $row[$val->name]; $html .= "<td>${value}</td>"; } $html .= "</tr>"; } $html .= "</table>"; return $html; }Session
ちなみに、ページ遷移してもログイン時の
Session
を利用しているからです。login.php
内では、以下のような処理をしています。# login.php $_SESSION['EMAIL'] = $row['email'];遷移した後のページでこの
$_SESSION['EMAIL']
を使うときは、以下のような記述をする必要があります。<?php session_start();ハマったこと
データベースの接続
文字列の変数はちゃんと
'$hoge'
などシングルクオーテーションマーク(あるいはダブルクォーテーションマーク)で囲む必要があります。# result.php $pdo = new PDO('mysql:host=localhost;dbname=pd_login;charset=utf8mb4','root',''); $sql = "INSERT INTO `diagnosis-result`(`email`, `execution_time`, `friendly`, `extrovert`, `emotional`, `positive`, `leader`) VALUES ('$user_email','$current_time',$q2+$q6,$q1+$q7,$q3+$q8,$q5+$q9,$q4+$q10)"; $qry = $pdo->prepare($sql); $qry->execute(); $pdo = null;データベースのデータ型
パスワードをhash化していますが、長さには余裕をもたせる必要があります。私は
varchar(16)
とやってしばらくハマっていました。varchar(255)
にしたら通りました。参考
- 投稿日:2020-07-20T13:22:47+09:00
【PHP】Phan の静的コード解析で use 指定したクラスオブジェクトを認識してくれない
Phan から PhanUndeclaredTypeProperty やら PhanUndeclaredMethod やら PhanUndeclaredClassMethod やら PhanUndeclaredFunction と叱られる
助っ人メンバーを入れたのに、一部の熱狂的なファンが認めてくれず活動できません。
sample.php<?php declare(strict_types=1); namespace MYNAME\MYPACKAGE; use Symfony\Component\Cache\Adapter\FilesystemAdapter; //助っ人。でも Phan からは「こんなやつ、聞いとらん」と怒られる class MyClass { /** @var FilesystemAdapter */ //← Phan から「こんなやつ、しらん」と怒られる protected $my_property; public function __construct() { $this->my_property = new FilesystemAdapter(); } public function set(string $key, $value) { $cache_item = $this->cache_pool->getItem($key); //← Phan から「そんなん、できん」と叱られる $cache_item->set($value); //← Phan から「なんやそれ」と叱られる $this->cache_pool->save($cache_item); //← Phan から「出なおしてこい」と叱られる } }エラー内容PhanUndeclaredTypeProperty Property \MYNAME\MYPACKAGE\MyClass->my_property has undeclared type \Symfony\Component\Cache\Adapter\FilesystemAdapter
FilesystemAdapter
オブジェクトのメソッド、例えばgetItem()
を使っても、「そんなメンバー(メソッド)しらんし、聞いとらん」と叱られます。use
で告知したよ?エラー内容PhanUndeclaredMethod Call to undeclared method \MYNAME\MYPACKAGE\MyClass->my_property->getItem($key)TS; DR
Phan が
vendor
ディレクトリなどにある依存パッケージ(ライブラリ)を読み込んでから静的解析をしていないことが原因です。各メソッドの PHPDoc コメントに
@suppress PhanUndeclaredClassMethod
を追加してもいいのですが、config.php
ファイルを作成して、対象ディレクトリにvendor
を追加しつつ、解析対象対象から外すと、パッケージがインポートされるようになります。phan.php<?php return [ 'directory_list' => [ 'src', 'vendor/symfony', // use で使うパッケージを追加 ], 'exclude_analysis_directory_list' => [ 'vendor/' // 解析対象から除外 ], ];解析の実行例$ # --allow-polyfill-parser → php-ast がなくても解析する設定 $ # --config-file ./phan.php → 設定ファイルを指定 $ ./vendor/bin/phan --allow-polyfill-parser --config-file ./phan.php ...TL; DR
作曲家(
composer
)の言うことなんか聞かない PhanPHP の静的解析ツール「Phan」は静的解析時、
composer
などのautoloader
によるダイナミックなインポート先を参照しません。そのため、
use
で宣言しても解析時に必要なパッケージ/ライブラリがインポートされていないため解析エラーが出ます。
suppress
コメント(エラー無視コメント)の「@suppress PhanUndeclaredClassMethod
」を PHPDoc のブロックコメントに入れて黙殺してもいいのですが、やはり Phan の意見は尊重したいものです。色々ググってみたのですが、Phan の公式 Wiki の FAQ にちゃんと載ってました。
Phan says that a composer dependency of my project(a class, constant, or function) is undeclared
- You must add the directory (or file) of that dependency to
'directory_list'
or'file_list'
in.phan/config.php
. Usually, you will want to add'vendor'
to'exclude_analysis_directory_list'
as well. See https://github.com/phan/phan/wiki/Getting-Started#creating-a-config-file- Make sure that the file exists in vendor/ (e.g. make sure that
composer.phar install
was executed if this is running in CI)- Make sure that there are no typos in the variable name, that the namespace of the class is correct, and that the file containing the class/constant/function exists.
This is a common cause of
PhanUndeclaredClassMethod
,PhanUndeclaredClass
,PhanUndeclaredFunction
, etc.
(Frequently Asked Questions | Phan 公式 Wiki @ GitHub より)(筆者適当訳)
作曲家による(クラス、定数、または関数)の依存関係に対し、熱狂的なファンが「宣言していない」と怒っています。
- 依存関係のディレクトリ(またはファイル)を
.phan/config.php
の'directory_list'
もしくは'file_list'
に追加する必要があります。その場合、一般的に'vendor'
ディレクトリを'exclude_analysis_directory_list'
にも追加します。詳しくは https://github.com/phan/phan/wiki/Getting-Started#creating-a-config-file を参照ください。- ファイルが
vendor/
以下に存在することを確認してください。(たとえば CI で解析を実行する場合に「あらかじめcomposer.phar install
が実行されているか」などです)- 変数名にタイプミスがないこと、クラスの名前空間が正しいこと、およびクラス/定数/関数を含むファイルが存在することを確認してください。
これは
PhanUndeclaredClassMethod
、PhanUndeclaredClass
、PhanUndeclaredFunction
などが発生する「あるある」な原因です。とほほ。
参考文献
- Creating a Config File | Phan 公式 Wiki @ GitHub
- このPHPがテンプレートエンジンのくせに慎重すぎる (中篇) @ Qiita
- Phanで静的解析 @ Qiita
- 投稿日:2020-07-20T13:05:17+09:00
AWS Amazon linux 2でLaravel7の環境構築
新たにLaravel7でプロジェクトを作成する事になったため、今回必要な環境の構築覚書。
php :php 7.3
フレームワーク :Laravel 7
データベース :MariaDB 10.5.4
テンプレートエンジン:twig
ログイン管理 :sentinel 4.x以前素のphpで書かれていたプロジェクトの改修なのでテンプレートエンジンとログイン管理は該当プロジェクトと同じものを使用する事に。
EC2立ち上げからphp,apache,Composer,Laravelのインストールまで
AmazonLinux2でLaravelの開発環境構築
上記ページを参考に行う。MariaDBのインストールと設定
yumデフォルトでインストールできるmaria-dbはバージョン5.5の為、レポジトリを設定してダウンロードする。
インストール時は MariaDB-server の綴りに注意。(MとDBが大文字)$ curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash $ sudo yum install MariaDB-serverデータベースの文字列をUTF-8に設定する。/etc/my.cnf.d/server.cnf の [mariadb] セクションに以下の設定を追加。
/etc/my.cnf.d/server.cnf[mariadb] character-set-server=utf8有効化~開始~セキュリティ設定
$ sudo systemctl enable mariadb $ sudo systemctl start mariadb $ sudo mysql_secure_installation Set root password? [Y/n] Remove anonymous users? [Y/n] Disallow root login remotely? [Y/n] Remove test database and access to it? [Y/n] n Reload privilege tables now? [Y/n] ... Thanks for using MariaDB!ログイン~文字コード確認
#ログイン mysql -u root -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 23 Server version: 10.5.4-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. #文字コード確認 MariaDB [(none)]> show variables like 'char%'; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | utf8 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | utf8 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+ #ログアウト MariaDB [(none)]> exit Byetwigインストール(LaravelでTwigを利用する専用パッケージrcrowe/twigbridgeのインストール)
$ composer require rcrowe/twigbridgeSentinelインストール
$ composer require caratalyst/sentinel "^4.0"参考文献
AmazonLinux2でLaravelの開発環境構築
CentOS 7 に MariaDB Community Server 最新版をインストール
EC2(Amazon Linux 2)にmariaDBを導入する
簡単!Laravelでtwigテンプレートエンジンを使う実例
- 投稿日:2020-07-20T12:28:54+09:00
Laravel CRUD処理を使った投稿アプリを作成する その5 投稿削除機能
目的
- アプリを作成する上で基本となるCRUD処理を有したLaravelアプリをチュートリアル的に作成する方法をまとめる
実施環境
- ハードウェア環境
項目 情報 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をインストールする 前提条件
- 実施環境に近い環境が構築されていること。
- 筆者は下記の方法で環境構築を行った。
前提情報
- DockerやAWSなどは使用せずにMacのローカルに実施環境と同じLaravel開発環境を構築して実施する。
- チュートリアルで実際に筆者が作成したソースコードをGitHubにて公開予定である。
- CRUD処理の作成完了を最短目標にしてバリデーションなどは後々設定することとする。
- 実施環境と同じ環境がDockerやAWSで用意できるなら都度読み替えていただければ実施が可能だと思う。
- 公式ドキュメントと一冊の技術書を元に本記事を記載する。
- Laravel 7.x
- PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応
- 本記事はシリーズとして内容を分割する予定である。記事のタグ「miriwo_laravelチュートリアル」を本シリーズの記事に付与するのでそのほかの記事がみたい方は活用していただきたい。
この記事の読後感
- 投稿内容の削除機能が実装できる。
全ての記事(miriwo_laravelチュートリアル)を通した読後感
- Laravelアプリでログインなどのユーザ認証付き投稿アプリの作成ができる。
概要
- ルーティングの記載
- コントローラの記載
- ビューファイルの修正
- 確認
詳細
ルーティングの記載
laravel_crudディレクトリで下記コマンドを実行してルーティングファイルを開く。
$ vi routes/web.php開いたファイルに下記の行を追記する。
laravel_crud/routes/web.phpRoute::post('/delete', 'ContentController@delete')->name('delete');コントローラの記載
laravel_crudディレクトリで下記コマンドを実行して作成したコントローラファイルを開く。
$ vi app/Http/Controllers/ContentController.php下記の内容をクラス内に追記する。
laravel_crud/app/Http/Controllers/ContentController.phppublic function delete(Request $request) { $contents_delete_query = Content::select('*'); $contents_delete_query->where('id', $request['content_id']); $contents_delete_query->delete(); return redirect('/output'); }記載後のコントローラファイルの内容を下記に記載する。
laravel_crud/app/Http/Controllers/ContentController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Models\Content; class ContentController extends Controller { public function input() { return view('contents.input'); } public function save(Request $request) { $input_content = new Content(); $input_content->content = $request['content']; $input_content->save(); return redirect('/output'); } public function output() { $contents_get_query = Content::select('*'); $all_contents = $contents_get_query->get(); return view('contents.output', [ 'all_contents' => $all_contents, ]); } public function delete(Request $request) { $contents_delete_query = Content::select('*'); $contents_delete_query->where('id', $request['content_id']); $contents_delete_query->delete(); return redirect('/output'); } }ビューファイルの修正
laravel_crudディレクトリで下記コマンドを実行してビューファイルを開く。
vi resources/views/contents/output.blade.php
開いたビューファイルに下記の内容を追記する。
laravel_crud/resources/views/contents/output.blade.php<h1>output</h1> @foreach ($all_contents as $item) <hr> <p>{{$item['content']}}</p> <!-- 下記を追記 --> <form action="{{route('delete')}}" method="post"> @csrf <input type="hidden" name="content_id" value="{{$item['id']}}"> <input type="submit" value="削除"> </form> @endforeach確認
laravel_crudディレクトリで下記コマンドを実行してローカルサーバを起動する。
$ php artisan serveブラウザで下記にアクセスする。
「削除」ボタンをクリックして投稿内容が削除されることを確認する。
下記の様に投稿内容が削除されれば本記事の内容は完了である。
- 投稿日:2020-07-20T11:39:55+09:00
Laravel学習用備忘録 その2
- 投稿日:2020-07-20T10:53:17+09:00
【備忘録です】【PHP】DB操作
備忘録です
DB操作のイメージを掴むために残します。
phpMyAdminを使ってWebブラウザ上からDB操作を行ってきましたが、コードを書いてDB操作を行うこともできます。
sample.php<pre> <?php $host = 'localhost'; // データベースのホスト名又はIPアドレス ※CodeCampでは「localhost」で接続できます $username = 'username'; // MySQLのユーザ名 $passwd = 'passwd'; // MySQLのパスワード $dbname = 'dbname'; // データベース名 $link = mysqli_connect($host, $username, $passwd, $dbname); // 接続成功した場合 if ($link) { // 文字化け防止 mysqli_set_charset($link, 'utf8'); $query = 'SELECT goods_id, goods_name, price FROM goods_table'; // クエリを実行します $result = mysqli_query($link, $query); // 1行ずつ結果を配列で取得します while ($row = mysqli_fetch_array($result)) { print $row['goods_id']; print $row['goods_name']; print $row['price']; print "\n"; } // 結果セットを開放します mysqli_free_result($result); // 接続を閉じます mysqli_close($link); // 接続失敗した場合 } else { print 'DB接続失敗'; } ?> </pre>クエリ・・・DBに対する命令文のことです。
mysqli_set_charset・・・クライアントのデフォルト文字セットを設定しています。
mysqli_query・・・データベース上でクエリを実行します。
mysqli_fetch_array・・・結果の行を連想配列・数値添字配列あるいはその両方で取得します。
mysqli_free_result・・・結果に関連付けられたメモリを解放します。上記のコードでは
1.DB接続
2.文字コード設定
3.クエリ実行
4.結果を取得
5.メモリ解放
6.DB切断という処理を行っており、
DBに接続してSELECT文を実行し全レコードを表示ということをしています。phpMyAdminでは裏で自動でやってくれていましたが、今回の場合は書く必要があります。DB接続
sample.phpmysqli_connect($host, $username, $passwd, $dbname) $host データベースのホスト名又はIPアドレス $username MySQLのユーザ名 $passwd MySQLのパスワード $dbname データベース名 //機能 //データベースへ接続 //返り値 //リンクID(サーバへの接続を表すオブジェクト) //失敗するとFALSEを返すmysqli_connect()にデータべースの情報を渡すことで、接続ができます。
返り値はリンクIDで、「接続したデータベースを特定するためのID」のようなものです。
DB接続は、phpMyAdminにおけるログインと同じ動作になります。sample.php$link = mysqli_connect($host, $username, $passwd, $dbname); // 接続成功した場合 if ($link) { // 文字化け防止 mysqli_set_charset($link, 'utf8'); $query = 'SELECT goods_id, goods_name, price FROM goods_table'; // クエリを実行します $result = mysqli_query($link, $query);上記のif文で接続に成功したか失敗したかの判定を行っています。
もしDB接続に失敗しても処理が止まるわけではなく、ソースコード従って以降も順に実行されていきます。DB接続に失敗した後に、クエリー実行(mysqli_query)やDB切断(mysqli_cliose)などを行っても必ず失敗するため、無駄な処理となってしまいます。
そのため、if文を用いて以降の処理を行うかどうかを決定しています。
クエリ実行
クエリ実行は以下のように行れます。
sample.phpmysqli_query($link, $query) $link リンクID $query 実行するクエリ //機能 //クエリを実行する //返り値 //成功すると結果セット、もしくはTRUEを返す //失敗するとFALSEを返すmysqli_query()は、実行する命令によって成功時の返り値が変わります。
SELECT文を発行した場合は、結果セットが返りますが、INSERET文やUPDATE文、 DELETE文の場合はTRUEが返ります。
結果セットとは、「SELECTにより選択されたデータのかたまり」のようなものです。
phpMyAdminではSELECT文を入力し実行すれば結果を表示まで自動でやってくれましたが、プログラムの場合は実行結果の何の情報をどのように取得するか、別途指定する必要があります。
結果を取得
実行したクエリの結果取得は、以下のように行います。
sample.phpmysqli_fetch_array($result) $result 結果セット //機能 //結果セットを配列として返す //返り値 //成功すると行の配列を返す //読み込むデータがなくなるとNULLを返す //失敗するとFALSEを返すfetchとは「取ってくる」という意味で、SELECTした結果を1行だけ配列として取得します。
取得は1行だけなので、全てのファイルを読み込むにはwhile文と組み合わせて使います。
phpMyAdminでは裏で自動的にやってくれていた処理ですが、プログラムを書く場合、SELECT文はmysqli_query()とmysqli_fetch_array()がセットになります。メモリ開放
sample.phpmysqli_free_result($result) $result 結果セット //機能 //結果に関連付けられたメモリを開放する //返り値 //値を返さないメモリとは、コンピュータ内でデータやプログラムを記憶する装置です。
メモリはデータの保存や取り出しが高速で行え、一時的なデータの保存場所として利用します。
またメモリには容量があり、容量を越えると急激に処理が遅くなります。
結果セットはこのメモリに保存されており、メモリ開放をしなくても現時点では問題はないですが、
後々のためmysqli_query()で結果セットを取得した場合、利用し終わったらすぐにmysqli_free_result()で開放しましょう。DB切断
DB切断は以下のように行います。
sample.phpmysqli_close($link) $link: リンクID //機能 //データベースから切断する //返り値 //成功するとTRUEを返す //失敗するとFALSEを返すファイル操作と同様、データベースに接続している時間は可能な限り短いほうがいいためSELECTや INSERTなどの必要な処理が終わったらすぐに切断します。
DB切断は、phpMyAdminにおけるログアウトと同じ動作となります。