Webとの親和性が高いスクリプト言語であるPHPの基本的な内容や注意点についてまとめました。
Webとの親和性が高いスクリプト言語であるPHPの基本的な内容や注意点についてまとめました。
こんにちは。
EMoshUのブログでは、メンバーのインタビュー記事に限らず、エンジニアブログと題して、技術的な内容についても発信していこうと考えています。
エンジニアブログ第1弾となる今回は、PHP(7系を想定)について書かせていただきます。
サーバーサイドのエンジニアの方であれば、おそらく一度は使ったことがあるであろうPHP。
Webとの親和性が高いスクリプト言語で、比較的とっつきやすい言語ということもあり、なんとなく雰囲気で書いても動いてしまうので、コンパイル型言語に慣れ親しんだ方からすると、違和感のある言語だったりもすると思います。
特に、言語仕様がゆるいという部分は、良い面もあれば悪い面もあり、仕様を正しく理解しないでコードを書いていると思わぬバグを生んでしまったり、セキュリティホールを作り出してしまう、なんてことにもなりかねないので、注意が必要な言語です。
ということで今回は、このPHPの細かな部分の挙動や違いをいくつかピックアップして、違いの比較を行ったり、考察していきたいと思います。
今回取り挙げる1つ1つの話題は、他のネット記事等でも度々取り上げられている内容だったりもしますが、本記事の後半の方で、これらの挙動の違いから見えてくる注意点を考察したり、コーディングする上で大事な考え方といった部分についても述べさせていただきますので、ぜひ最後までお読みいただければと思います!
1つめは、if文等の条件式でよく使われる、==と===です。
挙動の違いとしては、型まで含めて一致しているかを確認するか否かです。
(文章で説明されてもわかりづらいですよね・・)
コードで説明した方がわかりやすいと思いますので、具体例を挙げてみます。
// 文字列の'1'と数値の1を==で比較
$hoge = '1';
if ($hoge == 1) {
echo('true');
} else {
echo('false');
}
$ php test.php
true
==で比較した場合、型は文字列であるか数値であるかは関係なく、値のみを比較します。
そのため、今回の場合はどちらも「1」なので、判定結果はtrueになります。
// 文字列の'1'と数値の1を===で比較
if ($hoge === 1) {
echo('true');
} else {
echo('false');
}
$ php test.php
false
===で比較した場合、型も含めて値が一致しているかを厳密に比較します。
そのため、「1」という値としては一致していますが、文字列と数値とで型が異なるので、判定結果はfalseになります。
一見、==の方が便利そうに思えるかもしれませんが、きちんと理解せずに==を多用してしまうと、気づかぬうちにバグを埋め込んでしまう可能性があるので注意が必要です。
たとえば、0(数値)と'hoge'(文字列)を==で比較した場合、結果はどうなるでしょうか?
$a = 0;
$b = 'hoge';
if ($a == $b)
上記の判定結果は、trueになります。
$bは文字列なので一見falseになりそうに見えますが、上記のような==での比較の場合、内部的にキャストが行われ、文字列を数値に変換しようと試みた結果0になり、0と0の比較なのでtrueになる、という挙動になります。
このように、型がバラバラな値同士を==で比較すると、判定結果がとてもわかりづらく、意図していた動作と異なる挙動になってしまう場面に遭遇することがあります。
今回のような簡単なサンプルプログラムであれば、まだ気づきやすいと思いますが、これが大規模なWebアプリケーションやフレームワークを扱う場合などは、開発時には気づかず、あとあとになってなんか挙動がおかしい、なんてことになりかねません。
(テスト時に検知できればまだなんとかなりますが、これが検知されずにリリースされてしまったとしたら・・・)
このような状況を回避するためにも、私自身もなるべく===を使用するように心がけるようにしています。
===を使用して、文字列の1と数値の1を比較する場合は、たとえば以下のようなやり方が考えられます。
// $c = '1';
// $d = 1;
if (intval($c) === intval($d)) {
// trueだった場合の処理
}
上記のような形で、自身で明示的にキャストして型を統一するようにした上で、===を使って判定する、といった実装にすることで、想定外の判定結果になることを防ぐことができます。
これは個人的な経験則の話になるのですが、フレームワークによっては、ビュー(テンプレート)やJavaScriptから、1とか2といった数値をPHP(アクション・コントローラー)側に受け渡した際に、整数型ではなく文字列型としての値になっているケースがあったりしました。
(真偽値の想定が、文字列として'true'・'false'が渡ってくるなんてこともあったり)
このような状況に出くわしたとしても、安易に==で判定するのではなく、キャストする等してきちんと型を意識するようにすることで、結果的にバグを減らすことにつながるのかな、と思っています。
2つめは、文字列を扱う際に使われる'(シングルクォーテーション)と"(ダブルクォーテーション)です。
違いとしては、変数展開を行うか否かです。
これも文章での説明だとなかなか伝わりづらいと思いますので、具体例を挙げてみます。
// ''で括った場合
$hoge = 'hoge';
$fuga = '$hoge';
$piyo = '\n';
echo $hoge, $fuga, $piyo, $hoge;
$ php test.php
hoge$hoge\nhoge
'(シングルクォーテーション)で括った場合、対象の値は変数展開されずに、文字列扱いされて設定されます。
上記の例の場合、以下のような解釈になります。
変数名 | 解釈される値 | 補足 |
---|---|---|
$hoge | 'hoge' | hogeという文字列が設定されます |
$fuga | '$hoge' | $hogeの変数の中身ではなく、$hogeという文字列が設定されます |
$piyo | '\n' | 改行文字としての\nではなく、\nという文字列が設定されます |
// ""で括った場合
$hoge = "hoge";
$fuga = "$hoge";
$piyo = "\n";
echo $hoge, $fuga, $piyo, $hoge;
$ php test.php
hogehoge
hoge
"(ダブルクォーテーション)で括った場合、対象の値は変数展開されて、その結果が設定されます。
上記の例の場合、以下のような解釈になります。
変数名 | 解釈される値 | 補足 |
---|---|---|
$hoge | 'hoge' | hogeという文字列が設定されます |
$fuga | 'hoge' | 変数$hogeが展開されて、$hogeに設定されているhogeという文字列が設定されます |
$piyo | (改行) | 改行文字として解釈されて、改行されます |
"(ダブルクォーテーション)の方が、よしなに変数展開をしてくれて便利な印象がありますが、括る対象の値が明らかに文字列であることがわかっている場合は、個人的には'(シングルクォーテーション)を使用するようにしています。
理由は、文字列を"(ダブルクォーテーション)で括った場合、変数展開しようとする分の計算コストが発生するため、明らかに文字列であるのであれば、シンプルに'(シングルクォーテーション)で括った方が処理の無駄がないためです。
(といっても、最近のコンピュータのスペックから考えると、計算コストは大した差にはならないと思いますが、明確に文字列である、というのを示すためにも、使い分けるように意識しています)
もちろん、改行文字などを機能させたい場合は、'(シングルクォーテーション)だと文字列扱いされてしまうため、"(ダブルクォーテーション)を使用して変数展開する必要があります。
プログラムを動作させる、という意味においては、どちらを使用しても問題ありませんが、一緒に働くメンバーには、ぜひこれらの違いをしっかりと理解した上で、使い分けられるようになってもらいたいと思っています。
3つめは、変数の値や(連想)配列のキーや値の確認でよく使われるisset()とempty()です。
判定する値によっては、真偽値の判定結果が微妙にわかりづらい場合があるので、それぞれの実行例と、実行結果を比較してみます。
$value_list = [
null, // null
'', // 空文字
0, // 数値の0
'0', // 文字列の'0'
1, // 数値の1
'1', // 文字列の'1'
true, // 真偽値(true)
false, // 真偽値(false)
[], // 空配列
[1], // 配列
['hoge' => 'fuga'] // 連想配列
];
foreach ($value_list as $value) {
if (isset($value)) {
echo 'true', "\n";
} else {
echo 'false', "\n";
}
}
$ php test.php
false
true
true
true
true
true
true
true
true
true
true
isset()は、値が設定されていればtrueを返す、という挙動になるので、基本的に何かしらの値が入っていればtrueになります。
上記の例の場合でも、null以外はすべてtrueという結果になっています。
$value_list = [
null, // null
'', // 空文字
0, // 数値の0
'0', // 文字列の'0'
1, // 数値の1
'1', // 文字列の'1'
true, // 真偽値(true)
false, // 真偽値(false)
[], // 空配列
[1], // 配列
['hoge' => 'fuga'] // 連想配列
];
foreach ($value_list as $value) {
if (empty($value)) {
echo 'true', "\n";
} else {
echo 'false', "\n";
}
}
$ php test.php
true
true
true
true
false
false
false
true
true
false
false
empty()は、値が空でなければtrueを返す、という挙動になりますが、isset()と比べて、一部判定結果がわかりづらいものがあるので注意が必要です。
たとえば、文字列・数値を問わず0を判定する場合や、真偽値(true・false)をempty()で判定しようとすると、結果が混乱しやすいかもしれません。
isset()は、私の場合は主に連想配列のキーの存在チェックで使用しています。
具体的な使用例は以下のような感じです。
// $e = [
// 'hoge' => 'value',
// 'fuga' => 'value',
// ]
if (isset($e['hoge'])) {
// trueの場合の処理
}
一方で、値の0や空文字チェックを行う際は、empty()関数を使用すると想定外のバグを埋め込んでしまう可能性があるので、===を使用してチェックを行うようにしています。
// $f = '0'
// trueにならないケース
if (empty($f)) {
// trueの場合の処理
}
// trueになるケース
if (intval($f) === 0) {
// trueの場合の処理
}
このように、isset()やempty()、===などを必要に応じて使い分けられるようになると、バグにつながる機会が減ってくると思います。
そのためには、挙動の違いについてしっかりと理解する必要があります。
今回はPHPのごくごく基本的な内容のご紹介となりましたが、本記事での考察は以上となります。
ご紹介した内容について「知らなかった」という方は、今日からぜひ意識してみていただければと思います。
以下、私が今回の記事を書くにあたって考えたことについて述べさせていただきます。
今回、EMoshUのエンジニアブログの第1弾の記事を書くにあたって、どのようなレベル感の内容を記事にするか真剣に悩みました。
基本的な内容の紹介記事にするのか、これまでの案件等を通して培ったノウハウの紹介記事にするのか、はたまた何かに特化したテクニック集のような記事にするのか、等々。
色々と悩んだ結果、基本的な内容の紹介記事にする、という選択をしました。
理由は以下の2点です。
至極当然の話ではありますが、プログラムは、正しく動作するコードを書くことが大事です。
それに加えて、可読性やメンテナンス性を考慮したり、パフォーマンス面について意識できるようになると、さらにレベルアップできます。
正しく動作するコードを書くためにも、上記のようなレベルアップのためにも、基本を理解して徹底することが、私は何もよりも大事であると考えています。
どんなに大規模なシステムであっても、突き詰めていくと、最終的には単純な分岐処理や繰り返し処理が行われているだけ、といったことが多々あったりします。
一見するとすごく複雑そうに見えるプログラムも、1つ1つ細分化して見ていくと、小さな処理が複数集まって、表面上複雑に見えているだけだったりします。
プログラミングに限った話でなく、大きな成果を上げるためには、小さなことをコツコツと積み上げていく必要があります。決して近道はない。
プログラミング(今回の場合だとPHP)に関しては、今回ご紹介した内容はほんのごく一部ですが、1つ1つ細かな部分にまで気を配って、意識しながらコードを書くようにすることで、少しずつ成長していけるのかなと思っています。
もちろん私自身もまだまだ実力不足の身ですので、上記のようなことを意識しながら、日々試行錯誤を繰り返しています。
もっともっと、エンジニアとしても、人としても強くなれるよう、今後も精進していきます。
EMoshUは、メンバー同士で切磋琢磨しながら、一緒に会社を大きくしていける、そんな仲間を募っています。
一緒に同じ志を持って試行錯誤しながら成長したいという方、さらなるレベルアップを図りたいという方がいらっしゃいましたら、ぜひ募集要項をご確認の上、ご連絡いただければと思います。