- 投稿日:2019-01-27T01:02:52+09:00
PHPで最低限覚えておきたい基礎文法
はじめに
何番煎じだよ的な感じはかなりありますが...
久しぶりにPHPの基礎文法についておさらいしたので、その内容を簡単にまとめた記事です。これからPHPを始める人の参考になれば嬉しいです。(もし間違っている点あれば、ご指摘いただけると幸いです)
基本編
PHPでプログラムを動かす為の基本です。サクッと覚えちゃいましょう。
Hello World
<?php echo "Hello World"; ?>まずは、おなじみのこれ。
PHPは、<?php
と?>
の間に書かれたものが、プログラムとして実行されます。プログラムの終わりには;
を付けます。コメント
#1行コメント //1行コメント /*複数行コメント 複数行コメント */全部で3種類あります。
/*複数行コメント*/
を使う場合、同じ形でコメントアウトをネストするとエラーになるので注意です。文字列の出力
echo "こんにちは"; //こんにちは print "こんにちは"; //こんにちは
echo
もしくは
この2つの違いは、次の3つです。基本はecho
で問題ないかと思います。1.
echo
はカンマ「,」区切りで複数の文字列を指定できるのに対し、echo "Hello", "World"; //この構文はエラーが出ません print "Hello", "World"; //この構文はエラーが出ます2.
echo
は返さない$str = echo "Hello World"; //この構文はエラーが出ます $str = print "Hello World"; //この構文はエラーが出ません※結果を返さない分、わずかにecho()の方が早いようです。
文字列
echo "文字列"; echo `文字列`;文字列は
"ダブルクォーテーション"
か'シングルクォーテーション'
で囲むことで、扱うことができます。
この2つの違いは、変数が展開できるか否かです。変数については、この後で触れてます。$str = "太郎"; echo "私の名前は、$str"; //私の名前は、太郎 $str = "太郎"; echo `私の名前は、$str`; //私の名前は、$strヒアドキュメント
echo <<<EOD ヒアドキュメントを使えば、長い文章を簡単に出力することができます。 連結文字やダブルクオーテーション使わずに済みます。 EOD; /* ヒアドキュメントを使えば、長い文章を簡単に出力することができます。 連結文字やダブルクオーテーション使わずに済みます。 */ヒアドキュメントは、
<<<開始ID 文字列 終端ID;
で使うことができます。IDでよく使われるのが、「EOM」「EOF」「EOD」ですが、「ABC」でも何でも構いません。
ちなみに、「EOM」は End Of Message、「EOF」は End Of File、「EOD」は End Of Document の略です。改行
<html> <head> <title>PHPの基礎知識をまとめてみた</title> </head> <body> <?php echo "1行目:改行して出力<br />"; echo "2行目:改行後の文字列"; ?> </body> </html> <?php echo "1行目\n"; echo "2行目"; ?>ブラウザ上での改行は、
<br />
とHTMLタグ
を入れます。ソース上での改行は、\n
と特殊文字
を入れます。文字列のエスケープ
非文字列エスケープの例echo "<a href="http://php.net/manual/ja/index.php">PHPマニュアル</a>"; //Parse error: parse error, expecting `','' or `';'' inこれたど、
echo "文字列"
の括りの中にさらに"
が入っている為、エラーが出ます。
これを回避するには、エスケープ文字\
を使います。エスケープ文字は重複する"
の前に付けます。文字列エスケープの例echo "<a href=\"http://php.net/manual/ja/index.php\">PHPマニュアル</a>"; //PHPマニュアル変数編
変数というのは値を保持しておく箱だと思って下さい。
値を保持しておいて後から計算に使ったり、取り出して表示したりすることができます。変数
$num = 1; echo $num; //1 $str = "Hello World"; echo $str; //Hello World変数は、
$変数名 = 値
で宣言します。ポイントは、変数の前に$
を付けること。
ちなみにPHPでは型の定義を勝手にしてくれます。つまり、Javaみたいに型の定義を宣言する必要が無いです。ただし、変数には命名規則があるので気を付けましょう。
文字列と変数の結合
$str = "文字列"; echo `私の名前は、`.$str; //私の名前は、太郎文字列結合演算子
.
を使うことで、文字列と変数を結合できます。変数の参照渡し
非参照渡しの例$a = 1; $b = $a; $a = 2; echo $b; //1PHPが
$a
と$b
を違う変数として扱っているため、$a
の値を変更しても$b
の値は元の「1」を保持したままです。
これを参照渡しを使うことで、$a
と$b
を「違う名前を持つ同じ変数」として扱えるようにできます。
参照渡しを使うにはアンパサンド&
を使います。&
を代入する方の変数の$
前に付けます。参照渡しの例$a = 1; $b = &$a; //アンパサンドを使う $a = 2; echo $b; //2変数のスコープ
※関数を理解していないと躓くと思うので、とりあえずこういうのがあるんだ程度で覚えといて下さい。
function outputInfo($name){ $pet = "猫"; //ローカルスコープ echo $name."さんは".$pet."を飼っています” } outputInfo("山田"); echo $pet."は可愛いですね";出力結果//山田さんは猫を飼っています //undefined~ は可愛いですね変数の使える範囲を
スコープ
と言います。
P関数内で作成した変数は、ローカルスコープと呼ばれ、その関数内でしか参照できません。ローカル変数をグローバルスコープでも使えるようにするためには、グローバル宣言をする必要があります。
グローバル宣言はglobal 変数名;
で宣言することができます。function outputInfo($name){ global $pet; $pet = "猫"; echo $name."さんは".$pet."を飼っています” } outputInfo("山田"); echo $pet."は可愛いですね";出力結果// 山田さんは猫を飼っています // 猫は可愛いですね定数編
定数というのは、変数に対して変更してはいけない値を保持しておくものです。消費税とか円周率とか。
定数
define(`PI`, 3.14); echo PI; //3.14定数は、
define
で宣言します。変数と違って$
を付ける必要はありません。
慣習的に、定数は常に大文字で表記されます。ちなみに、定数のスコープはグローバルなので、PHPスクリプト中どこでも定数にアクセスできます。
演算子編
演算子というのは、プログラミングで計算や判定をしたりするのに使われる記号のことです。
算術演算子
$a + $b // 加算 $a - $b // 減算 $a * $b // 乗算 $a / $b // 除算 $a % $b // $a を $b で割った余り小学校で習った算数と同じですね。数値を足したり引いたりするための演算子です。
加算子(インクリメント)/減算子(デクリメント)
$a++ // $a の値をひとつ加算する(変数を返し、変数に+1を加えます。) $a-- // $a の値をひとつ減算する(変数を返し、変数から-1を引きます。) ++$a // $a の値をひとつ加算する(変数に+1を加え、変数を返します) --$a // $a の値をひとつ減算する(変数から-1を引き、変数を返します)数の値を「+1」「-1」にする演算子です。インクリメント演算子とデクリメント演算子と言います。
インクリメント/デクリメント演算子を変数の前/後に付けた場合で挙動が異なるので、注意しましょう。文字列演算子
$a . $b // 文字列 $a と文字列 $b を連結文字列を結合する演算子です。連結したい文字列や変数の間にドット「.」を挟みます。
代入演算子
$a = $b // 代入 $a += $b // $a = $a + $b に同じ $a -= $b // $a = $a - $b に同じ $a *= $b // $a = $a * $b に同じ $a /= $b // $a = $a / $b に同じ $a %= $b // $a = $a % $b に同じ $a &= $b // $a = $a & $b に同じ $a |= $b // $a = $a | $b に同じ $a ^= $b // $a = $a ^ $b に同じ $a <<= $b // $a = $a << $b に同じ $a >>= $b // $a = $a >> $b に同じ $a .= $b // $a = $a . $b に同じ変数に値を代入する演算子です。
比較演算子
$a == $b // $a と $b が等しい $a === $b // $a と $b が等しい(型を厳密に比較する) $a != $b // $a と $b が等しくない $a <> $b // $a と $b が等しくない $a !== $b // $a と $b が等しくない(型を厳密に比較する) $a < $b // $a が $b よりも小さい $a > $b // $a が $b よりも大きい $a <= $b // $a が $b 以下である $a >= $b // $a が $b 以上である後で出てきますが、if、for、while の条件分岐に利用する演算子です。
論理演算子
$a and $b // $a かつ $b が TRUE であれば $a && $b // $a かつ $b が TRUE であれば $a or $b // $a または $b が TRUE であれば $a || $b // $a または $b が TRUE であれば !$a // $a が FALSE であれば $a xor $b // $a まはた $b どちらか片方のみが TRUE であれば論理演算を行うための演算子です。
配列編
配列というのは、複数の値を入れられる箱だと思って下さい。
変数は1度に1つの値しか保持できませんが、配列を使えば1つの変数に複数の異なる値を保持する事ができます。配列
$fruit = array["りんご","いちご","ぶどう”]; echo $fruit[0]; //りんご echo $fruit[1]; //いちご echo $fruit[2]; //ぶどう変数では1つの変数に1つの値しか保持(代入)する事ができませんが、配列を使えば1つの変数に複数の値を保持する事ができます。
配列は、array[値1,値2,...]
で使うことが出来ます。[]の中は、0,1,2...の順番で割り振られています。連想配列
$fruit = array[ "apple" => "りんご", "strawberry" => "いちご", "grape" => "ぶどう", ]; echo $fruit["apple"]; //りんご echo $fruit["strawberry"]; //いちご echo $fruit["grape"]; //ぶどう配列では、[ ]の中は、0,1,2...の数字の順番でしたが、数字ではなく文字のキーをもとにして値を設定した配列を連想配列と呼びます。
連想配列は、配列名 = array[キー1 => 値1, キー2 => 値2, ... ];
で使うことができます。制御構造編(Comming soon...)
- 投稿日:2019-01-26T22:14:53+09:00
超初歩:PHP(とか)書くなら知っておいてほしい事2-ヒアドキュメントの使い方を変数展開を交えて説明
ヒアドキュメントを使わないからコードがゴミクズのようになるんだよ
ほんと、これに尽きる。
PHPは元々HTMLのテンプレートエンジンとして開発されたので、
<?php $now = date("Y-m-d H:i:s"); ?> <!DOCTYPE html> <html> <head> <title>TODO supply a title</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <div>現在時刻は<?php echo $now; ?></div> </body> </html>こんな感じで、HTML内に<?php ?>タグでPHPコードを埋め込むことが出来るようになっている。
一方、ヒアドキュメント とは何かと言うと、複数行に渡る文字列を纏めて変数なり標準出力なりに渡す機能。ヒアドキュメント自体はPHPの構文になる。
<?php $now = date("Y-m-d H:i:s"); echo <<< EOL <!DOCTYPE html> <html> <head> <title>TODO supply a title</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <div>現在時刻は{$now}</div> </body> </html> EOL;こんな感じ。<<< XXX(XXXは任意の文字)から XXX; までに書かれてある文字列が変数や標準出力に渡される。慣例として使われるEOLは多分End Of Lineの略だと思うし、PHPマニュアル では、EOT(多分End Of Text)やEOD(多分End Of Data)等、いろんなIDが使われている。
ヒアドキュメント内では変数展開も出来る。注意点としては、閉じの XXX; の前には如何なる文字もあってはダメ。つまり、インデントすると閉じとはみなされなくなります。(変数展開も知っておいて欲しい。超初歩:PHP(とか)書くなら知っておいてほしい事1-ストリングリテラルの書き方を変数展開を交えて説明)
ヒアドキュメントを使うと何が便利なのか? なんだけど、例えばメールを送る時などに超便利。メール送信について結構見かけるのはこんな感じでテンプレートを用意してシンボルの置き換えを行う方法だけど、
contact_mail.txt{{title}}へのお問い合わせありがとうございます。 以下の内容でお問い合わせを受け付けました。 ■お名前:{{name}} ■お名前ふりがな:{{hurigana}} ■貴社名:{{company}} ■E-Mail:{{mail}} ■電話番号:{{tel}} ■URL:{{web_site}} ■お問い合わせ内容: {{message}} ----- 通常、2営業日以内に担当者より折返しご連絡を差し上げておりますが、 3日を経過しても連絡がない場合は恐れ入りますが 会社代表電話までお問い合わせください。 よろしくお願いいたします。 ================================ ashworth株式会社 TEL: 026-000-0000(代表) ================================<?php $mail_body = file_get_contents("/var/www/data/mail_template/contact_mail.txt"); $mail_body = str_replace("{{title}}", $_POST["title"], $mail_body); $mail_body = str_replace("{{name}}", $_POST["name"], $mail_body); $mail_body = str_replace("{{hurigana}}", $_POST["hurigana"], $mail_body); $mail_body = str_replace("{{company}}", $_POST["company"], $mail_body); $mail_body = str_replace("{{mail}}", $_POST["mail"], $mail_body); : mb_send_mail($to, $subject, $mail_body, $header);もう、見るからにスマートじゃない上、コード量も多く、抽象化したとしてもかなりメンテナンス性は悪い。一番問題なのがPHPコードとテンプレートが離れている事。
「メールの文面を変えたい」という要望が出た時、自分の書いたコードならまだ勝手がわかるからいいけど、他人の書いたコードでこれをやられると、そもそも『テンプレートどこだよ!?』ってなってテンプレートファイルに辿り着くまでが大変だし、大体の場合、送られてきたメールが提示されて『この部分を変えたい』という依頼が来るんだけど、PHPコード上のどの変数とどのシンボルが対応しているのかコードとテンプレートとにらめっこしなければ分からないという非効率極まりない事になる。特に注文の確認メールとかだと置き換えされる変数が50とか100とかある場合も普通にあって、ウォーリーを探せ!みたいになってくる。
こういうときに、ヒアドキュメントが強力な威力を発揮してくれる。
$mail_body = <<< EOL {$_POST["title"]}へのお問い合わせありがとうございます。 以下の内容でお問い合わせを受け付けました。 ■お名前:{$_POST["name"]} ■お名前ふりがな:{$_POST["hurigana"]} ■貴社名:{$_POST["company"]} ■E-Mail:{$_POST["mail"]} ■電話番号:{$_POST["tel"]} ■URL:{$_POST["web_site"]} ■お問い合わせ内容: {$_POST["message"]} ----- 通常、2営業日以内に担当者より折返しご連絡を差し上げておりますが、 3日を経過しても連絡がない場合は恐れ入りますが 会社代表電話までお問い合わせください。 よろしくお願いいたします。 ================================ ashworth株式会社 TEL: 026-000-0000(代表) ================================ EOL; mb_send_mail($to, $subject, $mail_body, $header); // $to、$subject等は割愛こんな感じでソースコードが非常にシンプルになる。
メールテンプレートを作ってシンボルを置き換えるのは、ヒアドキュメントの無かった頃の人がやり始めたんだと思うし、CMSみたいので当たり前になっているからそうするのが正しいと思っている人は多いと思うんだけど、今どき自前でシステム作るならそんなやり方はあり得ないよなぁと思う。
だいたい、メールテンプレートに切り分けてあれば非エンジニアも編集できて分業できるでしょ? みたいな事言う人いるけど、非エンジニアの人がメールの文面を編集する事なんか、絶対無いです。
もし、どうしてもこれをテンプレート的に使いたくて、複数の処理から同じメール文面が呼ばれるというのならこんな感じで抽象化すればいい。これならIDE等を使ってコードジャンプでたどり着けるし、メールの文面と埋め込まれる変数の位置関係が瞭然と把握できる。
MyMail.phpclass MyMail { public static function getContactMailBody($params) { return <<< EOL {$params["title"]}へのお問い合わせありがとうございます。 以下の内容でお問い合わせを受け付けました。 ■お名前:{$params["name"]} ■お名前ふりがな:{$params["hurigana"]} ■貴社名:{$params["company"]} ■E-Mail:{$params["mail"]} ■電話番号:{$params["tel"]} ■URL:{$params["web_site"]} ■お問い合わせ内容: {$params["message"]} ----- 通常、2営業日以内に担当者より折返しご連絡を差し上げておりますが、 3日を経過しても連絡がない場合は恐れ入りますが 会社代表電話までお問い合わせください。 よろしくお願いいたします。 ================================ ashworth株式会社 TEL: 026-000-0000(代表) ================================ EOL; } }require("MyMail.php"); $mail_body = MyMail::getContactMailBody($_POST); mb_send_mail($to, $subject, $mail_body, $header); // $to、$subject等は割愛SQLとかでもそうなんだけど、今まさに改修を行っているコードのその場所に変更したい対象が無いと非常に辛い。Java界隈ではDAOという、どこのバカが考え出したのか知性を疑うような無駄な抽象化が頻繁に行われている。「SQL文は全部一箇所に纏まっていた方が綺麗でっしゃろ?」みたいな事を考えたんだろうけど、おまえ、自分でメンテナンスしてみろ。システムが巨大になればなるほど破綻するから。
で、そのSQL文とかも、ヒアドキュメントを使うととても見やすくなる。
まず、使わない例。
$qry = "SELECT * FROM table "; $qry .= "WHERE id='{$id}' "; $qry .= " AND name='{$name}' "; $qry .= "ORDER BY last_access DESC;"; $result = $mysqli->query($qry);使った例。
$qry = <<< EOL SELECT * FROM table WHERE id='{$id}' AND name='{$name}' ORDER BY last_access DESC; EOL; $result = $mysqli->query($qry);ね?
補足
あと、ヒアドキュメントは変数展開させない事も勿論出来る。例えば半角の¥とか$とかをそのまま表示したいかもしれない。
そういう時はこれ。
echo <<< 'EOL' 只今のレートは $1=109.537円です。\n EOL;シングルクオートで囲む。こうすると、$とか\がそのまま出力されます。
で、これ、ヒアドキュメントとは呼ばず、Nowdoc と呼ぶそうです。
呼び方分ける意味、ある?
まとめ
変数展開とヒアドキュメントあたりは知っているのと知らないのとではコードの書きっぷりにものすごい差が生まれてきてメンテナンス性に直結するのに、知らない人が多過ぎる。本当に「これ、クソコードだなぁ…」と思うコードは確実に、変数展開もヒアドキュメントも使っていない。使っていないからコードがゴミクズになっている。大体ゴミクズになる理由の2~3割くらいがこの辺り起因だったりする。
結局、ネットに出回っているコードが文字列連結しまくりなので、初学者はそれが当たり前だと思っちゃうんだろうなぁ…。
- 投稿日:2019-01-26T17:54:30+09:00
超初歩:PHP(とか)書くなら知っておいてほしい事1-ストリングリテラルの書き方を変数展開を交えて説明
ストリングリテラルにシングルクオート使う人は頭が悪い
ネットを見ていると、'(シングルクオート)で囲っている人がやたら多いんだけど、何考えているのかよくわからない。PHPは、というか他の言語も結構そうだったりすんだけど、ダブルクオートとシングルクオートの役割は似ているようでいて少し違っていて、通常、ダブルクオートで囲ったストリングリテラル内では変数展開が出来る。シングルクオート内では出来ない。正確に言うとしない。
変数展開ってなんぞ?と思う人もいるかと思うけど、その名の通り、変数に入っている値をその場所に埋め込む事。
例えば、
<?php $value = "Qiita"; echo "$value の記事です。" . PHP_EOL; echo '$value の記事です。' . PHP_EOL;みたいにすると、
[****@**** my]$ php test1.php Qiita の記事です。 $value の記事です。となる。シングルクオートは変数展開されない。改行コードを示す \n とかもそのままの文字として出力されてしまう。
PHPの場合、ストリング連結は .(ドット)なので何も考えていない人は
<?php $value = 'Qiita'; echo '本日は'.$value.'の記事を書きました。';みたいにするんだけど、変数展開を使えば、
<?php $value = "Qiita"; echo "本日は{$value}の記事を書きました。";と、書ける。変数を { } で囲むとそれが強制的に展開される。上記の場合、囲わないと $valueの記事を書きました。 という変数と認識されて、
PHP Notice: Undefined variable: valueの記事を書きました。
「そんな変数定義されていません」という意味のNoticeが発生するため、$valueの後に半角スペース等が必要になってくるが、{ } で囲めばそういった制約が無くなる。
上の2つを見比べれば一目瞭然だけど、どこまでがストリングリテラルなのかの分かりやすささが段違い。ドットで連結した方はブチブチ切れていて、ここにエスケープしたシングルクオートが入ってくると、本当にコードがゴミクズみたいになってくる。もっというと、関数呼び出しの引数でこういうのを渡すと更にカオスになる。(※敢えてコードのカラーマーキングをしないで例示します)
$sql = 'SELECT * FROM table WHERE test=\''.$col1.'\' AND hoge=\''.$col2.'\';'; some_method('/foo/bar/'.$dir.'/'.$filename.'.log',$message.'final',FILE_APPEND);もう、どこまでがリテラルでどこで引数が区切られているのかわけわからない。ダブルクオートで変数展開を使えばこう。(普通はカンマの後に半角スペース入れるけど、条件を同じにするため今回は省いた)
$sql = "SELECT * FROM table WHERE test='{$col1}' AND hoge='{$col2}';"; some_method("/foo/bar/{$dir}/{$filename}.log","{$message}final",FILE_APPEND);一目瞭然。分かりやすい。
こんなもん、敢えて言うまでもないとずっと思っていたんだけど、ほんとうにシングルクオート使う人がやたら多い。多分、何にも考えていないんだと思う。
大体、シングルクオートを好んで使っている人のコードはもう、どれもこれもメッチャクチャのメンテナンス性の低そうなコードで、いつも『この人、仕事できなそうだなぁ…』と思いながら見ている。
変数展開の更に高度な使い方
また、PHPでは変数の値を使って変数が定義できるため、例えば、
<?php $hoge = "test"; $test = "Qiita始めました。"; echo ${$hoge};てな感じにすると、
[****@**** my]$ php test3.php Qiita始めました。という感じになる。この、変数展開による変数定義はわりと高度で、最初の頃は『何でそんな事する必要があるの?』と思う人も多いかもしれない。
これを使うと便利な例の一つがPDOを使う場合。PDOはPHPでデータベースにアクセスするためのクラスで、DBアクセスが抽象化されていてDB毎の差異はライブラリ側が吸収してくれるため、MySQLでもPostgreSQLでもSQLiteでもOracleでもほぼ同じコードでアクセスできる優れもの。
で、PHPマニュアル|PDOStatement::bindParamの例1
<?php /* バインドされた PHP 変数によってプリペアドステートメントを実行する */ $calories = 150; $colour = 'red'; $sth = $dbh->prepare('SELECT name, colour, calories FROM fruit WHERE calories < :calories AND colour = :colour'); $sth->bindParam(':calories', $calories, PDO::PARAM_INT); $sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12); $sth->execute();のようなコードについて、bindParam()してクエリを実行するまでの処理全体を抽象化出来る。
具体的に書くとこんな感じ。
<?php class MyPDO { private static function getParamType($val) // 主旨ではないため仮の実装 { if(gettype($val) === "integer") { return PDO::PARAM_INT; } if(gettype($val) === "boolean") { return PDO::PARAM_BOOL; } if(gettype($val) === "NULL") { return PDO::PARAM_NULL; } return PDO::PARAM_STR; } public static function execute($dbh, $qry, $binds) { $sth = $dbh->prepare($qry); foreach($binds as $key => $val) { ${$key} = $val; $sth->bindParam(":{$key}", ${$key}, self::getParamType($val)); } return $sth->execute(); } } $qry = "SELECT name, colour, calories FROM fruit " $qry .= "WHERE calories < :calories AND colour = :colour'"; $binds = array("calories" => 150, "colour" => "red"); $result = MyPDO::execute($dbh, $qry, $binds); // $dbhについてはPHPマニュアルのサンプルコードにないため割愛初心者の人は更に何やってるかわからないだろうけど、上のコードの主旨はここ。
foreach($binds as $key => $val) { ${$key} = $val; $sth->bindParam(":{$key}", ${$key}, self::getType($val)); }PHPマニュアルのコードだと、
$calories = 150; $colour = 'red'; $sth->bindParam(':calories', $calories, PDO::PARAM_INT); $sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);こんな感じでバインド処理をパラメタの数だけ列挙しているけど、これ、2つだからまだ良い。これが10個とか20個とかになってくると、結構辛くなってくるのは分かると思う。
だって、パラメタ名が決め撃ちだから、発行するSQL毎にこんなのが山のように作られることになる。システムがでかくなればでかくなるほど決め打ちのコードが膨大に埋め込まれてメンテナンスできなくなっていく。「仕様変更でテーブルのカラム名が変わりマース」とか言われると、それだけでパニックになる。
MyPDOクラスのように抽象化してあればどこからでもこのクラスを共通で利用できて、あとは流し込むSQL文とバインドしたいパラメタを連想配列で渡すだけ、という、超メンテナンス性の高いコーディングができる。たったこれだけのことで、システム全体のコードを劇的に減らすことができる。
なお、完全な余談だけど、PDOを使っているサンプルコードは大体こんな感じでbindする行と変数を定義する行がわかれていて、なんでこんなめんどくさい事しているのか分かっていない人は多いと思う。
$calories = 150; $sth->bindParam(':calories', $calories, PDO::PARAM_INT);PHPマニュアルのbindParam()の第二引数に着目してほしいんだけど、
public PDOStatement::bindColumn ( mixed $column , mixed &$param [, int $type [, int $maxlen [, mixed $driverdata ]]] ) : boolこんな感じ mixed &$param で、変数への参照を渡す事になっている。だから、bindParam()とは別のところで、その変数を定義する必要があるわけなのです。以下のように、直接リテラルを渡す事はできません。
$sth->bindParam(':calories', 150, PDO::PARAM_INT); // エラー結果、1つのバインドに対して2行が必要になります。この実装については正直あんまり綺麗とは思えず、そこはライブラリ側で担保しておくべきところなんじゃないの?とはずっと思っています。
まとめ
このように、PHP(とか)において、変数展開はとても強力で便利な機能であり、プログラマの たしなみ みたいなもんなので、絶対に覚えるようにしてほしい。特に初学の段階で覚えておくべき超重要機能です。
だからこそ、ストリングリテラルはダブルクオートで囲うべきであって、シングルクオートで囲ってしまうような知能の欠如したプログラマは滅びて欲しいのです。
シングルクオートを使うのはストリングリテラルの中で $ そのものを使いたいとか絶対に変数展開させず書いたまま使いたいとか限られたケース、または、ダブルクオートリテラル内にHTMLやSQLをかかなければならなくて、そのattribute値やcolumn値を囲う場合に使うとか、本当に利用すべき場所なんか限られるはずなんです。
それでもシングルクオートを使う人が絶えないのは結局、自分で物を考えることができず、見よう見まねでコードを書いている人が山のように居るからって事にほかならないと思っています。
変数展開しない、例えば連想配列のキーはシングルクオートでもいいでしょ?
みたいな人も居るだろうけど、どうしてそこまでして敢えてそこで使いたい? 意味わからん。
仕事なんですよ。遊びじゃないんです。効率というものを考えたら、まー、シングルクオートなんてありえないですね。
そんなわけで、変数展開出来ない旧石器時代みたいなプログラミング言語はあんまり触りたくないでーす。後で書きますけど、ヒアドキュメントの無いプログラミング言語もかなり鬱陶しいですね。(書いた。超初歩:PHP(とか)書くなら知っておいてほしい事2-ヒアドキュメントの使い方を変数展開を交えて説明)
基本、ゴミクズのようになったコードは大嫌いでPHPはそんなコードだらけなのですが、PHPのコードの大半がゴミクズなのは実はPHPが悪いのではなく、コード書いてる人が頭が悪いからなのです。JavaScriptも似たようなところがありますね。
というわけで、
ストリングリテラルは "(ダブルクオート)で囲ってくんろ
以上でした。
- 投稿日:2019-01-26T17:06:24+09:00
【PHP】print_r(false)をvar_dumpしたらtrueになった
タイトルの通りです。
var_dump(print_r(false));
の結果は、bool(true)
になります。<?php print_r(true); // 出力結果:1 var_dump(print_r(true)); // 出力結果:1bool(true) print_rの出力(1)とvar_dumpの出力(bool(true))が同時に行われる print_r(false); // 出力結果:なし var_dump(print_r(false)); // 出力結果:bool(true)公式サイトに答えがありました。
return パラメータがTRUEの場合は、この関数はstringを返します。それ以外の場合の返り値はTRUEです。
例えば
print_r($test, TRUE)
のように記述すると、その場で出力せずにprint_rの結果を返り値として渡します。それをしない場合は、全て返り値はTRUEになるとのことでした。
<?php $test_true = print_r(true); // 出力結果:1 echo $test_true; // 出力結果:1 var_dump($test_true); // 出力結果:bool(true) echo gettype($test_true); // 出力結果:boolean $test_false = print_r(false); // 出力結果:なし echo $test_false; // 出力結果:1 var_dump($test_false); // 出力結果:bool(true) echo gettype($test_false); // 出力結果:boolean // 第二引数にTRUEを指定し、print_rの結果を返り値として受け取る $test01 = 'test01'; $test02 = print_r($test01, TRUE); // 出力結果:なし echo $test02; // 出力結果:test01出力値と返り値を混ぜていただけでした・・・
- 投稿日:2019-01-26T16:30:33+09:00
Unreal C++でAES暗号化しPHPで復号するときにハマったこと
Unreal Engine内のゲームのリザルト情報を暗号化し、サーバー上で復号する処理を書いてみました。
C++でのAES暗号化に関してはとにかく情報が少なく、まず暗号化の方式から勉強するハメになりました。結論としては、AES-256-CBC方式で、OpenSSLを使って暗号化を行っています。
具体的には、Unreal C++でFString
をunsigned char*
に変換、AES_cbc_encrypt()
を用いて暗号化しFBase64::Encode()
でBase64エンコード、それをPHP側でopenssl_decrypt()
しています。以下、Unreal Engine特有の記述も含まれます。
純粋にC++でOpenSSLを使ってAESで暗号化したい方は適宜読み飛ばしてください。コードの全容は末尾に載せています。
ざっくり
- Unreal Engine内蔵のAES暗号化はセキュリティの観点から微妙なのでOpenSSLを使うべき
- PHPの
openssl_encrypt()
は中でBase64エンコードをしている- AES-256の中にもいくつかモードがある
- パディングは0で埋めればいいというわけではない
AES-256と「暗号利用モード」
AESとは暗号と復号に同じ鍵を用いる共通鍵方式の暗号化アルゴリズムです。
鍵の長さによりAES-128, AES-192, AES-256に分けられます。今回はAES-256を用います。
AESが一度に暗号化できるデータの長さは16バイトと決まっています。
AES-256で16バイト以上のデータを暗号化したい場合は暗号化処理を繰り返し行う必要があります。
その暗号化処理の繰り返しをどうやって行っていくかを「暗号利用モード」と言います。
詳細はこちらの記事が分かりやすいです。【暗号化】ブロック暗号のモードまとめ (比較表付き) - Qiita
https://qiita.com/shoichi0599/items/6082b765c1257b71985bよく登場するのは2つ。
- 左から右に素直に暗号化していくECB (セキュリティ微妙)
- ひとつ前のブロックとのXORをとりながら暗号化するCBC (セキュリティ良い)
ECBを除く多くのモードでは初期化ベクトル、略してIVを必要とします。
Unreal内蔵のAES暗号化はおそらくECB
以上を踏まえて、Unreal Engineに初めから内蔵されているAES暗号ライブラリ"FAES"を使ってみましょう。
(参考: https://answers.unrealengine.com/questions/289228/ue4-fstring-encryption-example-using-faes.html )FString UResultEncryptor::Encrypt(FString str) { if (str.IsEmpty()) return str; //暗号化するデータを16バイト単位にする uint32 Size = str.Len(); Size = Size + (FAES::AESBlockSize - (Size % FAES::AESBlockSize)); // キーを生成 FString Key = "hogehogefooofooohogehogefooofooo"; TCHAR *KeyTChar = Key.GetCharArray().GetData(); ANSICHAR *KeyAnsi = (ANSICHAR*)TCHAR_TO_ANSI(KeyTChar); uint8* Blob = new uint8[Size];//実際に暗号化するバイト列 if (FString::ToBlob(str, Blob, str.Len())) { FAES::EncryptData(Blob, Size, KeyAnsi);//暗号化 str = FBase64::Encode(Blob, Size); delete Blob; return str; } delete Blob; return ""; }前の章で述べたIVを指定する箇所がないことから、暗号利用モードはECBかと推測されます。
というわけで今回は、AES-256-CBCで暗号化ができるOpenSSLをインクルードして使ってみることにしました。Unreal C++でOpenSSLを使う方法
Build.csにコンポーネント
OpenSSL
を追記します。YourModule.Build.csusing UnrealBuildTool; public class YourModule : ModuleRules { public YourModule(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "OpenSSL" }); //OpenSSLを追記 } }暗号化を行いたいファイルに以下を記述します。
今回はAESしか使わないのでaes.h
のみインクルードしています。Encryptor.h#define UI UI_ST THIRD_PARTY_INCLUDES_START #if PLATFORM_WINDOWS #include "ThirdParty/OpenSSL/1.0.2g/include/Win64/VS2015/openssl/aes.h" #elif PLATFORM_MAC #include "ThirdParty/OpenSSL/1.0.2g/include/Mac/openssl/aes.h" #endif THIRD_PARTY_INCLUDES_END #undef UI正しいパディングの埋め方"PKCS#7"
前述の通りAES-256は16バイト単位で暗号化を行うのですが、暗号化したいデータが16バイトの倍数であるとは限りません。
そんなときはデータが16バイトの倍数になるようにデータを水増しします。その水増し部分をパディングと言います。さて、そのパディングを何で埋めますか?
「そりゃ0でしょ」とお思いのあなた、違います。PHPの
openssl_encrypt()
およびopenssl_decrypt()
ではPKCS#7と呼ばれる方式でパディングを埋めてるとのことです。これは「0ではなくパディングのバイト長で埋める」という考え方です。
例えば暗号化したいデータを16バイト単位で区切っていった結果末尾が以下で終わったとします。
0A CB EB 47 3E FC FF 00 00 2D
この場合パディング長、つまり16バイトになるまでに足りないバイト数は6バイトです。
よって、以下のようにパディングを埋めます。
0A CB EB 47 3E FC FF 00 00 2D 06 06 06 06 06 06
パディングが0バイト、つまり元のデータがちょうど16バイトの倍数の場合は、パディングが16バイトとして解釈するようです。
データの末尾が01
で終わった場合に、それが1バイトのパディングを付与した結果なのか元データが01
で終わったのかが区別できないからでしょう。OpenSSLにも
pkcs7.h
というそれっぽいヘッダーファイルがあったので覗いてみたのですが全く分からなかったので手動で実装しました…Encryptor.cppFString UResultEncryptor::Encrypt(FString str) { //... uint32 Size = str.Len(); Size = Size + (AES_BLOCK_SIZE - (Size % AES_BLOCK_SIZE)); //strをもとに暗号化するバイト列 in を用意 TCHAR *c = str.GetCharArray().GetData(); unsigned char *in = new unsigned char[Size]; strcpy((char*)in, (char*)TCHAR_TO_UTF8(c)); //PKCS#7パディングを付加 int padSize = Size - str.Len(); memset(in + str.Len(), padSize, padSize); //...Base64エンコードを忘れずに
AES-256は暗号化する内容がテキストかバイナリかなんて考えてくれないので、暗号化した後のデータをそのままUTF-8の文字列として解釈できる可能性は限りなく低いです。
PHPの
openssl_encrypt()
およびopenssl_decrypt()
は、オプションを指定しない限りは暗号化した後はBase64エンコード、復号化する前はBase64デコードを行います。よって、C++側でも暗号化の後にはBase64エンコードしてあげましょう。
今回はUnreal Engine内蔵のFBase64
を用いました。
純粋なC++で実装してる方はすみません、頑張って探してください。Encryptor.cpp#Encrypt()AES_cbc_encrypt((unsigned char*)in, out,Size, &aesKey,iv, AES_ENCRYPT); str = FBase64::Encode(out, Size); str = FGenericPlatformHttp::UrlEncode(str); return str;コードの全容
以上を踏まえて、今回書いたコードです。
モジュール名など一部脚色を加えています。
本当はこれにJSON関連の処理も入っているのですが省略しています。Encryptor.h#pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "Json.h" #include "Base64.h" #include "GenericPlatformHttp.h" #define UI UI_ST THIRD_PARTY_INCLUDES_START #if PLATFORM_WINDOWS #include "ThirdParty/OpenSSL/1.0.2g/include/Win64/VS2015/openssl/aes.h" #elif PLATFORM_MAC #include "ThirdParty/OpenSSL/1.0.2g/include/Mac/openssl/aes.h" #endif THIRD_PARTY_INCLUDES_END #undef UI #define AES_KEYLENGTH 256 #include "Encryptor.generated.h" UCLASS() class MYPROJECT_API UEncryptor : public UBlueprintFunctionLibrary { GENERATED_BODY() public: UFUNCTION(BlueprintCallable, Category = "Encrypt") static FString Encrypt(FString str); };Encryptor.cpp#include "Encryptor.h" FString UEncryptor::Encrypt(FString str) { if (str.IsEmpty()) return str; //Size設定 ブロックサイズ単位になるように調整 uint32 Size; Size = str.Len(); Size = Size + (AES_BLOCK_SIZE - (Size % AES_BLOCK_SIZE)); // キー KeyChar を生成 FString Key = "hogehogefooofooohogehogefooofooo"; TCHAR *KeyTChar = Key.GetCharArray().GetData(); unsigned char *KeyChar = new unsigned char[AES_KEYLENGTH]; strcpy((char*)KeyChar, (char*)TCHAR_TO_UTF8(KeyTChar)); //strをもとに暗号化するバイト列 in を用意 TCHAR *c = str.GetCharArray().GetData(); unsigned char *in = new unsigned char[Size]; strcpy((char*)in, (char*)TCHAR_TO_UTF8(c)); //PKCS#7パディングを付加 int padSize = Size - str.Len(); memset(in + str.Len(), padSize, padSize); //暗号化されたバイト列が入る out unsigned char* out = new unsigned char[Size]; //初期化ベクター //ホントは0で埋めずにちゃんと作ってあげた方がいいです unsigned char iv[AES_BLOCK_SIZE]; memset(iv, 0x00, AES_BLOCK_SIZE); //暗号化 if (in != nullptr) { AES_KEY aesKey; AES_set_encrypt_key((unsigned char*)KeyChar, AES_KEYLENGTH, &aesKey); AES_cbc_encrypt((unsigned char*)in, out,Size, &aesKey,iv, AES_ENCRYPT); str = FBase64::Encode(out, Size); str = FGenericPlatformHttp::UrlEncode(str); return str; } return ""; //失敗したときは空の文字列を返す }index.php<?php function decrypt($str) { //暗号化パラメータ define('PASSWORD','hogehogefooofooohogehogefooofooo'); $password = PASSWORD; $method = 'aes-256-cbc'; if(!empty($str)) { $str = openssl_decrypt($str, $method, $password); } return $str; } echo decrypt($_GET['s']); ?>実行結果hoge
- 投稿日:2019-01-26T15:42:47+09:00
【PHP】print_rやvar_dumpした値を代入すると、その時点で出力も行われる
タイトルの通りです。
そもそも代入する人はいないと思いますが・・・
print_r
通常の
print_r
の出力が行われます。加えて、boolean型の1
が代入されます。<?php $test01 = 'test01'; $test02 = print_r($test01); // 出力結果:test01 print_r($test02); // 出力結果:1 var_dump($test02); // 出力結果:bool(true)var_dump
通常の
var_dump
の出力が行われます。こちらは何も代入されません。$test03 = 'test03'; $test04 = var_dump($test03); // 出力結果:string(6) "test03" print_r($test04); // 出力結果:なし var_dump($test04); // 出力結果:NULL
- 投稿日:2019-01-26T14:20:41+09:00
LaravelでGoogle しごと検索の Indexing APIを呼び出す
はじめに
Google しごと検索が日本でもリリースされました。求人情報をGoogleの検索結果に表示することができるようになります。
Googleによると、必要な手順は大まかに以下の3つになります
- 求人情報を構造化データ(JobPosting)でマークアップ
- Indexing APIを利用して更新/削除情報をGoogleに通知
1はGoogleの資料、構造化データタイプの定義を参照
2のIndexing APIをPHP+Laravelから利用してみます。なお、今日時点でIndexing APIの利用は無料です。
LaravelでGoogle しごと検索の Indexing APIを呼び出す
Google提供のクライアントライブラリをcomposerでプロジェクトに追加します。
https://github.com/googleapis/google-api-php-clientお約束。composer require
composer require google/apiclient:"^2.0"composer.jsonにgoogle/apiclientが追加されました。
// (略) "require": { "php": "^7.1.3", "chillerlan/php-qrcode": "^2.0", "davejamesmiller/laravel-breadcrumbs": "5.x", "doctrine/dbal": "^2.5", "encore/laravel-admin": "1.6.*", "fideloper/proxy": "^4.0", "google/apiclient": "2.0", // (略)ライブラリインストールします
composer installライブラリが利用できるか確認するために、適当に呼び出してみました
use Google_Client; // 略 $client = new Google_Client(); $client->setApplicationName("Client_Library_Examples"); $client->setDeveloperKey("YOUR_APP_KEY"); dd($client->getAccessToken()); => nullよさそう。
続いて、
https://developers.google.com/search/apis/indexing-api/v3/prereqs?hl=ja
にあるPHPサンプルコードを貼り付けてみます
(jsonファイルはGoogle APIs コンソールから入手してconfigフォルダに置いた)$client = new Google_Client(); $client->setAuthConfig('./config/ivory-totem-999999-1234567890.json'); $client->addScope('https://www.googleapis.com/auth/indexing'); $httpClient = $client->authorize(); $endpoint = 'https://indexing.googleapis.com/v3/urlNotifications:publish'; $content = '{ "url": "https://job.e2info.co.jp/job/4611", "type": "URL_UPDATED" }'; $response = $httpClient->post($endpoint, ['body' => $content]); dd($response ); => GuzzleHttp\Message\Response {#1000 -reasonPhrase: "OK" -statusCode: 200 -effectiveUrl: "https://indexing.googleapis.com/v3/urlNotifications:publish" -headers: array:11 [ "content-type" => array:1 [ 0 => "application/json; charset=UTF-8" ] "vary" => array:3 [なんと、Index送信成功しました
注意事項
- Google曰く、サイトマップよりIndexing APIの使用をおすすめするが、サイトマップ送信もしましょうとのこと。つまり両方やったほうがいい(文献)
まとめ
ということで、あっという間に完成してしまいました。
- 実際に使う場合、例外処理とログ出力はちゃんとやりましょう。
- 一括送信する場合、マルチパートリクエスト(バッチ処理)を使いましょう。
- 割り当て制限に注意。割り当て
終わり
- 投稿日:2019-01-26T14:00:27+09:00
[PHP] try-catch-finally句内のreturnについて
概要
try-catch-finally句内のreturnについて - @eijenson さん の記事を読んで、PHPではどうなってんだろ?という素朴な疑問が湧いたので、試して見た。
前提環境
macOS High Sierra 10.13.6
PHP 7.3.0 (cli) (built: Dec 7 2018 11:06:29) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.3.0-dev, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.3.0, Copyright (c) 1999-2018, by Zend Technologies結果
ソースコード<?php class Sample { public function returnsample() { try { echo 'try!'; throw new Exception('sample!'); return 'try!!!'; } catch (Exception $e) { echo 'catch!'; echo $e; return 'catch'; } finally { echo 'finally statement is executed!'; return 'Crush the exeception'; } } } $a = new Sample(); $sample = $a->returnsample(); // 例外が出たにも関わらず、ここの中身については // 出力されている。 echo "\n\nsample output!\n"; echo $sample;出力結果$ php try-catch-return.php try!catch!Exception: sample! in /Users/oki.suguru/workspace/php/try-catch-return.php:17 Stack trace: #0 /Users/oki.suguru/workspace/php/try-catch-return.php(33): Sample->returnsample() #1 {main}finally statement is executed! sample output! Crush the exeception%見事に$eの中身である$eが握りつぶされることがわかった。
Javaと同様,PHPでもfinally句のなかで、returnすると、例外の内容が握りつぶされるようだ。バージョンごとに何か違いがあるかどうかは未検証。
気が向いたらやることにする。
- 投稿日:2019-01-26T13:10:18+09:00
CentOS6.10 + php7 + Composer +laravel5.7を導入
はじめに
centos6.10にphp7とcomposerを導入したのでそのメモになります。
php
composerを導入するにはまずphpを導入しなければいけません。まずphpをインストールします。
remiリポジトリの導入
何もせずにyumでインストールしようとするとphp5がインストールされてしまうので、remiリポジトリを導入してphp7をインストールする準備をします
remiリポジトリの導入
公式remiインストール
yum install http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
php7.3インストール
yum list php で確認するとバージョンはphp7.3が一番新しいようだったのでphp7.3をインストール
yum install php --enablerepo=remi-php73# php -v PHP 7.3.1 (cli) (built: Jan 8 2019 19:26:20) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.3.1, Copyright (c) 1998-2018 Zend Technologiescompoesr
composerインストール
確か公式ページのインストール方法を簡単にした方法↓
# curl -sS https://getcomposer.org/installer | php All settings correct for using Composer Downloading... Composer (version 1.8.0) successfully installed to: インストールパス Use it: php composer.phar場所を移動
mv composer.phar /usr/local/bin/composercomposer コマンド
ファイル
composer.json
- プロジェクトに必要なライブラリをここに記入していく
composer.lock
- composerコマンドでcomposer.jsonに記載されたライブラリと、依存関係でインストールしたライブラリも記載される
- 後述するcomposer install すれば開発環境をメンバーで同じにできる
composer コマンド
comoser init
- composer.jsonが生成。laravelなどプロジェクト作成時に実行される
composer require
- インストールしたい対象だけをインストール。
- composer.jsonを直接編集すると、インストールするためにcomposer updateを実行しなくてはいけず、インストールしたい対象だけでなくその他も一緒にアップデートされてしまう。
- よって、直接編集してインストールするということはなさそう。
composer update
- composer.jsonにかかれているライブラリを全てアップデート。
composer install
- composer.lockにかかれているライブラリをインストール。
laraver5.7導入
インストール
composerを使ってlaravelインストール
composer global require laravel/installer下記、グローバルのcomposerへパスを通しましょう
$HOME/.config/composer/vendor/bin/プロジェクト作成
laravel new プロジェクト名バージョン確認。laravelではartisanを使用してマイグレーションだったりその他諸々のlaravelの機能を使用するみたい。
# php artisan -V Laravel Framework 5.7.22