PHP でコードを書いていると、足し算や掛け算はもちろん、値を比較したり、条件を組み合わせたりと、さまざまな場面で「演算子」を使います。演算子とは、値や変数に対して何らかの計算・処理を行う記号のことです。数が多く似た記号も多いため、特に == と === のように「どちらを使えばよいか分からない」と迷いやすいものもあります。この記事では、算術・代入・文字列連結・比較・論理・三項・インクリメントといった演算子を、コーディング初心者〜中級者の方に向けて、php.net の仕様に沿って整理して解説します。特に比較演算子は、PHP 8 で挙動が変わった点も含めて丁寧に見ていきます。
目次
まずは算術演算子|四則演算とべき乗・剰余
もっとも基本となるのが、足し算・引き算・掛け算・割り算といった算術演算子です。PHP では、加算 +、減算 -、乗算 *、除算 / に加えて、剰余(割った余り)を求める %、べき乗を求める ** が用意されています。次のコードで、それぞれの結果を確認してみましょう。
<?php
echo 7 + 3; // 10
echo 7 - 3; // 4
echo 7 * 3; // 21
echo 7 / 3; // 2.3333333333333(小数になる)
echo 7 % 3; // 1(7 を 3 で割った余り)
echo 2 ** 10; // 1024(2 の 10 乗)
ここで注意したいのが除算 / です。PHP の / は、割り切れない場合でも整数ではなく浮動小数点数(小数)を返します。7 / 3 が 2 ではなく 2.333… になるのはこのためです。また、剰余 % は内部でオペランドを整数に変換してから計算するため、小数を渡すと小数部分が切り捨てられます。
「割り算の商を整数で得たい」というときは、演算子ではなく intdiv() 関数を使うと分かりやすく書けます。intdiv(7, 3) は 2 を返します(小数部分を切り捨てた整数除算)。なお、% や intdiv() で 0 による除算を行うと DivisionByZeroError が投げられる点も覚えておくとよいでしょう。
<?php
echo intdiv(7, 3); // 2(商を整数で得る)
echo 7 / 3; // 2.3333333333333(こちらは小数)
代入演算子と複合代入|= だけではない
変数に値を入れる = が、もっとも基本的な代入演算子です。$x = 5; のように、右辺の値を左辺の変数に代入します。ここで大切なのは、= は「等しい」という意味ではなく「代入する」という意味だということです。「等しいか」を調べるのは、後で出てくる比較演算子 == の役割です。この混同はバグの原因になりやすいので、はっきり区別しておきましょう。
そして、既存の変数に対して「今の値をもとに計算し直して代入する」場面はとても多くあります。そのための書き方が複合代入演算子です。たとえば $x += 3; は $x = $x + 3; と同じ意味で、同じ変数を2回書かずに済みます。次のコードで、代表的な複合代入の動きを確認します。
<?php
$x = 10;
$x += 5; // $x = $x + 5 と同じ → 15
$x -= 3; // 12
$x *= 2; // 24
$x /= 4; // 6
$x %= 4; // 2(6 を 4 で割った余り)
echo $x; // 2
$greeting = 'Hello';
$greeting .= ', World'; // 文字列を後ろに連結 → 'Hello, World'
echo $greeting; // Hello, World
それぞれの複合代入が「どの演算をしてから代入するか」を、次の表に整理します。基本の算術演算子や文字列連結に対応した省略形になっている、と捉えると覚えやすいです。
| 演算子 | 意味($a に対して) |
|---|---|
+= | $a = $a + …(加算して代入) |
-= | $a = $a - …(減算して代入) |
*= | $a = $a * …(乗算して代入) |
/= | $a = $a / …(除算して代入) |
%= | $a = $a % …(剰余を代入) |
.= | $a = $a . …(文字列を連結して代入) |
??= | $a が未定義か null のときだけ右辺を代入(null合体代入) |
末尾の ??= は「null合体代入演算子」と呼ばれ、PHP 7.4 で追加されました。$config['color'] ??= 'blue'; のように書くと、$config['color'] が未定義または null のときだけ 'blue' を代入し、すでに値が入っていれば何もしません。デフォルト値をまとめて設定したいときに便利です。?? 自体の詳しい挙動は、後述の三項演算子の項でも軽く触れます。
文字列の連結は . (ドット)で行う
PHP で文字列同士をつなげるときは、+ ではなくドット . を使います。JavaScript などでは + で文字列連結をするため、他言語に慣れていると間違えやすいポイントです。PHP で 'a' + 'b' のように文字列に + を使うと、数値としての加算とみなされて意図しない結果になります。文字列をつなぐときは必ず . を使いましょう。
<?php
$first = '田中';
$last = '太郎';
// . で連結する
$name = $first . $last; // '田中太郎'
echo $name;
// .= で末尾に追記していく
$message = 'こんにちは、';
$message .= $name;
$message .= 'さん';
echo $message; // こんにちは、田中太郎さん
ループの中で文字列を少しずつ組み立てていくような場面では、.= がよく使われます。前の $greeting .= ', World'; の例と同じく、「今ある文字列の後ろに追加する」という動きです。
比較演算子|== と === の違いを理解する
ここからが、多くの人がつまずく比較演算子です。まず押さえたいのが ==(等価)と ===(厳密等価)の違いです。== は「値が等しいか」だけを見ます。このとき、両辺の型が違えば PHP が自動的に型をそろえてから比較します。この自動変換を「型ジャグリング(type juggling)」と呼びます。一方 === は「値だけでなく型も同じか」を見るため、型変換は行われません。
<?php
var_dump(1 == '1'); // bool(true) 値が等しい('1' が数値 1 に変換される)
var_dump(1 === '1'); // bool(false) 型が違う(int と string)ので false
var_dump(0 == '0'); // bool(true)
var_dump(true == 1); // bool(true) true は 1 とみなされる
var_dump(null == false); // bool(true)
var_dump(1 === 1); // bool(true) 値も型も同じ
このように、== は型が異なっても「変換して等しくなるなら true」と判定します。意図しない一致を避けたい場合は、型まで一致を求める === を使うのが安全です。特に、関数の戻り値が「値」か「false」かを区別したいとき(例: strpos() の戻り値チェック)は、=== でないと 0 と false を取り違えるバグになります。基本は === を使い、型変換を意図するときだけ == を選ぶ、と考えておくとよいでしょう。
PHP 8 で変わった「数値と文字列の比較」
== の型ジャグリングには、PHP 8.0 で重要な変更が入りました。PHP 7 以前は、数値と「数値でない文字列」を == で比較すると、数値のほうがなぜか文字列に合わせるのではなく、文字列が数値 0 に変換されて比較されていました。そのため、0 == 'foo' が true になるという直感に反する挙動がありました。
PHP 8.0 以降はこれが見直され、数値と非数値文字列('foo' のように数として解釈できない文字列)を比較するときは、数値のほうを文字列に変換して、文字列同士として比較するようになりました。その結果、0 == 'foo' は false になります。なお '123' のような「数値文字列」との比較はこれまでどおり数値として行われるため、0 == '0' や 123 == '123' は引き続き true です。バージョンによって結果が変わるので、この違いは表で確認しておきましょう。
| 比較式 | PHP 7 まで | PHP 8 以降 |
|---|---|---|
0 == 'foo' | true | false |
0 == '' | true | false |
0 == '0' | true | true |
'abc' == 0 | true | false |
123 == '123' | true | true |
この変更は「数として比べられない文字列は、うっかり 0 と等しくならないようにする」という、より安全な方向への修正です。とはいえ、そもそも型変換に頼らず === を使っていれば、こうしたバージョン差の影響は受けません。ここでも「基本は ===」という指針が役立ちます。
その他の比較演算子と宇宙船演算子
等価以外にも、大小や不一致を調べる演算子がひととおりそろっています。それぞれの意味を次の表にまとめます。
| 演算子 | 意味 |
|---|---|
== | 等価(型変換して値が等しい) |
=== | 厳密等価(値も型も等しい) |
!= / <> | 非等価(値が等しくない)。この2つは同じ意味 |
!== | 厳密非等価(値または型が異なる) |
< | より小さい |
> | より大きい |
<= | 以下 |
>= | 以上 |
<=> | 宇宙船演算子。左が小さければ -1、等しければ 0、大きければ 1 を返す |
非等価の != と <> はまったく同じ意味で、どちらを書いても構いません。!== は === の否定で、「値または型のどちらかが違えば true」になります。
少し特殊なのが宇宙船演算子 <=>(PHP 7.0 で追加)です。これは true/false ではなく整数を返し、左辺が右辺より小さければ -1、等しければ 0、大きければ 1 になります。usort() のような並び替えの比較関数を書くときに重宝します。
<?php
var_dump(1 <=> 2); // int(-1) 左が小さい
var_dump(2 <=> 2); // int(0) 等しい
var_dump(3 <=> 2); // int(1) 左が大きい
// 昇順ソートの比較関数として使える
$nums = [3, 1, 2];
usort($nums, fn($a, $b) => $a <=> $b);
print_r($nums); // [1, 2, 3]
論理演算子|条件を組み合わせる
複数の条件を組み合わせるときに使うのが論理演算子です。「かつ」を表す &&、「または」を表す ||、否定の ! がよく使われます。PHP にはこれとは別に、単語で書く and・or・xor もありますが、後述するように優先順位が異なるため注意が必要です。まずは意味を整理します。
| 演算子 | 意味 |
|---|---|
$a && $b | 論理積(AND)。両方 true なら true |
$a || $b | 論理和(OR)。どちらかが true なら true |
! $a | 否定(NOT)。true と false を反転する |
$a and $b | 論理積。&& と同じ判定だが優先順位が低い |
$a or $b | 論理和。|| と同じ判定だが優先順位が低い |
$a xor $b | 排他的論理和。どちらか一方だけ true なら true |
&& と || には「短絡評価(ショートサーキット)」という性質があります。&& は左辺が false なら右辺を評価せずに全体を false と決め、|| は左辺が true なら右辺を見ずに true とします。これを利用して $user && $user->isActive() のように「左が成立するときだけ右を実行する」書き方ができます。
&&/|| と and/or は優先順位が違う
and / or は、判定結果こそ && / || と同じですが、演算子の優先順位が異なります。具体的には、and と or は代入演算子 = よりも優先順位が低いのに対し、&& と || は = より高くなっています。この差が、次のようなバグを生みます。
<?php
// or は = より優先順位が低い
// → まず $result = true が実行され、そのあと or false が評価される
$result = true or false;
var_dump($result); // bool(true)
// || は = より優先順位が高い
// → まず (false || true) が評価され、その結果が代入される
$result = false || true;
var_dump($result); // bool(true)
// 意図が「false または true を代入」なら || を使う。
// and/or を代入と混ぜると想定外の結果になりやすい。
$result = true or false; は、= のほうが or より先に結び付くため、実際には ($result = true) or false; と解釈されます。つまり $result には true だけが入り、or false は結果に影響しません。こうした混乱を避けるため、通常の条件式では && と || を使い、and / or はあえて使わないのが無難です。
三項演算子で条件分岐を1行に
簡単な条件分岐は、三項演算子 ?: を使うと if 文より短く書けます。条件 ? 真のときの値 : 偽のときの値 という形で、条件の真偽に応じてどちらかの値を返します。
<?php
$age = 20;
$label = ($age >= 18) ? '大人' : '子ども';
echo $label; // 大人
// 省略形(Elvis 演算子): 真のときの値を省くと、
// 条件が真ならその値自体を、偽なら右辺を返す
$name = '';
$display = $name ?: 'ゲスト';
echo $display; // ゲスト($name が空文字=偽なので)
真ん中の値を省いた ?: は省略形(Elvis 演算子)と呼ばれ、$a ?: $b は「$a が真なら $a、偽なら $b」を返します。似たものに null合体演算子 ?? がありますが、?: が「値が偽かどうか」で判定するのに対し、?? は「値が null(または未定義)かどうか」だけで判定します。空文字や 0 をそのまま活かしたい場面では ?? が適しています。?? と isset()・empty() の使い分けは、別記事「PHP の isset・empty・null合体演算子(??)の違いと使い分け」で詳しく解説しているので、そちらを参照してください。
インクリメントとデクリメント|前置と後置
変数の値を 1 だけ増やす・減らすには、インクリメント ++ とデクリメント -- を使います。これらは変数の前に置くか後ろに置くかで、式全体としての「返す値」が変わります。前置(++$x)は「先に 1 を足してから、その値を返す」、後置($x++)は「今の値を返してから、あとで 1 を足す」という違いです。
<?php
$x = 5;
echo $x++; // 5 を出力(出力後に 6 になる)
echo $x; // 6
$y = 5;
echo ++$y; // 先に 6 にしてから出力 → 6
echo $y; // 6
単独の行で $x++; のように使う分には、前置でも後置でも結果は同じです。違いが表れるのは、echo $x++; のように「増減と同時にその値を使う」ときだけです。for ループのカウンタなど、値を使わずに増やすだけの場面では、どちらを書いても問題ありません。
演算子の優先順位に注意する
ここまで見てきたように、演算子には「どれを先に計算するか」という優先順位があります。算数と同じで、2 + 3 * 4 は掛け算が先に計算されて 14 になります。論理演算子でも && は || より優先順位が高い、といった順序が決まっています。優先順位をすべて暗記する必要はありません。意図した順序で計算させたいときは、丸括弧 () で明示的にくくるのが確実で、読み手にも伝わりやすいコードになります。迷ったら括弧、と覚えておきましょう。
まとめ
PHP の演算子を、算術・代入・文字列連結・比較・論理・三項・インクリメントの順に整理してきました。算術では除算 / が小数を返すこと、整数の商には intdiv() を使うこと、代入では += や .=、??= といった複合代入が便利なことを見ました。もっとも重要なのは比較演算子で、== は型変換して値を比べ、=== は型まで含めて比べます。意図しない一致を避けるため、基本は === を使うのが安全です。加えて、PHP 8.0 では数値と非数値文字列を == で比べたときの挙動が厳格化され、0 == 'foo' が false になった点も押さえておきましょう。論理演算子では &&/|| と and/or の優先順位の違いに気を付け、順序に迷ったら括弧で明示する、という姿勢を持っておくと安心です。