【PHP入門講座】 演算子

### [目次に戻る](http://qiita.com/mpyw/items/752e19a578cf7d96fc5f)

今まで演算子 `+` や `=` などの説明をすっ飛ばしてきましたが、ここでようやく詳しく学習することになります。以下、演算子の<ins>優先度の高い順</ins>に紹介していきます。「優先度」とは、四則演算で言うならば乗除算が加減算より優先されるといったイメージです。

今回はボリュームぎっしりなので、一気に覚えようとはせずに徐々に触れながら覚えていく程度に思ってください。演算子絡みのよく分からない挙動・エラーに遭遇したらこのページに戻ってくればいいでしょう。

#  1. `clone` `new` (最優先)

**※ この項目には未学習の内容が含まれています。**

## 2-1. `clone`

オブジェクトのクローンを生成して返します。

```php
$b = clone $a;
```

## 2-2. `new`

オブジェクトのインスタンスを生成して返します。

```php
$a = new A();
```

# 2. `array()` `[ ]`

## 2-1. `array()`

- 配列を生成して返します。

## 2-2. `[ ]`

- 配列を生成して返します。(PHP5.4以降)
- 配列のキーを指定します。

# 3. `++` `--` `~` `@` `(型)`

## 3-1. `++`

**「インクリメント」** と呼ばれます。 

### 3-1-1. 数値

<ins>変数を</ins> **+1** する操作を行います。値に対しては使えません。

```php
$i = 0;
$j = 0;
$i = $i + 1;
echo $i;
echo $j;
$j = $j + 1;
```

これは下記のように簡略化することが出来ます。

```php
$i = 0;
$j = 0;
echo ++$i;
echo $j++;
```

<ins>前につけるのと後ろにつけるのでは解釈が異なる</ins>ので注意してください。

**前につけた場合**

1. インクリメントが行われる
1. 操作の **後** の値が返される

**後ろにつけた場合**

1. 操作の **前** の値が返される
1. インクリメントが行われる

### 3-1-2. `TRUE` `FALSE` 配列 オブジェクト リソース

<ins>何も起きません。</ins>

### 3-1-3. `NULL`

`0` と見なされた後インクリメントされて `1` になります。

### 3-1-4. 数字以外の文字列

```php:PHPコード
$s = 'X';
$a = array();
$a[] = ++$s;
$a[] = ++$s;
$a[] = ++$s;
$a[] = ++$s;
var_dump($a);
```

```text:実行結果
array(4) {
  [0]=>
  string(1) "Y"
  [1]=>
  string(1) "Z"
  [2]=>
  string(1) "AA"
  [3]=>
  string(2) "AB"
}
```

### 3-1-5. 数字とアルファベットで構成される文字列

```php:PHPコード
$s = 'A8';
$a = array();
$a[] = ++$s;
$a[] = ++$s;
$a[] = ++$s;
var_dump($a);
```

```text:実行結果
array(3) {
  [0]=>
  string(2) "A9"
  [1]=>
  string(2) "B0"
  [2]=>
  string(2) "B1"
}
```

### 3-1-6. 【要注意】 数値と "判断される" 文字列

数値と判断された文字列はインクリメントされると、<ins>自動的に数値に変換されます</ins>。「浮動小数点数」の説明で **科学記法** について言及しましたが、実は文字列上でも該当してしまうので注意してください。この判断基準に関しては、 **「緩やかな比較と厳密な比較」** で詳しく学習します。

```php:PHPコード
$s = '01';
$t = '1D8';
$a = array();
$a[] = ++$s;
$a[] = ++$t;
$a[] = ++$t;
$a[] = ++$t;
var_dump($a);
```

```text:実行結果
array(4) {
  [0]=>
  int(2)
  [1]=>
  string(3) "1D9"
  [2]=>
  string(3) "1E0"
  [3]=>
  float(2)
}
```

## 3-2. `--`

**「デクリメント」** と呼ばれます。

### 3-2-1. 数値

<ins>変数を</ins> **-1** する操作を行います。値に対しては使えません。

### 3-2-2. `TRUE` `FALSE` `NULL` 配列 オブジェクト リソース

何も起きません。こちらには <ins>`NULL` も含まれている</ins>ことに注意してください。

### 3-2-3. 数値と判断されない文字列

何も起きません。

### 3-2-4. 【要注意】 数値と判断される文字列

自動的に数値に変換されます。

## 3-3. `~`

