【PHP】今更聞けない文と式についての基礎の基礎
目次
きっかけ
私は今までなんとなくコードを書いてきましたが、よく考えるとなぜセミコロンで区切るのだろうかとか、 ifの次に ()
を書くのはなぜ?とか超初心者の質問に答えられない自分に気づいたのです。
入門書を見てもなんとなく「こう書くとこう動くよ」と書いてあることが多く、何故そういうコードを書くか厳密に説明できますか?
私にはできませんでした。
そこで改めて調べると重要なワードが3つあることに気づきました
- 文(statement)
- 式(expression)
- 言語構造(language construct)
この3つです。
「クラスは?関数は?トレイトは?」などと聞こえてきそうですが、これらも上記3つの中のどれかに過ぎません。
ではこれらの基本構文について説明していきたいと思います。
文(statement)
PHPのコードは文の集まりである
PHPのコードは文で構成されます。
最初に重要な結論をまとめると、PHPのコードは文の集まりです。多数の文からコードができています。
以下、すんごい簡単なコードの例です。
$price = 1000; // 一つの文
if($price > 100) {
$price_with_tax = 1000 * 1.1; // これも計算と代入をする文
echo "税込み $price_with_tax 円です";
}
上のコードではいくつ文があるんでしょうか?
PHPでは文はセミコロン ;
て区切るよー!というのは誰でも知ってると思います。
では上のコードでは ;
が3つあるので3つの文なのですしょうか?
if文は文というけど一つの文として数えますか?
ド初心者に質問されるとわからなくななってきます。
これらを説明するための式、言語構造についての理解が必要になります。
リテラル(literal)と変数(variable)
式の説明に入る前に変数とリテラルについて、説明します。
変数(variable)
変数についてはサラッと説明します。雑に説明すると $
で始まる何かが変数です。
$a = 1; // 超基本
$b = $a; // 変数は他の変数に代入できる
var_dump( $b ); // 変数は関数呼び出しに使うことが多い
$date = new DateTime(); // オブジェクトなども変数に格納する
リテラル(literal)
リテラルとは一言でいうと「変数じゃなくて直接値を記載するなにか」をリテラルと言います。
PHPのコードを書いてると $name
とか $count
とか、変数を使います。
一方で $count = 1
とか、 $name = 'taro'
とか直接値を記載することもあります。
この直接コードに記述する値を リテラル と言います。
直値をコードの中に書くなー!と誰でも教育されますが、実際にはそんなこと無理です。
function do_something(){
/* 実際の処理 */
if( $error ) { // エラーなら false を返す
return false; // falseもリテラル、直値
}
}
ね、無理ですよね?
ほかにもカウンターを0にしたり、nullやfalseを返したり、絶対にリテラルは必要になります。
ではどんなリテラルがあるのか具体的に見ていきます。
普通のリテラルの例
$number = 1; // 数値はリテラル
$name = '太郎'; // 文字列もリテラル
$isError = false; // trueやfalseもリテラル
$null = null; // nullもリテラル
このくらは基礎の基礎なので、誰でも理解できると思います。ちょっと複雑なリテラルもあります。
ちょっと難しいリテラル
$numbers = [1, 2, 3]; // 配列はリテラル
$prices = ['apple' => 100, 'orange' => 200 ]; // 連想配列もリテラル
$hex = 0xff; // 16進数の数字
// ヒアドキュメントも文字列なのでリテラル
$haiku = <<<EOD
古池や
蛙飛び込む
水の音
EOD
リテラルという言葉を知らなかった人も多いんじゃないでしょうか?知らなくてもコードは書けますからね。
ここまで理解したうえで本日の一番の課題の式の説明に行きます。
式(expression)
PHPは式だらけの言語
式というと何を浮かべますか?
$a = 1 + 2;
これは代入式という式ですね。誰でもわかると思います。
1;
これは式ですか?直感に反するかもしれませんがPHPではこれは式です。
PHPではそれっぽいものはたいてい式になります。
$a; // 変数も式
1 + 1; // 計算も式
$a !== 1; // 比較演算も式
[1, 2, 3]; // 配列も式
max(1, 2, 3 ); // 関数を呼ぶのも式
$a ? 'はい' : 'いいえ'; // 三項演算も式
ここで重要なのは、 式は値を持つ ということです。
値を持つということは
- 変数に代入できる
- 関数に引数として渡すことができる
ということです。後ろで詳細の説明をします。
式の値
とても大事なことなので3回言います。
式は必ず値を持ちます。
式は必ず値を持ちます。
式は必ず値を持ちます。
以下の式の値は直感的にわかると思います。
考えてみてください
1 + 1;
はい。2ですね。ばかにするなとか怒らないでください。続きます
$a = 1 + 1;
この式の値はいくつでしょうか?これも答えは2です。代入式の値は最後に代入された値になります。
式が値を持つことを理解するためにはechoするとわかりやすいです。
echo( $a = 1 + 1 ); // 2と表示される
比較演算などもすべて式です。だから値を持ちます。
$name = '太郎';
echo ($a === '太郎'); // 1と表示される
echo ($a === '花子'); // なにも表示されない
比較演算式である $a === '太郎'
はtureもしくはfalseをという値を持ちます。
しかし、echoするときにtrueは1、falseは空文字列 ''
で表現されます。 これがPHPの気持ち悪いところですね。
演算子で結合された式は(たぶん)すべて値を持ちます。
ちょっと例を見てみます
$a = 1;
$a === 1 || $a === 0; // true
$a === 1 && $a === 0; // false
!$a; // false
!!$a; // true
$a !== 0; // true
!($a !== 0); // false
$a === 1 ? 'いちだよ' : 'いちじゃないよ'; // いちだよ
リテラル、変数と式の関係
前述しましたがPHPでは以下のようなコードも式です
1;
$a;
[1, 2, 3];
リテラルや変数も単独で式であること言うことです。
つまりこの三者の関係を表すと図のようになります。
- 変数はすべて式です
- リテラルもすべて式です
「変数」だと思っていたものはほとんど式だった
さてここで、 sqrt()
というPHPの組み込み関数を例に取ります。
まずは公式ドキュメントの定義から。
sqrt(float $num): float
float型の引数をとり、float型の値を返します。返す値は $num の平方根です。
echo sqrt(4); // 2
echo sqrt(25); // 5
ここでsqrt()の引数はfloat型の変数、もくしはリテラルだと、初心者は考えます。
実はfloat型の式であれば何でもいいです。ちょっと例を見てみます。
echo sqrt(1 + 1 + 2); // 2
計算式です。1 + 1 + 2は4ですから、式の値は4になり、2となります。
echo sqrt($a = $b = $c = $d = 4); // 2
代入式です。4回も代入しています、複雑な説明は省きますが、直感どおりすべて4が代入されますので、2になります。
echo sqrt(sqrt(sqrt(sqrt(65536))));
なんどもsqrt()を実行していますが、難しいことはなく () の中の式 sqrt(sqrt(sqrt(65536)))
は4になるので2になります。
echo sqrt(is_null(null) * 4);
最後の例はかなり悪いですが is_null(null)
はtrueなので1のみなされて4をかけると4になります。
こんなように関数の引数としても柔軟に式が使えることがわかると思います。
制御構造も式でできている
それでは最初のコードに戻ります
$price = 1000; // 一つの文
if($price > 100) {
$price_with_tax = 1000 * 1.1; // これも計算と代入をする文
echo "税込み $price_with_tax 円です";
}
if文が入っていますね。
if文って()の中の条件を満たしたら{}の中の処理を実行する
というように漠然と理解してないでしょうか?
しかしここまで呼んでいただけれたならピンと来ると思います。
$price > 100
とはなんでしょうか?
そうです。式です。
if()やwhile()の中に入るのは式でったのです。条件式と呼んだりしますが、特殊な式ではなく今まで解説してきた式であれば何でも入ります。
if( $condition /* 条件式 */ ) {
/* 条件式がtrueとみなされればここに来る */
}
もちろんよくある無限ループの以下のようなコードも
while(true) {
/* ループの中の処理 */
}
trueは常にtureになる式なので、無限ループをします。
よくあるファイル読み込みのコードでも、なぜ()の中に = が入っているのか理解できると思います。
while( $text = fgets($fh) ) {
echo $text;
}
こんなやつですね。 $text = fgets($fh)
は式ですが、fgets()は
- ファイルが読み込めるときは読み込んだテキストを返す(ture)
- ファイルが終端に達するとfalseを返す
という関数なので、このコードできれいにファイルをすべて出力してくれるわけです。
以下のようなif文の中で代入をするコードも理解できると思います。
if($resource = getResouce()) {
/* リソースに追加なにか処理 */
$resource->someAction();
}
個人的にはあまり好きな書き方ではないですが なにかをgetできた場合を処理をして、できなかった場合をスルーというときによく書かれるこういうコードも、 条件
ではなく 値をもった条件式
であると理解できればすんなり読めると思います。
式についての重要なまとめ
なんがたわからないコードがでてきたら式だと考えると理解できる(ことがある)
- 条件文の中になんだか複雑なコードがあってわからない
- メソッド呼び出しをするときに
()
の中に長いコートが書いてあってわからない - 何かを代入しているみたいだけど
=
の右側がすんごく長くて難しい
こんなときは、ひとつひとつの式を読み解いていくと、理解が捗ると思います。多分。
言語構造
言語構造ってなんですか?
説明が難しいのですが、最初のコード
$price = 1000;
if($price > 100) {
$price_with_tax = 1000 * 1.1;
echo "税込み $price_with_tax 円です";
}
なぜifで条件分岐できるかというと「そういう言語構造だから」という説明になります。
functionやclassも言語構造です。こればかりはifはこうなる、functionはこうなる、と、一つ一つ理解してもらうしかありません。
ただし、多くのPHPの言語構造というのは
keword {
doSomething1();
doSomething1();
}
というようになにかしらのキーワードをとって後ろに {}
のブロックがきます。
はい、これが最後の重要ワード、ブロックです。
ブロックってなんですか?
ブロックは複数の文のまとまりです。
ひとつのブロックはひとつの文であると考えることもできます。
つまりひとつの文の中に複数の文がネストされて入っているという考え方です。
これが理解できるとコードは文の塊であると理解できます。
ブロックは制御構造でも使いますが、関数やクラスの定義でもよく使います。
function calc_area( int $width, int $height ) : int {
$area = $width * $height;
return $area;
}
こんな簡単な関数はありませんが、何十行になっても関数はひとつのブロックです。
class Person {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function sayHello() {
$message = "こんにちは、私は {$this->name} です";
return $message;
}
}
クラスの場合はクラスの中でメソッドが定義されることがほとんどですので、ブロックの中にまたブロックが存在することになります。
このクラスもまた一つのブロックに過ぎません。
色々な言語構造
くわしく掘り下げるとキリがありませんが、他にも言語構造はPHPにたくさん存在しています。
歴史が長いため、なんでそんなものが…みたいなものも多いですが、最もよく使う言語構造はechoやrequireでしょうか。
echo "こんにちは!";
echoを関数だと勘違いしている人が多いですが、関数であるなら上記のコードはエラーになります。
echo("こんにちは!");
と書かなくてはいけません。実際には()
がなくても動作するので、これは実は言語構造です。
似たような関数っぽい言語構造に return
require
include
exit
die
などがありますが、きりがないのでここまでとしたいと思います。
終わりに
別にPHPに限った話じゃないものが多々混じってるようにも思いますが、今はPHPについてフォーカスしたくてこのような書き方になりました。
他の言語にも通じるところがあるので、ある程度参考にしていただければ幸いです。
また、あくまで初心者向けのテキストなので、厳密には間違っていることでもある程度四捨五入した書き方をしている点には、ご容赦いただきたいです。
最後に私は経歴だけは長い職業プログラマーですが、基本的に独学で仕事を始めた身なので、学のある方からすると至らない点はあるかと思います。お気づきの点がありましたら、コメントなどで教えていただけると幸いです。
読んでいただき、ありがとございました。