- 投稿日:2020-03-29T22:48:14+09:00
VSCode+docker-compose+php(xdebug)の環境構築のポイント
はじめに
ありふれた記事なのではあるが一発では起動できない。。
なので基本的なところは他の記事に譲るとして、必ずデバッグする為のポイントを上げてみた。1. VSCodeにPHPDebugをインストール
インストール後にF5などで
RUN and Debug
を起動してlaunch.jsonを適当に作る。launch.json{ "port": 19000, "pathMappings": { "/var/www/workspace/php_loach":"/Users/xxx/workspace/project/php_loach" } }注)他にも必要な項目はあるが、わかりやすく、
port
とpathMappings
だけ取り上げた。他の記事で確認してほしい。
portはxdebugで指定するportと合わせる。webでアクセスするportとは違う。
pathMappingsのディレクトリは、projectのドキュメントルートを指定。dockerとローカルは同じディレクトリを指定。2. サーバ側にxdebugをインストール
Dockerfileでxdebugをインストールする。(詳細は他の記事で確認してほしい)
そしてxdebug.iniを用意する。xdebug.ini; Enable xdebug extension module zend_extension=/usr/lib64/php/modules/xdebug.so ; xdebug xdebug.remote_enable = 1 xdebug.remote_enable = 1 xdebug.default_enable = 1 xdebug.remote_host = docker.for.mac.localhost xdebug.remote_port = 19000 xdebug.remote_handler=dbgp xdebug.remote_mode=req xdebug.remote_autostart = Onこんな感じの。(詳細は他の記事で確認してほしい)
ここのremote_portとlaunch.jsonのportをあわせる。
docker-composeのportは全く関係ない。docker-composeはアクセス用のportを書くところでxdebugのportは書かない。無視。3. VSCodeのdebugを起動
launch.jsonを正しく設定してあると、F5を押しただけで、下のバーがオレンジに変わってXdebugが起動する。
そして、止めたいところでコードの左側にブレイクポイント(赤いマーク)をつける。4. クライアント側の設定を忘れない
これ、忘れがち。。
ブラウザなら虫マークのdebugの拡張を追加して、虫マークを緑にする。
POSTMANなどでAPIのエンドポイントにアクセスするときは、エンドポイントの最後に
XDEBUG_SESSION_START=true
を追加する。
http://localhost:9001/api/getUser?XDEBUG_SESSION_START=true
これを忘れるとブレイクポイントで止まらない!!終わりに
自分のハマりポイントを上げてみた。これですんなりと動いたのでこれ以外は思い浮かばない。。
もし、他にハマりポイントがあれば教えてほしい。
- 投稿日:2020-03-29T20:35:34+09:00
eval(\psy\sh())がローカルちゃんと動かないとき
phpbrewでreadlineつきでphpインストール
phpbrew install php-7.2.28 +default +readline +dbs +fpm
- 投稿日:2020-03-29T19:49:00+09:00
常に動くLINEBOTにお引っ越し(レンタルサーバ+PHP編)
今回のモチベーション
前回、こちらの記事を参考にWikipedia APIを使った、調べものLINE botを作った。
前回の記事
https://qiita.com/shima-07/items/2322598ca5a40cfee47bだが、
- ngrokを立ち上げている時しか使えないから普段使えない。
- いざ、ngrokを立ち上げるとアドレスが変わってしまうため、Messaging API settingsのwebhook URLを毎度変えないと動かない。
うーん。。。
ngrok立ち上げるのめんどくさい! 常に使えるようにしないと意味ないじゃん!
と思ったわけです。だから『常に動くようにしよう!』が今回の動機です。
今回やったこと
- 1. まずは now を試してみた
- 2. さくらのレンタルサーバでやることにした
- 3. jsで書いていたものをPHPに書き直した
最終的にはPHP化してさくらサーバに載っけました。
1. まずは now を試してみた
このあたりを参考に進めてみる。
さすが良記事!サクサク進むぜと思いながら最後までいきデプロイ完了!
簡単だったなあと思いながら、Webhook URLに入れて「Verify」をクリック・・・・う。。まあよくある。
(1時間ほど立ち向かう)
色々と試すが私の手におえないと判断して諦める。2. さくらのレンタルサーバでやることにした
この記事を発見!
http://blog.hetabun.com/line-bot-php-sakuraこれ通りやることで、「こんにちは」に対して「こんにちは!」と元気よく返してくれるボットができました。
3. jsで書いていたものをPHPに書き直した
ここからが本番。前回あげた下記jsのコードと同じような振る舞いをPHPで書いていく。
PHPももちろん初心者である。再掲
server.js'use strict'; const express = require('express'); const line = require('@line/bot-sdk'); const PORT = process.env.PORT || 3000; // 追加 const axios = require('axios'); const config = { channelSecret: '作成したBOTのチャンネルシークレット', channelAccessToken: '作成したBOTのチャンネルアクセストークン' }; const app = express(); app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない) app.post('/webhook', line.middleware(config), (req, res) => { //ここのif文はdeveloper consoleの"接続確認"用なので後で削除して問題ないです。 if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){ res.send('Hello LINE BOT!(POST)'); console.log('疎通確認用'); return; } Promise .all(req.body.events.map(handleEvent)) .then((result) => res.json(result)); }); const client = new line.Client(config); function handleEvent(event) { if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } let mes = '' // console.log(event.message.text); if(event.message.text.indexOf('?') > -1){ // ?を含んでいる場合にはwikiで検索したものを出して、含んでない場合はurlを返す var str = event.message.text; var result = str.split( '?' ).join( '' ); //?を取り除く処理 mes = result + 'の説明:'; //wikiのbodyの前の一言 getBody(event.source.userId,result); //wiki APIで取得できたらプッシュメッセージ }else{ var result = event.message.text; mes = result + 'のURL:'; //wikiのurlの前の一言 getUrl(event.source.userId,result); //wiki APIで取得できたらプッシュメッセージ } return client.replyMessage(event.replyToken, { type: 'text', text : mes }); } const getBody = async (userId,word) => { const res = await axios.get('http://wikipedia.simpleapi.net/api?keyword='+ encodeURIComponent(word) + '&output=json'); const item = res.data; // console.log(item); await client.pushMessage(userId, { type: 'text', text: item[0].body, }); } const getUrl = async (userId,word) => { const res = await axios.get('http://wikipedia.simpleapi.net/api?keyword='+ encodeURIComponent(word) + '&output=json'); const item = res.data; // console.log(item); await client.pushMessage(userId, { type: 'text', text: item[0].url, }); } app.listen(PORT); console.log(`Server running at ${PORT}`);処理の整理
- LINEからのメッセージを受け取る
- そのメッセージをWikipedia APIに渡して結果を受け取る
- メッセージによってLINE側に返却するものを変える
- ?があるときはurlを返す
- ?がないときはbodyを返す
LINEからのメッセージを受け取る
参考にした記事中にあった下記の
$text
で取れているからそれはOK。LINEからのメッセージ.php//ユーザーからのメッセージ取得 $json_string = file_get_contents('php://input'); $jsonObj = json_decode($json_string); $type = $jsonObj->{"events"}[0]->{"message"}->{"type"}; //メッセージ取得 $text = $jsonObj->{"events"}[0]->{"message"}->{"text"}; //ReplyToken取得 $replyToken = $jsonObj->{"events"}[0]->{"replyToken"};そのメッセージをWikipedia APIに渡して結果を受け取る
ここが一番ハマった。
jsではres.data.item[0].body
の構造で取れていたので、
同じノリで$value = $res->{"data"}->{"item"}[0]->{"body"};
のような書き方をして、、当然何も取れず。結論、下記のような取り方でできた。
wikipediaAPIからURLやbody取得部分.php$keyword = mb_convert_encoding($text, "UTF-8", "auto"); $res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json'); $jsonwiki_decode = json_decode($res,true); // 0番目の物だけを抽出する。もっとたくさん抽出したいときはここを変更する $jsonwiki = $jsonwiki_decode[0]; //欲しい項目だけの配列にする $wikidata = array( 'url' => $jsonwiki["url"], 'body' => $jsonwiki["body"] ); $URL = $wikidata["url"]; $body = $wikidata["body"];流れとしては、
- LINEから受け取ったキーワードをAPIのkeywordとして渡せるようにエンコードする
- それをAPIに渡し
$res
として取得する- json_decodeして配列にする
- そのキーワードに対しての一番先頭の回答を取得するため[0]を取得する
- それに対して
$wikidata
として必要な項目だけ取得する- LINEに返したいものは、
$URL = $wikidata["url"]
や$body = $wikidata["body"]
として取得できる補足
上記
$res
にどんなものが入っているか見るために下記のようなものをページを作って見てました。( jsはconsole.log()で気軽に見えたけどphpではどうやってみたらいいかわからずわざわざこんなことしました。。。 )
check_data.php<html> <head> <title> test </title> </head> <body> <form method="POST" action="show.php"> キーワード: <input type="text" name="name" size="15" /> <input type="submit" name="submit" value="送信" /> </form> <?php if($_REQUEST['submit'] != null){ $input = $_REQUEST[name]; //$textのなかに'?'が含まれている場合 $text = str_replace('?', '', $input); $keyword = mb_convert_encoding($text, "UTF-8", "auto"); $res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json'); $jsonwiki_decode = json_decode($res,true); $jsonwiki = $jsonwiki_decode[0]; $wikidata = array( 'url' => $jsonwiki["url"], 'body' => $jsonwiki["body"] ); $URL = $wikidata["url"]; $body = $wikidata["body"]; print('$input: '.$input.'-----'); print('$text: '.$text.'-----'); print('$keyword: '.$keyword.'-----'); print('$res: '.$res.'-----'); print('$jsonwiki_decode :'.$jsonwiki_decode.'-----'); print('$jsonwiki :'.$jsonwiki .'-----'); print('$wikidata :'.$wikidata .'-----'); print('URL: '.$URL.'-----'); print('body: '.$body.'-----'); } ?> </body> </html>メッセージによってLINE側に返却するものを変える
- ?がある場合はURLをLINEに返す。また、Wikipedia APIに渡すときには?を取り除く。
- ?がない場合はBodyをLINEに返す。
分岐部分.phpif(strpos($text,'?') !== false){ //$textのなかに'?'が含まれている場合 $text = str_replace('?', '', $text); $keyword = mb_convert_encoding($text, "UTF-8", "auto"); $res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json'); $jsonwiki_decode = json_decode($res,true); // 0番目の物だけを抽出する。もっとたくさん抽出したいときはここを変更する $jsonwiki = $jsonwiki_decode[0]; //欲しい項目だけの配列にする $wikidata = array( 'url' => $jsonwiki["url"], 'body' => $jsonwiki["body"] ); $URL = $wikidata["url"]; $body = $wikidata["body"]; // メッセージ部分 $response_format_text = [ "type" => "text", "text" => $URL ]; }else{ // ?が含まれないときの処理 $keyword = mb_convert_encoding($text, "UTF-8", "auto"); $res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json'); $jsonwiki_decode = json_decode($res,true); // 0番目の物だけを抽出する。もっとたくさん抽出したいときはここを変更する $jsonwiki = $jsonwiki_decode[0]; //欲しい項目だけの配列にする $wikidata = array( 'url' => $jsonwiki["url"], 'body' => $jsonwiki["body"] ); $URL = $wikidata["url"]; $body = $wikidata["body"]; // メッセージ部分 $response_format_text = [ "type" => "text", "text" => $body ]; } }サンプル
できたー レンタルサーバ上で動いたー!
— yuta kawashima (@y_kawashima_) March 29, 2020
前回のはngrokでやってたから普段使えなかったけど、これでいつでも使える!
#protoout pic.twitter.com/HznZSciExJ全ソースコード
linebot.php<?php $accessToken = 'アクセストークン'; //ユーザーからのメッセージ取得 $json_string = file_get_contents('php://input'); $jsonObj = json_decode($json_string); $type = $jsonObj->{"events"}[0]->{"message"}->{"type"}; //メッセージ取得 $text = $jsonObj->{"events"}[0]->{"message"}->{"text"}; //ReplyToken取得 $replyToken = $jsonObj->{"events"}[0]->{"replyToken"}; //メッセージ以外のときは何も返さず終了 if($type != "text"){ exit; } if($type == "text"){ if(strpos($text,'?') !== false){ //$textのなかに'?'が含まれている場合 $text = str_replace('?', '', $text); $keyword = mb_convert_encoding($text, "UTF-8", "auto"); $res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json'); $jsonwiki_decode = json_decode($res,true); // 0番目の物だけを抽出する。もっとたくさん抽出したいときはここを変更する $jsonwiki = $jsonwiki_decode[0]; //欲しい項目だけの配列にする $wikidata = array( 'url' => $jsonwiki["url"], 'body' => $jsonwiki["body"] ); $URL = $wikidata["url"]; $body = $wikidata["body"]; // メッセージ部分 $response_format_text = [ "type" => "text", "text" => $URL ]; }else{ // ?が含まれないときの処理 $keyword = mb_convert_encoding($text, "UTF-8", "auto"); $res = file_get_contents('http://wikipedia.simpleapi.net/api?keyword=' . $keyword . '&output=json'); $jsonwiki_decode = json_decode($res,true); // 0番目の物だけを抽出する。もっとたくさん抽出したいときはここを変更する $jsonwiki = $jsonwiki_decode[0]; //欲しい項目だけの配列にする $wikidata = array( 'url' => $jsonwiki["url"], 'body' => $jsonwiki["body"] ); $URL = $wikidata["url"]; $body = $wikidata["body"]; // メッセージ部分 $response_format_text = [ "type" => "text", "text" => $body ]; } } $post_data = [ "replyToken" => $replyToken, "messages" => [$response_format_text] ]; $ch = curl_init("https://api.line.me/v2/bot/message/reply"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data)); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json; charser=UTF-8', 'Authorization: Bearer ' . $accessToken )); $result = curl_exec($ch); curl_close($ch);おわりに
APIから返ってきた値をいい感じで取ってくるところでだいぶハマりました。
JSONデータの扱い方にもっと慣れないとなあー次はHerokuを使ったお引っ越しもやってみようと思います。
- 投稿日:2020-03-29T19:24:09+09:00
XAMPPのphpmyadminにアクセスした時、HY000/2002が出てアクセス拒否された場合の対処
起こったこと
xmappでApacheとMySQLを起動してさてphpmyadmin/index.htmlにアクセスすると
以下みたいなエラーが出てログイン画面すら出ない状態になっていました。
今回行った対応を順に書いていきます。
※この記事ではrootユーザでのログインを行っているが実際の運用ではrootユーザではなく別のユーザを別途定義した方が無難。ログイン画面を出す
デフォルトでxmappをインストールした場合、C:\xampp\phpMyAdmin\配下にconfig.inc.phpが置いてあります。
今回はその中の一部を編集する。config.inc.phpを開き、以下の項目があることを確認してください。
何も編集していない状態であればおそらく下記のような状態になっているかと思われます。(これとは違う場合もあるかもしれない)config.inc.php/* Authentication type and info */ $cfg['Servers'][$i]['auth_type'] = 'config'; $cfg['Servers'][$i]['user'] = 'root'; $cfg['Servers'][$i]['password'] = ''; $cfg['Servers'][$i]['extension'] = 'mysqli'; $cfg['Servers'][$i]['AllowNoPassword'] = true; $cfg['Lang'] = ''; /* Bind to the localhost ipv4 address and tcp */ $cfg['Servers'][$i]['host'] = '127.0.0.1'; $cfg['Servers'][$i]['connect_type'] = 'tcp'; /* User for advanced features */ $cfg['Servers'][$i]['controluser'] = 'pma'; $cfg['Servers'][$i]['controlpass'] = '';
$cfg['Servers'][$i]['auth_type']
は認証モードの設定でデフォルトではconfigとなっています。これをcookieと変更してください。これによって有効なMySQLユーザーをログイン画面で入力すればログインできるはずです。ついでに$cfg['Servers'][$i]['user'] = 'root';
はcookieでは不要なので空文字にしてください。config.inc.php(一部)$cfg['Servers'][$i]['auth_type'] = 'cookie'; $cfg['Servers'][$i]['user'] = ''; $cfg['Servers'][$i]['password'] = '';(HY000/2002): 対象のコンピューターによって拒否されたため、接続できませんでした。の対処
前述の設定をしたconfig.inc.phpを保存し、phpmyadminのページをリロードするとログイン画面が表示されるかと思います。
あらかじめ設定したrootユーザでログイン……、がっダメ!ここで小一時間詰まってしまいました。原因はおそらくhostの項目でローカルのipアドレス127.0.0.1になっているからかなと思いlocalhostに変更したりしましたが一向に解決せず。
そんな時下記の質問記事を発見しました。
xampp phpmyadmin access denied error(#2002)
どうやら、localhost:MySQLのポート番号(デフォルトでは3306?)
と記載しないといけなかったみたいです。つまり下記のように直すということですね。
※筆者のMySqlのポート番号は3306です。自分の環境に合わせたポート番号に変更してください。config.inc.php(一部)/* Bind to the localhost ipv4 address and tcp */ $cfg['Servers'][$i]['host'] = 'localhost:3306';config.inc.phpを保存し、IDとパスを入力してログイン…。成功!
所感
ポート番号込みで定義しないといけないというのは盲点でしたが当たり前ですね…。
とりあえずphpmyadmiにはアクセスできたので、この後は別途ユーザを作成し、
rootユーザのアクセスを制限しておく必要があるかと思います。
振り返れば簡単なことなのですがこれ調べるだけで半日終わっちゃって本来の勉強が進まない…。
- 投稿日:2020-03-29T18:08:44+09:00
PHP 郵便番号と住所を検索 正規表現
郵便番号か住所で検索
内容
郵便番号または都道府県を選択し市町村を入力すると
郵便のデータが検索できます。10件を超えた場合に”前へ”、”次へ”を押すと各々のページに飛ぶようになっています。
SQL脆弱性があると思いますが、今のところこのまま学習を進めていきます。
// 変数を設定 $post_num ='';//郵便番号 $area ='';//都道府県選択 $city ='';//市町村 $town ='';//県域 $error1 = [];//エラー文 $error2 = [];//エラー文 $user_data = []; $abc_data = []; $query = ''; $host = 'localhost'; $username = ''; $passwd = ''; $dbname = ''; $link= mysqli_connect($host, $username, $passwd, $dbname); $page = 1; $count = '';//トータル件数 $totalpage = ceil($count/ 10);//切り上げ変数が存在するかを確認
//どちらかに値が入っていた場合 if(isset($_GET['post_num']) === true || isset($_GET['area']) === true) { //変数を代入する if(isset($_GET['post_num']) === true) { $post_num = $_GET['post_num']; } if(isset($_GET['area']) === true) { $area = $_GET['area']; } if(isset($_GET['city']) === true) { $city = $_GET['city']; } if(isset($_GET['town']) === true) { $town = $_GET['town']; } if(isset($_GET['page']) === true) { $page = $_GET['page']; }if(isset($_GET['post_num']) === true || isset($_GET['area']) === true)このif文を書かないと空欄の状態でも変数の確認をしてしまうため、
後述のエラー文が出てしまいます空白の削除をします
$post_num = str_replace(array(" "," "),"", $post_num); $area = str_replace(array(" "," "),"", $area); $town = str_replace(array(" "," "),"", $town);str_replace(array(半角、全角空白)、空、変数)となっており
変数の中の半角、全角空白を空要素と入れ替えます。つまり、空白があった場合に削除します。エラーメッセージ
if(empty($post_num) === true && ($area === '都道府県を選択' || empty($city) === true)) { $error1[] = '郵便番号を入力してください'; }else if ((preg_match('/^[0-9]{7}$/', $post_num) !== 1) && ($area === '都道府県を選択' || empty($city) === true)) { $error1[] = '7桁の数字で入力してください'; }else { print ""; }areaとcityの入力がしていない場合でないと条件を付けないと
2つの値が入っているときにもエラー文が出てしまう。}else if ((preg_match('/^[0-9]{7}$/', $post_num) !== 1) && ($area === '都道府県を選択' || empty($city) === true)) { $error1[] = '7桁の数字で入力してください';preg_match(正規表現、文字列)で文字列が正規表現と一致しているかを
確認する
一致していれば1、していなければ0で、失敗するとfalseを返します。
正規表現は0から9までの値を7桁でとなっている。
なので、post_numが7桁の数字出なかった場合、かつareaまたはcityの
値が空だった場合にエラー文が出るようになる。if ($area === '都道府県を選択' && (empty($post_num) === true ||(preg_match('/^[0-9]{7}$/', $post_num) !== 1))) { $error2[] = '都道府県を選択してください'; } if(empty($city) === true && (empty($post_num) === true ||(preg_match('/^[0-9]{7}$/', $post_num) !== 1))){ $error2[] = '市区町村名をを入力してください'; }前述とほぼ同じで、post_numに値が入っている場合にはエラー分が出ないようになっています。
データベースに接続
if(count($error1) === 0 || count($error2) === 0) { if($link) { mysqli_set_charset($link, 'utf8'); if($post_num !== '') { $query = "SELECT post_num, area , city, town FROM pt_table WHERE post_num = '$post_num'";エラー文がないときに接続します。文字化けを防止でutf8で表記します。
post_numが空ではない場合にsql文を代入します。WHERE post_num = '$post_num'";データベースにある郵便番号の値とpost_numが同じ場合になるのが条件。
} else { $limit = 10*$page-10; $query = "SELECT post_num, area , city, town FROM pt_table WHERE (area = '$area') AND (city = '$city') LIMIT ".$limit.",10";pageは1番最初のページ数を表しています。 LIMITは開始位置、終わりの位置までを指定。
つまり、10件ごとにページに表示します。変数limitは0、10、20、30と増えていきます。
$query = "SELECT post_num, area , city, town FROM pt_table WHERE (area = '$area') AND (city = '$city') LIMIT ".$limit.",10";データベースのarea,cityの値がそれぞれ一致している場合という条件。
変数limitを”$limit”と書いた場合には文字列となってしまいます。カンマで囲うと数字として扱えます。総件数を出すために文字列を取得
$abc = "SELECT post_num, area , city, town FROM pt_table WHERE (area = '$area') AND (city = '$city')"; $result = mysqli_query($link, $abc); while($row = mysqli_fetch_array($result)) { $abc_data[] = $row; } }まず、変数queryでデータを取得しようとすると最大10件しか取得できません。理由はLIMITを使って10件ずつ取得するプログラムになっているからです。なので、別の実行データを
1行ずつ取得し、文字列にしていきます。実行したデータを取得
$result = mysqli_query($link, $query); while($row = mysqli_fetch_array($result)) { $user_data[] = $row; }10件ずつ取得するデータを1行ずつ取得し文字列にします。
メモリを開放しデータベースから閉じる
mysqli_free_result($result); mysqli_close($link); }else { echo 'DB接続失敗'; } } }総件数
$count = count($abc_data);ページング
<?php if ($page > 1) { ?> <a href="?page=<?php echo ($page - 1); ?>&area=<?php print $area; ?> &city=<?php print $city; ?>">前のページへ</a> <?php } ?> <?php if ($page < $totalpage) { ?> <a href="?page=<?php echo ($page + 1); ?>&area=<?php print $area; ?> &city=<?php print $city; ?>">次のページへ</a> <?php } ?>ファイル名?post_num=&area=県名&city=市名とURLに表示させる事を目的にする。
GETで値を取得したときにはURLが表示され、その先頭は?が付きます。
現在のページが1よりも大きいは(page-1)つまり前のページにいきます。トータルページが今のページよりも多い場合には次のページに行きます
全コード
<?php // 変数を設定 $post_num =''; $area =''; $city =''; $town =''; $error1 = []; $error2 = []; $user_data = []; $abc_data = []; $query = ''; $host = 'localhost'; $username = 'codecamp31549'; $passwd = 'RXFXXBTL'; $dbname = 'codecamp31549'; $link= mysqli_connect($host, $username, $passwd, $dbname); $page = 1; $count = '';//トータル件数 $totalpage = ceil($count/ 10);//切り上げ /*変数が存在する確認*/ if(isset($_GET['post_num']) === true || isset($_GET['area']) === true) { if(isset($_GET['post_num']) === true) { $post_num = $_GET['post_num']; } if(isset($_GET['area']) === true) { $area = $_GET['area']; } if(isset($_GET['city']) === true) { $city = $_GET['city']; } if(isset($_GET['town']) === true) { $town = $_GET['town']; } if(isset($_GET['page']) === true) { $page = $_GET['page']; } /*空白削除*/ $post_num = str_replace(array(" "," "),"", $post_num); $area = str_replace(array(" "," "),"", $area); $town = str_replace(array(" "," "),"", $town); /*エラーメッセージ*/ if(empty($post_num) === true && ($area === '都道府県を選択' || empty($city) === true)) { $error1[] = '郵便番号を入力してください'; }else if ((preg_match('/^[0-9]{7}$/', $post_num) !== 1) && ($area === '都道府県を選択' || empty($city) === true)) { $error1[] = '7桁の数字で入力してください'; }else { print ""; } if ($area === '都道府県を選択' && (empty($post_num) === true ||(preg_match('/^[0-9]{7}$/', $post_num) !== 1))) { $error2[] = '都道府県を選択してください'; } if(empty($city) === true && (empty($post_num) === true ||(preg_match('/^[0-9]{7}$/', $post_num) !== 1))){ $error2[] = '市区町村名をを入力してください'; } /*データベースに接続*/ if(count($error1) === 0 || count($error2) === 0) { if($link) { mysqli_set_charset($link, 'utf8'); if($post_num !== '') { $query = "SELECT post_num, area , city, town FROM pt_table WHERE post_num = '$post_num'"; // var_dump($query); } else { $limit = 10*$page-10; $query = "SELECT post_num, area , city, town FROM pt_table WHERE (area = '$area') AND (city = '$city') LIMIT ".$limit.",10"; $abc = "SELECT post_num, area , city, town FROM pt_table WHERE (area = '$area') AND (city = '$city')"; $result = mysqli_query($link, $abc); while($row = mysqli_fetch_array($result)) { $abc_data[] = $row; } } // var_dump($query); $result = mysqli_query($link, $query); while($row = mysqli_fetch_array($result)) { $user_data[] = $row; } mysqli_free_result($result); mysqli_close($link); }else { echo 'DB接続失敗'; } } } $count = count($abc_data); ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>郵便</title> </head> <body> <form action="./17-6.php" method="get"> <h1>郵便番号検索</h1> <h2>郵便番号から検索</h2> <?php print "総件数" . htmlspecialchars($count,ENT_QUOTES,'UTF-8') . "件";?> <input type="search" name="post_num" value=""> <input type="submit" value="検索"> <h2>地名から検索</h2> <label>都道府県を選択 <select name="area"> <option>都道府県を選択</option> <option>北海道</option> <option>兵庫県</option> <option>新潟県</option> </select> </label> <label>市区町村 <input type="seach" name="city"> <input type="submit" value="検索"> </label> </form> <p><?php foreach($error1 as $key1 => $string1) { print htmlspecialchars($string1,ENT_QUOTES,'UTF-8'); } ?></p> <p><?php foreach($error2 as $key2 => $string2) { print htmlspecialchars($string2,ENT_QUOTES,'UTF-8');; } ?></p> <?php foreach($user_data as $read) {?> <table> <style type="text/css"> table, td, th { border: solid black 1px; } table { width: 600px; } tr td { width: 150px; } </style> <tr> <th>郵便番号</th> <th>都道府県</th> <th>市町村</th> <th>町域</th> </tr> <tr> <td><?php print htmlspecialchars($read['post_num'],ENT_QUOTES,'UTF-8'); ?></td> <td><?php print htmlspecialchars($read['area'],ENT_QUOTES,'UTF-8'); ?></td> <td><?php print htmlspecialchars($read['city'],ENT_QUOTES,'UTF-8'); ?></td> <td><?php print htmlspecialchars($read['town'],ENT_QUOTES,'UTF-8'); ?></td> </tr> </table> <?php } ?> <p> <!--GETを使用するときは?からスタートする--> <?php if ($page > 1) : ?> <a href="?page=<?php echo ($page - 1); ?>&area=<?php print $area; ?> &city=<?php print $city; ?>">前のページへ</a> <?php endif; ?> <?php if ($page < $totalpage) : ?> <a href="?page=<?php echo ($page + 1); ?>&area=<?php print $area; ?> &city=<?php print $city; ?>">次のページへ</a> <?php endif; ?> </p> </body> </html> ```php
- 投稿日:2020-03-29T17:03:47+09:00
実行された全てのSQLクエリをログに出力するLaravelプラグイン
Laravelで新しいプロジェクトを作るたびに毎回似たようなコードを書いている気がしてきたので、プラグインにして公開しました。
https://github.com/ngmy/laravel-query-log-tracker
PHP 7.2〜7.4、Laravel 5.6以上、6.x、7.xで動作するはずです。
PHP 7.2〜7.4とLaravel 7.xでユニットテストしています。
PHP 7.3.16とLaravel 7.3.0で実際に動作確認しました。ログには次の項目が出力されます。
- バインド後のSQL
- バインド変数
- 実行時間(ミリ秒)
- コネクション名
インストール方法はREADMEに書いていますが、Composerでインストールして、(パッケージディスカバリーを使っていない場合は)サービスプロバイダとファサードを
config/app.php
に追加するだけです。実行時にログを無効化することもできます。
QueryLogTracker::beginDisable(); // ログを無効化したいSQL QueryLogTracker::endDisable();QueryLogTracker::disable(function () { // ログを無効化したいSQL });設定ファイルで次の項目を設定できます。
- ログレベル
- ログから除外するSQLのパターン(正規表現)
- ログスタック
- ログチャンネル
テストはしていますがOSSなので自己責任で使ってください。
本番環境で使う場合は無効化や除外パターンを使って、ログに個人情報や秘密情報が出力されないように注意してください。
- 投稿日:2020-03-29T16:59:50+09:00
PHP+αでTodoアプリを作る話①
前回
前回はApatchとNginxを使ってサーバーを構築する記事を書きました。そちらをまだご覧になっていない方はそちらもご覧ください。
はじめに
以前、筆者はPHPでウェブサイトを作成する記事を作成しました。後で振り返ってみると内容に多くの不備があることに気づきました。(書いていた当時もちょっと無茶かなとは薄々感じていましたが笑)
筆者自身がしっかりとPHPを使いこなせるようにしたいと思ったこと、また、特に何をすべきかわかりにくいPHPを使う初心者の方のお役に立ちたいというのが今回記事を書こうと思った理由です。
長話はさておきさっそく記事を執筆していきたいと思います。
参考文献
PHP公式ドキュメント
https://www.php.net/manual/ja/tutorial.firstpage.php使用環境
OS:Windows10(仮想環境)
エミュレータ:Virtualbox
その他使用ツール:Xampp、PHP※仮想環境等の説明は省略させていただいております。
環境構築の前の余談
まずは、PHPやその他ツールを使用するための環境構築をしたいと思います。その他ツールとはサーバーアプリであるApacheや、DBを扱う言語であるSQLを使用するためのSQLサーバーなどがこれに当たります。
フレームワークを使ってウェブサイトなどを作成していると、今回のようなフルスクラッチでウェブサイトを作成するときに困惑される方も多いと思います。もしくはどうすればよいのかわからなかった方も多いと思います。(筆者も初めはこうでした笑)
余談はさておき早速始めていきたいと思います。
Xamppのインストール
参考文献の本に付属しているCDからでも、サイトから持ってきたものからでもいいのでxampp-win32-7.0.4-0-VC14-installer.exeというファイルを起動しましょう。
サイトから持ってくる場合は下記のURLから飛んで、一番上のファイルをダウンロードしてください。
https://sourceforge.net/projects/xampp/files/XAMPP%20Windows/7.0.4/※起動すると下記の様なエラー画面が表示されると思いますが、そのままokでいいです。
起動したら何もせず進めて大丈夫です。
インストール中下記の様な表示が現れると思いますが、アクセスを許可して大丈夫です。
起動確認
環境構築の次は各種設定を行っていきたいと思います。
起動しようとする下記の様な画面が表示されると思います。アメリカの国旗にチェックをした状態でSaveをクリックしてください。
Apacheの設定
次にApacheの設定を行っていきたいと思います。
いろいろなボタンがありますが、まずはApacheのStartをクリックしてください。下記の様に、項目が緑色になったら設定完了です。
このままでもいいのですが、せっかくなのできちんと起動しているかWebブラウザ上でも確認したいと思います。お好みのWebブラウザでlocalhostと検索して下記の様な画面が表示されればApacheは起動できています。
SQLサーバーの設定
次にSQLの設定をしていきたいと思います。
先ほどの要領で今度はMySQLのStartをクリックしてください。その際、下記の様な画面が表示されると思いますが、アクセスを許可してください。
先ほどと同じく、MySQLの項目が緑色になったら起動完了です。
次に、XamppのコントロールパネルからShellというボタンをクリックしてください。すると下記の様な画面が表示されると思います。
下記コマンドを入力して管理者ユーザーのパスワードを設定してください。
qiita.rbmysqladmin.exe -u root password xxxxxxxxxxのところには任意のパスワードを入力してください。5文字以上でも大丈夫です。
入力して特にエラーが表示されなければ成功です。下記コマンドでシェルを閉じましょう。
qiita.rbexit
一度、MySQLからStopをクリックし、再びStartをクリックして再起動しましょう。
ログインできるか確認してみましょう。MySQLからAdminをクリックするとこのような画面が表示されると思います。
ユーザー名はrootで、パスワードは先程設定したパスワードを入力してください。すると下記の画面にログインすることができると思います。
これでMySQLの設定は完了です。
※このような画面が表示された場合、C:\xampp\phpMyAdmin\config.inc.phpというファイルを編集する必要があります。
このような個所を見つけたら、configの部分をcookieに書き換えてください。
作業フォルダの設定と諸々
最後はPHPを使ってウェブ上に何か表示させたいと思います。
まずは、作業フォルダを設定していきたいと思います。C:\xampp\htdocsの中に何でもいいので作業用のフォルダを作成してください。名前は何でもいいですが、筆者はTodoという名前で作成しました。
続いてその中にtest.phpというファイルを作成し、内容を下記の様に編集してください。
test.php<?php phpinfo() ?>Apacheを起動した状態で、localhost/todoとWebブラウザで検索すると下記の様な画面が表示されると思います。
この画面もいいですが、せっかくなら何か文字を表示させたいです。先ほどのフォルダの中にano_test.phpというファイルを作成し、下記の様に編集してください。
ano_test.php<html> <head> <title>PHP Test</title> </head> <body> <?php echo '<p>Hello World</p>'; ?> </body> </html>PHPの公式ドキュメントから拝借したコードです。下記の様になりましたでしょうか。
これで作業フォルダに関する諸々の設定は完了です。
おわりに
今回はPHPを使用するための環境構築を行ってまいりました。フレームワークを使っていた時は今回のように色々設定することはなかったので、初めての時にはずいぶん苦労したものです。次回からは本格的にPHPの記述を行っていきたいと思います。
- 投稿日:2020-03-29T16:50:12+09:00
PHP学習記 #1日目
- 投稿日:2020-03-29T16:26:45+09:00
【初心者向け】PHPマスター講座 vol.2(変数を学ぼう)
みなさんこんにちは!けいです
今回は「変数」について解説します!!
【PHPマスター講座】変数とは
===========================
▶︎変数とは何か??
まず変数とは...
データを一時的に保存する【箱みたいなもの】です。・変数はまず宣言を行い
・宣言した変数に対して値の代入する基本的にはこの流れで進めていきます!
===========================
▶︎変数を実践で学ぼう?(基礎編)
ただ…
変数の説明だけだと
正直イメージがしにくいです。なので
実際に「変数を使った計算式」を見てみましょう!
【計算式】
<?php $total = 100+200+300; ?>
合計金額は:<?php print($total); ?>円になります。
このように変数名($total)を使うと…
【表示結果】
合計金額は:600円になります。上記のように結果を出力することができます。
===========================
?ポイント?
・変数名はPHPだと必ず「$」をつける
▶︎今回だと【$total】が変数名となります。・変数として使えるもの
▶︎英単語、英語とアンダースコア「_」、日本語でつけることが可能。
「$value」,「$value_kei」, 「$けい」(最初だけ大文字・小文字・全部大文字は可能)
(一般的に変数は全て「小文字」で書く)・変数として使えないもの
▶︎記号・空白が含まれている・先頭を数字で始める、は使用不可
「$"#」,「$my name」,「$123kkk」・変数は最初に「代入」という操作を行う
▶︎変数名を書いて「=」を書いて「記憶しておきたい情報」を書く。実際に変数を使用することで
▶︎記入漏れ
▶︎書き間違い
▶︎同じ言葉や計算式を何度も書く
上記を【未然に防ぐこと】ができます!
===========================
▶︎変数を実践で学ぼう?(応用編)
たとえば
あなたが「3つの商品」の
▶︎合計金額
▶︎税込金額
▶︎値引金額
▶︎セール後の金額
上記内容の情報を確認したい場合
PHPで変数をどのように使いますか?3分だけ、じっくり考えてみてください!!
(下に参考例を書いていますが、まず考えてみましょう!)
…
……
………
さて、どうでしょうか?
わたしだったら
下記のように「PHPの変数」を使います。
▶︎前提条件(変数の設定)
<?php $total = 100+200+300; ?>
//3つの商品の合計金額を出力する変数名($total)を定義
<?php $tax = 1.08; ?>
//消費税の変数名($tax)を定義
<?php $discount = 0.2?>
//値引金額の変数名($discount)を定義
<?php $final_price = 0.8 ?>
//セール後最終金額の変数名($final_price)を定義
▶︎文字列と計算式
合計金額は:<?php print($total); ?>円になります。税込金額は:<?php print($total*$tax); ?>円になります。
値引金額(税込)は:<?php print($total*$tax*$discount); ?>円になります。
セール後最終金額(税込)は:<?php print($total*$tax*$final_price); ?>円になります。
▶︎【表示結果】
合計金額は:600円になります。
税込金額は:648円になります。
値引金額(税込)は:129.6円になります。
セール後最終金額(税込)は:518.4円になります。※ただし、太字にした箇所が「小数点」になっていて変なので「小数点の対応」が必要になります。
?ポイント?
「小数点の対応方法」
▶︎小数点切り捨て:「floor」
▶︎小数点切り上げ:「ceil」
▶︎小数点四捨五入:「round」
▶︎文字列と計算式(小数点の対応)
値引金額(税込)は:<?php print ceil($total*$tax*$discount); ?>円になります。
セール後最終金額(税込)は:<?php print ceil($total*$tax*$final_price); ?>円になります。
▶︎【表示結果】
値引金額(税込)は:130円になります。
セール後最終金額(税込)は:519円になります。
===========================
さて、今回のPHPマスター講座 vol2(変数を学ぼう)はここまでとなります!
少しでもあなたのPHP学習のお役に立てれば幸いです?
- 投稿日:2020-03-29T15:35:39+09:00
PHPのArrayAccessインタフェースをざっくり解説
初心者向け記事。
PHPには、それを実装することでクラスに言語構造レベルの機能を持たせることができるインタフェースがいくつかある。Laravelなどのフレームワークの中でも使われるケースがあり、ソースコードを追ったりする際は知っておく必要が出てくる。今回はArrayAccessインタフェースを例にとって、どのように使われるのかをみていく。
(間違いなどあればご指摘ください...)
ArrayAccessインタフェース
PHPのオブジェクトは、
ArrayAccess
インタフェースを実装することで配列のように操作することができるようになる。ArrayAccess { /* メソッド */ abstract public offsetExists ( mixed $offset ) : bool abstract public offsetGet ( mixed $offset ) : mixed abstract public offsetSet ( mixed $offset , mixed $value ) : void abstract public offsetUnset ( mixed $offset ) : void }引数の
$offset
は配列のキー、$value
は格納する値に相当する。
それぞれのメソッドの役割は以下のようになる。
offsetExists()
は指定したキーに値がセットされているかをbool値で返す。isset()
による判定を可能にする。offsetGet()
はキーを元に値を取得する。offsetSet()
は指定したキーと値をペアで格納する。offsetUnset()
は指定したキーとそのペアの値を削除。unset()
による割り当ての解除を可能にする。引数にはmixedが指定されており、様々な型を受け入れてくれることがわかる。
Illuminate\Container\Container
クラスの例例えばLaravelのサービスコンテナ(
Illuminate\Foundation\Application
クラス)が継承しているIlluminate\Container\Container
クラスはArrayAccess
インタフェースを継承している。
以下は該当のテストコードを一部変更して引用したものになる。ContainerTest.phpuse PHPUnit\Framework\TestCase; class ContainerTest extends TestCase { public function testArrayAccess() { $container = new Container; $container['something'] = 'foo'; // offsetSet() $this->assertTrue(isset($container['something'])); // offsetExists() $this->assertSame('foo', $container['something']); // offsetGet() unset($container['something']); // offsetUnset() $this->assertFalse(isset($container['something'])); } }コードを見るとわかる通り、実装するContainerクラスは当然オブジェクトだが、配列と同じように操作していることがわかる。 ArrayAccessインタフェースを実装することで、このテストがパスすることを確認する。
実装
Container.phpclass Container implements ArrayAccess { private $container = []; public function offsetExists($key): bool { return isset($this->container[$key]); } public function offsetGet($key) { return $this->container[$key]; } public function offsetSet($key, $value): void { $this->container[$key] = $value; } public function offsetUnset($key): void { unset($this->container[$key]); } }本来の
Illuminate\Container\Container
クラスでは結合処理などを行なっているのでもう少し混み入っているが、ArrayAccess
インタフェースを使用してオブジェクトに配列操作を持たせるだけならこんな感じでも十分。
Container
クラスのインスタンスは自身が保持する$container
プロパティに実際に与えられるキーと値を格納する(実際のサービスコンテナの場合、ここにキーと解決処理などをバインドする)。それぞれ4つのメソッドの中では実際に配列をもつプロパティ
$container
にアクセスして必要な操作を行うことになるため、isset
やunset
などをその中で使用することが普通。
offsetExists
メソッドはisset
を使用し、$container
プロパティに指定されたキーをもつ値が格納されているかをboolで返す。offsetUnset
メソッドも同様にunset
を使用してキーとその値の割り当てを解除。offsetGet
メソッドとoffsetSet
メソッドは$container
プロパティに対し、指定されたキーでアクセスし、値を取得するか、新規に値を登録する。これを実装したら、テストが通ることを確認する。
$ ./vendor/bin/phpunit tests/ContainerTest.php PHPUnit 9.0.1 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 141 ms, Memory: 4.00 MB OK (1 test, 3 assertions)Laravelでは他にも
Eloquent\Model
クラスやHttp\Request
クラスなどでArrayAccess
インタフェースを実装してたりする。Container
はまだしもこの辺は正直不要だと思うが、それでも実装されてしまっているので読む際には知らないと??となりがちなので、基本的なPHPの知識として押さえておきましょう。。
- 投稿日:2020-03-29T11:10:51+09:00
【Laravel】アロー演算子の読み解き方
はじめに
最近PHPやLaravelを学習し始めた僕ですが、オブジェクトのメンバ(メソッド、プロパティ)を取得する役割のアロー演算子について触れる機会が多くあり、読み解くのに苦労したので自分なりにまとめたいと思います。
初学者が故に間違っている点ありましたらご指摘いただけると幸いです。アロー演算子てなんぞや
->
これです。
右辺に使いたいメソッドやプロパティ。左辺にそれらが置いてあるクラスを記述します。具体的な使い方としては
class PostController extends Controller { private $post; public function __construct(Post $instanceClass) { $this->post = $instanceClass; } public function index () { $post = $this->post->all(); } }
$this->post
PostControllerで定義した$postを呼んでる。呼び出す時$は取る。
post->all()
コンストラクタでPostモデルを$postに代入しているので、モデルクラスの返り値であるCollectionインスタンスのallメソッドを実行できる。
モデルクラスの返り値がなぜcollectionインスタンスかについては下記参照してください。
Laravel 5.8 コレクション読み解くコツは返り値を把握すること
個人的にはそのメソッドがどういう処理を行って、どんな値を返すのかを一つ一つ読み解くのがポイントだと思います。
返り値を把握する方法は、みなさん大好きなdd()
でコツコツとデバッグしていくのがベストです。最後に
railsを学習してた僕からしたらアロー演算子の表記が違和感ありすぎて理解に苦しみました。
単純にオブジェクトのメソッドやプロパティを呼び出すために使用しているものだと考えれば、さくさく読めるようになると思います。
ひとつひとつのアロー演算子で区切られた処理を地道にddしていき、メソッドの返り値を理解することが、初学者にとっては最善の策だと思います。