1. ホーム
  2. PHP

【PHP】演算子の使い方|算術・比較(==と===の違い)・論理・代入演算子を解説

Share

PHP でコードを書いていると、足し算や掛け算はもちろん、値を比較したり、条件を組み合わせたりと、さまざまな場面で「演算子」を使います。演算子とは、値や変数に対して何らかの計算・処理を行う記号のことです。数が多く似た記号も多いため、特に ===== のように「どちらを使えばよいか分からない」と迷いやすいものもあります。この記事では、算術・代入・文字列連結・比較・論理・三項・インクリメントといった演算子を、コーディング初心者〜中級者の方に向けて、php.net の仕様に沿って整理して解説します。特に比較演算子は、PHP 8 で挙動が変わった点も含めて丁寧に見ていきます。

まずは算術演算子|四則演算とべき乗・剰余

もっとも基本となるのが、足し算・引き算・掛け算・割り算といった算術演算子です。PHP では、加算 +、減算 -、乗算 *、除算 / に加えて、剰余(割った余り)を求める %、べき乗を求める ** が用意されています。次のコードで、それぞれの結果を確認してみましょう。

operator.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 / 32 ではなく 2.333… になるのはこのためです。また、剰余 % は内部でオペランドを整数に変換してから計算するため、小数を渡すと小数部分が切り捨てられます。

「割り算の商を整数で得たい」というときは、演算子ではなく intdiv() 関数を使うと分かりやすく書けます。intdiv(7, 3)2 を返します(小数部分を切り捨てた整数除算)。なお、%intdiv() で 0 による除算を行うと DivisionByZeroError が投げられる点も覚えておくとよいでしょう。

intdiv.php
<?php
echo intdiv(7, 3); // 2(商を整数で得る)
echo 7 / 3;        // 2.3333333333333(こちらは小数)

代入演算子と複合代入|= だけではない

変数に値を入れる = が、もっとも基本的な代入演算子です。$x = 5; のように、右辺の値を左辺の変数に代入します。ここで大切なのは、= は「等しい」という意味ではなく「代入する」という意味だということです。「等しいか」を調べるのは、後で出てくる比較演算子 == の役割です。この混同はバグの原因になりやすいので、はっきり区別しておきましょう。

そして、既存の変数に対して「今の値をもとに計算し直して代入する」場面はとても多くあります。そのための書き方が複合代入演算子です。たとえば $x += 3;$x = $x + 3; と同じ意味で、同じ変数を2回書かずに済みます。次のコードで、代表的な複合代入の動きを確認します。

assign.php
<?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' のように文字列に + を使うと、数値としての加算とみなされて意図しない結果になります。文字列をつなぐときは必ず . を使いましょう。

concat.php
<?php
$first = '田中';
$last  = '太郎';

// . で連結する
$name = $first . $last;   // '田中太郎'
echo $name;

// .= で末尾に追記していく
$message = 'こんにちは、';
$message .= $name;
$message .= 'さん';
echo $message;            // こんにちは、田中太郎さん

ループの中で文字列を少しずつ組み立てていくような場面では、.= がよく使われます。前の $greeting .= ', World'; の例と同じく、「今ある文字列の後ろに追加する」という動きです。

比較演算子|== と === の違いを理解する

ここからが、多くの人がつまずく比較演算子です。まず押さえたいのが ==(等価)と ===(厳密等価)の違いです。== は「値が等しいか」だけを見ます。このとき、両辺の型が違えば PHP が自動的に型をそろえてから比較します。この自動変換を「型ジャグリング(type juggling)」と呼びます。一方 === は「値だけでなく型も同じか」を見るため、型変換は行われません。

compare.php
<?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() の戻り値チェック)は、=== でないと 0false を取り違えるバグになります。基本は === を使い、型変換を意図するときだけ == を選ぶ、と考えておくとよいでしょう。

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'truefalse
0 == ''truefalse
0 == '0'truetrue
'abc' == 0truefalse
123 == '123'truetrue

この変更は「数として比べられない文字列は、うっかり 0 と等しくならないようにする」という、より安全な方向への修正です。とはいえ、そもそも型変換に頼らず === を使っていれば、こうしたバージョン差の影響は受けません。ここでも「基本は ===」という指針が役立ちます。

その他の比較演算子と宇宙船演算子

等価以外にも、大小や不一致を調べる演算子がひととおりそろっています。それぞれの意味を次の表にまとめます。

演算子意味
==等価(型変換して値が等しい)
===厳密等価(値も型も等しい)
!= / <>非等価(値が等しくない)。この2つは同じ意味
!==厳密非等価(値または型が異なる)
<より小さい
>より大きい
<=以下
>=以上
<=>宇宙船演算子。左が小さければ -1、等しければ 0、大きければ 1 を返す

非等価の !=<> はまったく同じ意味で、どちらを書いても構いません。!===== の否定で、「値または型のどちらかが違えば true」になります。

少し特殊なのが宇宙船演算子 <=>(PHP 7.0 で追加)です。これは true/false ではなく整数を返し、左辺が右辺より小さければ -1、等しければ 0、大きければ 1 になります。usort() のような並び替えの比較関数を書くときに重宝します。

spaceship.php
<?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 にはこれとは別に、単語で書く andorxor もありますが、後述するように優先順位が異なるため注意が必要です。まずは意味を整理します。

演算子意味
$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 は、判定結果こそ && / || と同じですが、演算子の優先順位が異なります。具体的には、andor は代入演算子 = よりも優先順位が低いのに対し、&&||= より高くなっています。この差が、次のようなバグを生みます。

precedence.php
<?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 文より短く書けます。条件 ? 真のときの値 : 偽のときの値 という形で、条件の真偽に応じてどちらかの値を返します。

ternary.php
<?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 を足す」という違いです。

increment.php
<?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 の優先順位の違いに気を付け、順序に迷ったら括弧で明示する、という姿勢を持っておくと安心です。

参考ページ