ビット演算での **否定** を表します。

### 3-3-1. 数値

通常のビット反転が行われます。

### 3-3-2. 文字列

<ins>バイト単位</ins>でのビット反転が行われます。

### 3-3-3. それ以外

**Fatal Error** が発生します。

## 3-4. `@`

**エラー制御演算子** と呼ばれます。これをつけた部分とそれより内側で発生した全てのエラーを非表示にすることが出来ます。但し、<ins>処理速度が大幅に低下する</ins>のでこれに頼るのはやめましょう。以下に例を示します。このコードはセキュリティ的に問題があるので絶対に使わないでください。

```php:パラメータが未定義か配列である、またはファイルが存在しなかったとしてもエラーが発生しません
$filename = @$_GET['filename'];
$fp = @fopen($filename, 'r');
```

```php:@演算子の効力は内側まで続くので、このように演算子1つでまとめることも出来ます
$fp = @fopen($_GET['filename'], 'r');
```

上記はチェックをサボりまくった例ですが、関数の実装上やむを得ない場合もあるので一概に「使うな」とは言えません。

## 3-5. `(型)` 

強制的に型変換を実行する **キャスト演算子** です。

# 4. `instanceof`

**※ この項目には未学習の内容が含まれています。**

あるオブジェクトがあるクラスのインスタンスかどうかを調べる演算子です。 **型演算子** と呼ばれます。論理値 `TRUE` `FALSE` のどちらかが返されます。

```php
$obj = new stdClass;
$classname = 'stdClass';
$v1 = $obj instanceof stdClass;   // TRUE
$v2 = $obj instanceof $classname; // TRUE
$v3 = $obj instanceof 'stdClass'; // 文法エラー
```

# 5. `!`

値を<ins>論理型に変換した後</ins>、 **否定(反転)** したものを返します。

```php
$v1 = !0;    // TRUE
$v2 = !$v1;  // FALSE
$v3 = !!$v1; // TRUE
```

# 6. `*` `/` `%`

## 6-1.  `*` `/` `%`

**乗算** と **除算** と **剰余算** を行います。結果の型は下記の条件に従います。

#### 整数

- 剰余算の場合は無条件で整数
- 乗算か除算の場合は2つの数が整数であるとき
- 且つ、除算の場合は割り切れるとき

#### 浮動小数点数

- 上記以外のとき

但し、ゼロで割ると **Warning** が発生して `FALSE` になります。

### 6-1-1. 数値

通常の演算が行われます。

### 6-1-2. 文字列 `TRUE` `FALSE` `NULL` リソース

数値に変換されます。

### 6-1-3. 配列 オブジェクト

**Fatal Error** が発生します。

# 7. `+` `-` `.`

## 7-1. `+` `-` (四則演算) 
**加算** と **減算** を行います。結果の型は乗除算と同様の法則に従います。

### 7-1-1.  文字列 `TRUE` `FALSE` `NULL` リソース

数値に変換されます。

### 7-1-2. 配列(減算のみ)・オブジェクト

**Fatal Error** が発生します。

## 7-2. `+` (配列演算)

### 【要注意】

これは、<ins>四則演算とは全く別の特別なもの</ins>として考えてください。

- どちらが一方でも配列でなければ **Fatal Error** を発生します。
- 2つの配列を<ins>キーはそのままにして</ins>結合します。
- <ins>先に存在していたキーが優先</ins>されます。

```php:PHPコード
$a = array(0 => 'A', 1 => 'B');
$b = array(1 => 'C', 2 => 'D');
var_dump($a + $b);
```

```text:実行結果
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "D"
}
```

これは `array_merge` 関数とは全く異なる挙動であり、この違いを理解しておく必要があります。詳しくは後ほど。

## 7-3. `.`

**文字列の結合** を行います。 Javaなどのノリで `+` 演算子を使うと文字列の結合ではなく整数変換後の **加算** が行われてしまうので注意してください。PHPにおける演算子の正しい使い方を覚えましょう。

### 7-3-1. 数値 `TRUE` `FALSE` `NULL` リソース

文字列に変換されます。

### 7-3-1. 配列 

`"Array"` に変換されますが、 **Notice** を発生します。

### 7-3-2. オブジェクト

**※ この項目には未学習の内容が含まれています。**

- マジックメソッド `__toString` が実装されている場合、その返り値となります。
- 実装されていない場合は **Catchable Fatal Error** が発生します。

## 7-4. よくあるミス

`+` `-` `.` の優先度は等しく、左から順に評価されていくので、下記のコードは意図したとおりに動きません。

```php:PHPコード
echo 'one plus three equals ' . 1 + 3 . ' !!'; 
```

`'one plus three equals 1'` という文字列に `3` が足される演算が行われてしまい、この文字列を整数に変換すると `0` になってしまうため、下記のような結果になります。

```text:実行結果
3 !!
```

これを避けたければ、

```php
echo 'one plus three equals ' . (1 + 3) . ' !!'; 
```

として先に足し算を行うことを明示するか、今回のケースでは `echo` の<ins>複数パラメータを渡す</ins>構文を用いて

```php
echo 'one plus three equals ', 1 + 3, ' !!'; 
```

とすることも出来ます。後者は

```php
echo 'one plus three equals ';
echo 1 + 3;
echo ' !!'; 
```

と全く同じことを意味します。

# 8. `<<` `>>`

**ビットシフト** を行います。全ての値は<ins>整数型</ins>に変換されます。

#　9. `<` `<=` `>` `>=`<br />10. `==` `!=` `===` `!==` `<>`<br />

- 2つの値を比較し、結果を論理値 `TRUE` `FALSE` のどちらかで返します。
- 比較規則の詳細に関しては **「緩やかな比較と厳密な比較」** で触れることにします。

# 11. `&`<br />12. `^`<br />13. `|`<br />

**ビット演算** を行います。全ての値は<ins>整数型</ins>に変換されます。

# 14. `&&` `||`

これらは **論理演算子** と呼ばれます。全ての値は<ins>論理型</ins>に変換されます。

## 14-1. `&&`

**「且つ」** を表します。

## 14-2. `||`

**「又は」** を表します。

## 14-3. 副作用完了点について

ビット演算子と論理演算子の違いについて考えてみましょう。ビット演算で **論理値(整数の `0` `1` )** のみを扱うケースを考えます。

```php:PHPコード
$a = false;
$b = true;
var_dump(
    $a & $b,
    $a && $b
);
```

```text:実行結果
int(0)
bool(false)
```

論理値としてはどちらも `FALSE` になるので、どちらを用いてもいいと思われるかもしれませんが、これは **副作用** が無い場合に限定されます。

**【副作用】**
式が条件判定に使われるだけでなく、<ins>変数の値の変更</ins>や<ins>標準出力</ins>が起きること。

例えば下記のケースでは実行結果が異なります。

```php:PHPコード
$i = array(0, 0, 0, 0);
var_dump(
    ++$i[0] | ++$i[1],
    ++$i[2] || ++$i[3],
    $i
);
```

```text:実行結果
int(1)
bool(true)
array(4) {
  [0]=>
  int(1)
  [1]=>
  int(1)
  [2]=>
  int(1)
  [3]=>
  int(0)
}
```

`$i[1]` と `$i[3]` の値が異なっていますね。この現象について考察してみましょう。`|` と `||` はどちらも左側から評価される点に着目します。

```php
++$i[0] | ++$i[1]
```

ビット演算子を用いたケースでは、如何なる場合においても `++$i[0]` と `++$i[1]` は実行されます。ところが…

```php
++$i[2] || ++$i[3]
```

これは `++$i[2]` が `1` となり、この<ins>式全体が `TRUE` となることが確定</ins>したので、 `++$i[3]` の評価は行われなかったのです。一方、`&&` の場合は<ins>式全体が `FALSE` となることが確定</ins>した時点で評価は終了します。

これらの評価が終了する点のことを **副作用完了点** と呼びます。今まで何気なく書いてきたif文も、この性質を盛んに利用しています。典型的な例が `isset` におけるチェックです。

```php
if (isset($_GET['a']) && is_string($_GET['a'])) {
    echo htmlspecialchars($_GET['a'], ENT_QUOTES, 'UTF-8');
}
```

もしこれが

```php
if (isset($_GET['a']) & is_string($_GET['a'])) {
    echo htmlspecialchars($_GET['a'], ENT_QUOTES, 'UTF-8');
}
```

であったとしたら、変数が未定義の場合でも `is_string` が実行され、 **Notice** が発生してしまうことになるでしょう。

# 15. `?` `:`

```php
$a = isset($_GET['a']) && is_string($_GET['a']) ? $_GET['a'] : '';
```

**三項演算子** と呼ばれます。 if～else文における<ins>同一の変数に対する代入</ins>を簡略化する役割を持ちます。詳しくは「制御構造と関数」にて扱いますが、形式だけ軽く紹介しておきます。

### 条件式 `?` 条件式がTRUEのときの値 `:` 条件式がFALSEのときの値

# 16. `=` `+=` `-=` `*=` `/=` `.=` `%=` `&=` `|=` `^=` `<<=` `>>=` `=>`

これらは **代入演算子** と呼ばれます。

## 16-1. `=`

最も基本的な代入演算子です。 <ins>右から左</ins>への代入を行います。代入演算子は、代入を行った後に<ins>式として代入した値を返す</ins>特性があります。

```php:PHPコード
var_dump($v[] = 'A', $v);
```

`$v[] = 'A'` は代入した `"A"` を返しています。

```text:実行結果
string(1) "A"
array(1) {
  [0]=>
  string(1) "A"
}
```

これを利用して、コードを短く出来ることがあります。例えばこんなケース。

#### 例1： 一斉に代入

```php:Before
$a = 0;
$b = 0;
$c = 0;
```

```php:After
$a = $b = $c = 0;
```

#### 例2： チェック中に代入

```php:Before
if (isset($_GET['a'])) {
    $a = $_GET['a'];
    if (is_string($a)) {
        echo htmlspecialchars($a, ENT_QUOTES, 'UTF-8');
    }
}
```

```php:After
if (isset($_GET['a']) && is_string($a = $_GET['a'])) {
    echo htmlspecialchars($a, ENT_QUOTES, 'UTF-8');
}
```

また、 <ins>代入される変数に隣接する演算子の優先度が変化</ins>することがあります。これも同様に簡略化に役立つことがあります。

#### 例3： 代入結果の否定

`strlen` は文字列のバイト長を返す関数です。

**strlen**
http://php.net/manual/ja/function.strlen.php

```php:Before
$str = 'hogehoge';
if (!($length = strlen($str))) {
    echo "This string is empty";
} else {
    echo "This string has {$length} bytes.";
}
```

```php:After
$str = 'hogehoge';
if (!$length = strlen($str)) {
    echo "This string is empty";
} else {
    echo "This string has {$length} bytes.";
}
```



## 16-2. `+=` `-=` `*=` `/=` `.=` `%=` `&=` `|=` `^=` `<<=` `>>=`

これらは全て<ins>計算した結果をもう一度代入し直す</ins>という動作をします。

```php:PHPコード
$i = $j = $k = 0;
var_dump(
    $i = $i + 1,
    $j += 1,
    ++$k
);
```

上の3つの式は全て等価です。

```text:実行結果
int(1)
int(1)
int(1)
```

なお、`$k++` とするのは意味が異なるので注意してください。

## 16-3. `=>`

これは一応代入演算子には分類されますが、 **ダブルアロー演算子** の名前の方が有名です。配列の初期化代入にのみ使用され、他とは性質が大きく異なるので、どちらかと言えば<ins>言語構文</ins>に近いものとして認知すべきでしょう。

```php
$data = array(
    'a' => 'A',
    'b' => 'B',
);
```

# 17. `and`<br />18. `xor`<br />19. `or`

これらの役割はそれぞれ `&&` `^` `||` と全く同じですが、優先度が異なります。

### `&&` `^` `||` ＞ 代入演算子 ＞ `and` `xor` `or`

#### 例. ファイルポインタのオープン

`die` はスクリプトの実行を強制的に中断する言語構文です。下記の例ではファイルが開けなかったときにエラーメッセージを出して強制終了します。

```php
$fp = @fopen('data.txt', 'r') or die('Failed to open data.txt');
```

### どうして `^^` って存在しないの？

ここに目をつけてくれた人はなかなか鋭いですね。 `&&` 演算子や `||` 演算子は式の左側を見ただけで結果を決めることがあると先ほど紹介しましたが、それに倣って `^^` 演算子を実装するとしても **「どちらか一方が `TRUE` ならば～」** という条件になるので、<ins>両側を見ることが確定</ins>してしまっており、結果的にはビット演算の `^` で十分だと言えるのが要因です。

# 20. `,`

さまざまな利用法(マニュアルよりそのまま言葉引用)

演算子は以上です。お疲れ様でした、徐々に覚えていきましょう。次回は **「緩やかな比較と厳密な比較」** について学びます。