お前は PHP 7 における Fatal Error / Catchable Fatal Error / Error / ErrorException / Exception の違いを言えるか？

# 導入

煽りタイトルすいません．PHP 7 のエラーや標準例外について，違いを整理し，使い分けを考察する記事です．

【引用】 [PHP 7.0.0α2 の例外の例外の継承関係を可視化してみた](http://qiita.com/ngyuki/items/8b78e6b19ab8778a1af6) by @ngyuki 

![7e7426e0-d147-1443-980b-3a9e5ae1e68f.png](https://qiita-image-store.s3.amazonaws.com/0/25060/cb5867f8-d7f5-143d-2312-a3fa17b1ba33.png)

# すべてを捕捉対象にする [`Throwable`]

[`Throwable`] は， PHP 7 の `throw` 文でスロー可能なあらゆるオブジェクトが実装する基底インターフェイスです． [`Error`] および [`Exception`] はこれを実装しています．但し，**PHPコードとして書かれるクラスが [`Throwable`] インターフェイスを直接実装することはできません．**

とりあえず `catch` 句に [`Throwable`] と書いておけば，そのブロック内でスローされた [`Error`] と [`Exception`] を捕捉できることが保証されます．

```php
try {
   ...
} catch (\Throwable $e) {
   ...
}
```

但し， [`Error`] を捕捉する必要性があるのは，**「フレームワークを自作していて，エラー時にもHTMLレンダリングを可能な限り行いたい」**など，極めて限定的な場合です．もしリソースの解放などを確実に行いたい場合には `finally` 句を使うのが正解です．

```php:△
try {
    ...   
} catch (\Throwable $e) {
    unlink('/tmp/tmp.txt');
    throw $e;
}
```

```php:◯
try {
    ...
} finally {
    unlink('/tmp/tmp.txt');
}
```

# エラー / [`Exception`] / [`Error`] / [`ErrorException`]

以上4点の違いや使い分けについて言及します．

## 旧来のエラー

【引用】 [$_GET, $_POSTなどを受け取る際の処理](http://qiita.com/mpyw/items/2f9955db1c02eeef43ea)

> PHPには主に以下のようなエラーがあります． (これで全てではありません)
>
> ### **E_PARSE** (別名: Parse Error, Syntax Error, 文法エラー)
>
> コードが文法的に誤っているときに発生します．一切の処理を行いません．
>
> ### **E_ERROR** (別名: Fatal Error, 致命的なエラー)
> 
> 処理を継続することが不可能になってしまった場合に発生します．発生したところで処理を停止します．これが発生するリスクを持つコードを書いてはいけません．
>
> ### **E_WARNING** (別名: Warning, 警告)
> 
> 処理を継続することは可能ですが，想定外の問題が起こったときに発生します．これが発生するリスクを持つコードは可能な限り書かないでおくべきです．
> 
> ### **E_NOTICE** (別名: Notice, 通知)
> 
> 処理を継続することは可能ですが，想定内の問題が起こったときに発生します．これは手を抜いて無視する人と丁寧に対応する人に二分されますが，<ins>対応の仕方を知らないまま手抜きをするようなプログラマになってはならない</ins>と私は考えるので，ここでは徹底的に対応することにします．

このようなエラーは，**PHP処理系内部**で発生します．困ったことに，後述する特殊な変換処理を設定しないかぎりは，catch 句で捕捉することができません．**「旧来のエラー」は [`Throwable`] ではありません．**本来ならば全てのエラーは [`Throwable`] のもとに体系化されるべきなのですが，これは例外が実装されていなかった時代の名残です．

**PHP処理系が持つ標準関数との互換性を強く意識したPHPコード**では，「旧来のエラー」を [`trigger_error`] 関数を用いてPHPレベルで能動的に発生させることもあります．

- [`password_hash` 関数などをPHP5.4以前でも使えるようにするコード](https://github.com/ircmaxell/password_compat/blob/master/lib/password.php)
- [`array_column` 関数をPHP5.4以前でも使えるようにするコード](https://github.com/ramsey/array_column/blob/master/src/array_column.php)

## [`Exception`]

[`Error`] や [`Throwable`] が実装される前の PHP 5 時代には， [`Throwable`] の代わりにすべての頂点にいたクラスです． [`Throwable`] の実装には [`Error`] と [`Exception`] の2種類がありますが，能動的にスローする際には，**普通のPHPコードとして書かれるものはすべて [`Exception`] あるいはその継承クラスを使うべき**です．

## [`Error`]

PHP 7 から新設されたクラスです． [`Error`] というクラス名になっていますが，「旧来のエラー」とは取り扱いが全く異なるものです． [`Exception`] とは対極に， **PHP処理系内部**あるいは**PHP処理系が持つ標準関数との互換性を強く意識したPHPコード**で用いられます． PHP 7 以降は [`trigger_error`] 関数との使い分けに悩む気もしますが， PHP 5 時代の実装のバックポートには [`trigger_error`] 関数が， PHP 7 時代の実装のバックポートには [`Error`] が使われるでしょう．

## [`ErrorException`]

[`set_error_handler`] 関数と併用して，先述の「特殊な変換処理」で変換するためだけに存在するクラスです．非常に名前がややこしいですが， [`ErrorException`] はあくまで [`Exception`] の一部であって， PHP 7 から新設された [`Error`] とは無関係です． [`ErrorException`] は**「旧来のエラーから変換される [`Exception`] 」**という意味です．

【引用】 [`set_error_handler` と `ErrorException` を組み合わせると幸せになるかも](http://qiita.com/suin/items/14c455691a0391ee265c#comment-8e8b9ed10262e1213ecc)

> ```php
set_error_handler(function($severity, $message, $file, $line) {
    throw new ErrorException($message, 0, $severity, $file, $line);
});
```

但し，ここで設定したハンドラは Fatal Error を受け取ることができず，どうしてもそれに対処したい場合は [`register_shutdown_function`] を使って「最後の悪あがき」をする程度しか方法がありません．

```php:
register_shutdown_function(function () {
   $error = error_get_last();
   if (!$error || !($error['type'] & (E_ERROR | E_PARSE | E_USER_ERROR | E_RECOVERABLE_ERROR))) {
        return;
   } 
   /* 最後の悪あがきをする */
});
```

**※ `E_RECOVERABLE_ERROR` (別名 Catchable Fatal Error) は [`ErrorException`] に変換可能な `E_ERROR` 相当のもので， [`set_error_handler`] での設定が無い場合には `E_ERROR` と同様の扱いになります．**PHP 5.2 から一部の `E_ERROR` が `E_RECOVERABLE_ERROR` に変更されています．

※ PHP 7 では `E_WARNING` `E_ERROR` `E_RECOVERABLE_ERROR` `E_PARSE` の一部が [`Error`] に変更されています．

## 要約すると…

### [`set_error_handler`] なしの場合

- そのまま続行可能な旧来のエラー<br> → `E_NOTICE` `E_WARNING` `E_USER_NOTICE` `E_USER_WARNING`
- 実行が停止する旧来のエラー<br> → `E_ERROR` `E_PARSE` `E_RECOVERABLE_ERROR` `E_USER_ERROR`
- `catch` 句で捕捉できるもの<br> → [`Error`] &nbsp;[`Exception`]

### [`set_error_handler`] ありの場合

- 実行が停止する旧来のエラー<br> → `E_ERROR` `E_PARSE` `E_USER_ERROR`
- `catch` 句で捕捉できるもの<br> → [`Error`] &nbsp;[`Exception`] <br>( [`Exception`] には `E_NOTICE` `E_WARNING` `E_USER_NOTICE`<br>`E_RECOVERABLE_ERROR` 変換後の [`ErrorException`] が含まれる)

# [`LogicException`] / [`RuntimeException`]

PHPプログラマ向けに提供されている [`Exception`] の継承クラスとして， [`LogicException`] と [`RuntimeException`] があります．いろいろな記事で何回も言われていることですが，以下のような使い分けを行うべきです．

- **[`LogicException`]** … 自身のコード，あるいはそれを利用している第三者のコードにバグがあることを示す例外．
- **[`RuntimeException`]** … 正しく取り扱っていても起こりうる例外．

以下では，これらを継承しているクラスの使い分けについて言及します．

## [`BadFunctionCallException`] / [`BadMethodCallException`]

これは説明するほどでもないですね．

| クラス名 | 継承元 | 利用シーン|
|:--|:--|:--|
|[`BadFunctionCallException`]|[`LogicException`]|関数が未実装である場合，<br>あるいは内部状態的に実行してはいけないタイミングで実行された場合の例外．| 
|[`BadMethodCallException`]|[`LogicException`]|メソッドが未実装である場合，<br>あるいは内部状態的に実行してはいけないタイミングで実行された場合の例外．| 

```php:例&#58;&#32;シリアライズを防ぐ
class Klass
{
    public function __sleep()
    {
        throw new \BadMethodCallException('serialization is not supported');
    }
}

serialize(new Klass); // BadMethodCallException
```

## [`OverflowException`] / [`UnderflowException`]

マニュアルによれば，コンテナの「オーバーフロー」「アンダーフロー」にそのまま対応するようです．数値計算のオーバーフローがこれに当てはまるかは…どうだろう？

| クラス名 | 継承元 | 利用シーン|
|:--|:--|:--|
|[`OverflowException`]|[`RuntimeException`]|コンテナのオーバーフロー時の例外．| 
|[`UnderflowException`]|[`RuntimeException`]|コンテナのアンダーフロー時の例外．| 

```php:例&#58;&#32;スタックの実装例
class Stack
{
    private $elements = [];
    private $size;

    public function __construct(int $size)
    {
        if ($size < 1) {
            throw new \LengthException("\$size must be positive integer, $size given");
        }
        $this->size = $size;
    }

    public function push($value)
    {
        if (count($elements) >= $size) {
            throw new \OverflowException('container is full');
        }
        $this->elements[] = $value;
    }

    public function pop()
    {
        if (empty($this->elements)) {
            throw new \UnderflowException('container is empty');
        }
        return array_pop($this->elements);
    }
}
```

## [`DomainException`] / [`InvalidArgumentException`] / [`LengthException`] / [`OutOfRangeException`] / [`OutOfBoundsException`] / [`RangeException`] / [`UnexpectedValueException`]

さぁ，見るからにここが一番混沌としています．正直なところ，今から書く説明があっているかどうかすら怪しいです…

[`LogicException`] と [`RuntimeException`] の分類ではなく，例外の内容で分類することにします．

### 引数や返り値のバリデーションを手広く行うもの

| クラス名 | 継承元 | 利用シーン|
|:--|:--|:--|
|[`DomainException`]|[`LogicException`]|型は合っているが，想定している<br>いくつかの取りうる値のどれにも該当しなかった場合の例外．<br>自分を含め，[`InvalidArgumentException`]と一緒くたに<br>されている気がするけど，もうちょっと使ってあげよう．| 
|[`RangeException`]|[`RuntimeException`]|型は合っているが，想定している<br>いくつかの取りうる値のどれにも該当しなかった場合の例外．<br>[`DomainException`]の実行時版．| 
|[`InvalidArgumentException`]|[`LogicException`]|引数の型が間違っている場合の例外．<br>スカラータイプヒントおよび[`TypeError`]が<br>PHP 7 から導入されたので，今後出番は減っていきそう…|
|[`UnexpectedValueException`]|[`RuntimeException`]|返り値の型が想定外であった場合の例外．<br>引数はコンパイル時エラーでだいたい検出可能，<br>返り値は実行時でなければ検出できない<br>…というスタンスで[`InvalidArgumentException`]と対になるのだろうか？

### コンテナへのアクセスのバリデーションを行うもの

| クラス名 | 継承元 | 利用シーン|
|:--|:--|:--|
|[`OutOfRangeException`]|[`LogicException`]|固定長であるコンテナのオフセットを無視したアクセスがあった場合の例外．<br>専ら[`SplFixedArray`]，あるいはそれを踏襲した実装のために存在するようなもの．|
|[`OutOfBoundsException`]|[`RuntimeException`]|可変長であるコンテナのオフセット，<br>またはインデックスを無視したアクセスがあった場合の例外．<br>マニュアルによると[`DomainException`]の実行時版であるらしいが，<br>個人的には[`OutOfRangeException`]の実行時版のように思える．|

### 分類しづらいもの

| クラス名 | 継承元 | 利用シーン|
|:--|:--|:--|
|[`LengthException`]|[`LogicException`]|専ら「長さ」「大きさ」を表す自然数を<br>バリデーションしたいときに使いそう．| 

# おまけ: バージョン別・エラーや例外の具体例

PHP 5.0 ~ 5.1 の検証は省略し，手元にある 5.5.34 および 7.0.9 で検証することにします．

```php:マークダウン自動生成コード
<?php

function test($version, $code, $strict = false)
{
    $trapped_code = ($strict ? 'declare(strict_types=1);' : '') . '
        function preg_grep_keys($pattern, $input, $flags = 0) {
            return array_intersect_key(
                $input,
                array_flip(preg_grep($pattern, array_keys($input), $flags))
            );
        }
        set_error_handler(function ($errno, $errstr) use (&$cancel) {
            $cancel = true;
            $consts = array_flip(preg_grep_keys("/\AE_/", get_defined_constants()));
            echo json_encode([
                "type" => "`{$consts[$errno]}`",
                "message" => $errstr,
            ]);
            exit;
        });
        register_shutdown_function(function () use (&$cancel) {
            if ($cancel || !$e = error_get_last()) return;
            $consts = array_flip(preg_grep_keys("/\AE_/", get_defined_constants()));
            echo json_encode([
                "type" => "`{$consts[$e["type"]]}`",
                "message" => $e["message"],
            ]);
        });
        try {
            ' . $code . '
        } catch (\Exception $e) {
            $cancel = true;
            echo json_encode([
                "type" => "[`" . get_class($e) ."`]",
                "message" => $e->getMessage(),
            ]);
        } catch (\Throwable $e) {
            $cancel = true;
            echo json_encode([
                "type" => "[`" . get_class($e) ."`]",
                "message" => $e->getMessage(),
            ]);
        }
    ';
    $error = json_decode(shell_exec(implode(' ', [
        $version < 7 ? '/usr/bin/php' : '/usr/local/bin/php',
        '-d error_reporting=-1',
        '-d display_errors=0',
        '-d log_errors=0',
        '-r ' . escapeshellarg($trapped_code),
    ])));
    return $error;
}

function escape_ascii($str)
{
    return preg_replace_callback('/[\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]/', function ($m) {
        return '&#' . ord($m[0]) . ';';
    }, $str);
}

function build(array $items)
{
    $tables = [
        '|No.|概要|PHP 5.5.34|PHP 7.0.9| PHP 7.0.9 (strict)|',
        '|:--|:--|:--|:--|:--|',
    ];
    $codes = [];
    $i = 0;
    foreach ($items as $name => $code) {
        $code = trim($code);
        $err5 = test(5, $code);
        $err7 = test(7, $code);
        $err7s = test(7, $code, true);
        $tables[] = sprintf('|%s|%s|%s|%s|%s|',
            ++$i,
            $name,
            $err5->type ?? '',
            $err7->type ?? '',
            $err7s->type ?? ''
        );
        $codes[] = implode("\n", [
            '```php:' . escape_ascii("No.$i $name"),
            $code,
            '/*',
            'PHP 5.5.34        : ' . ($err5->message ?? ''),
            'PHP 7.0.9         : ' . ($err7->message ?? ''),
            'PHP 7.0.9 (strict): ' . ($err7s->message ?? ''),
            '*/',
            '```'
        ]);
    }
    return implode("\n", $tables) . "\n\n" . implode("\n\n", $codes) . "\n";
}

echo build([

'未定義の変数の評価'
=> '
$undefined . "";
',

'未定義のグローバル定数の評価'
=> '
UNDEFINED . "";
',

'未定義の配列オフセットの評価'
=> '
$ary = [];
$ary[0] . "";
',

'未定義の連想配列インデックスの評価'
=> '
$asc = [];
$asc["undefined"] . "";
',

'未定義のインスタンスプロパティの評価'
=> '
$obj = new stdClass;
$obj->undefined . "";
',

'未定義の静的プロパティの評価'
=> '
$obj = new stdClass;
$obj::$undefined . "";
',

'未定義のクラス定数の評価'
=> '
$obj = new stdClass;
$obj::UNDEFINED . "";
',

'クラス経由でのインスタンスプロパティへのアクセス'
=> '
class Klass
{
    public $prop;
}
Klass::$prop;
',

'アクセス権限のないプロパティへのアクセス'
=> '
class Klass
{
    private static $prop;
}
Klass::$prop;
',

'ゼロ除算'
=> '
1 / 0;
',

'ゼロ剰余算'
=> '
1 % 0;
',

'マイナスのビットシフト'
=> '
1 << -1;
',

'配列と文字列の結合' => '
$ary = [];
$ary . ""; // PHP 7 のコンパイル時エラー回避
',


'配列変数と整数の加算' => '
$ary = [];
$ary + 1; // PHP 7 のコンパイル時エラー回避
',

'配列リテラルと整数の加算' => '
eval("[] + 1;"); // PHP 7 のコンパイル時エラー回避
',


'配列のオフセットの型間違い' =>'
$ary = [];
$ary[[]] . "";
',

'__toString()未実装時の文字列キャスト' =>'
$obj = new stdClass;
(string)$obj;
',

'__toString()の中でスローされる例外' =>'
class Klass
{
    public function __toString()
    {
        throw new \Exception;
    }
}
$obj = new Klass;
(string)$obj;
',

'__clone()の直接実行' =>'
class Klass
{
    public function __clone()
    {
        return new self;
    }
}
$obj = new Klass;
eval("\$obj->__clone();");// PHP 5 のコンパイル時エラー回避
',

'ArrayAccess未実装時の配列形式アクセス' =>'
$obj = new stdClass;
$obj[0] = 1;
',

'ユーザ定義関数のタイプヒンティングの無視' => '
function func(stdClass $obj) { }
func(1);
',

'ユーザ定義関数の引数不足' => '
function func($a) { }
func();
',

'組み込み関数のタイプ指定無視' => '
strlen([]);
',

'組み込み関数の引数不足' => '
strlen();
',

'組み込み関数の引数余剰' => '
strlen("", "");
',

'アサーションの失敗 (assert.exception=0)' => '
assert(false);
',

'アサーションの失敗 (assert.exception=1)' => '
ini_set("assert.exception", "1");
assert(false);
',

'foreachの型間違い' => '
$num = 1;
foreach ($num as $k => $v);
',

]);
```

|No.|概要|PHP 5.5.34|PHP 7.0.9| PHP 7.0.9 (strict)|
|:--|:--|:--|:--|:--|
|1|未定義の変数の評価|`E_NOTICE`|`E_NOTICE`|`E_NOTICE`|
|2|未定義のグローバル定数の評価|`E_NOTICE`|`E_NOTICE`|`E_NOTICE`|
|3|未定義の配列オフセットの評価|`E_NOTICE`|`E_NOTICE`|`E_NOTICE`|
|4|未定義の連想配列インデックスの評価|`E_NOTICE`|`E_NOTICE`|`E_NOTICE`|
|5|未定義のインスタンスプロパティの評価|`E_NOTICE`|`E_NOTICE`|`E_NOTICE`|
|6|未定義の静的プロパティの評価|`E_ERROR`|[`Error`]|[`Error`]|
|7|未定義のクラス定数の評価|`E_ERROR`|[`Error`]|[`Error`]|
|8|クラス経由でのインスタンスプロパティへのアクセス|`E_ERROR`|[`Error`]|[`Error`]|
|9|アクセス権限のないプロパティへのアクセス|`E_ERROR`|[`Error`]|[`Error`]|
|10|ゼロ除算|`E_WARNING`|`E_WARNING`|`E_WARNING`|
|11|ゼロ剰余算|`E_WARNING`|[`DivisionByZeroError`]|[`DivisionByZeroError`]|
|12|マイナスのビットシフト||[`ArithmeticError`]|[`ArithmeticError`]|
|13|配列と文字列の結合|`E_NOTICE`|`E_NOTICE`|`E_NOTICE`|
|14|配列変数と整数の加算|`E_ERROR`|[`Error`]|[`Error`]|
|15|配列リテラルと整数の加算|`E_ERROR`|`E_ERROR`|`E_ERROR`|
|16|配列のオフセットの型間違い|`E_WARNING`|`E_WARNING`|`E_WARNING`|
|17|__toString()未実装時の文字列キャスト|`E_RECOVERABLE_ERROR`|`E_RECOVERABLE_ERROR`|`E_RECOVERABLE_ERROR`|
|18|__toString()の中でスローされる例外|`E_ERROR`|`E_ERROR`|`E_ERROR`|
|19|__clone()の直接実行|`E_COMPILE_ERROR`|||
|20|ArrayAccess未実装時の配列形式アクセス|`E_ERROR`|[`Error`]|[`Error`]|
|21|ユーザ定義関数のタイプヒンティングの無視|`E_RECOVERABLE_ERROR`|[`TypeError`]|[`TypeError`]|
|22|ユーザ定義関数の引数不足|`E_WARNING`|`E_WARNING`|`E_WARNING`|
|23|組み込み関数のタイプ指定無視|`E_WARNING`|`E_WARNING`|[`TypeError`]|
|24|組み込み関数の引数不足|`E_WARNING`|`E_WARNING`|[`TypeError`]|
|25|組み込み関数の引数余剰|`E_WARNING`|`E_WARNING`|[`TypeError`]|
|26|アサーションの失敗 (assert.exception=0)|`E_WARNING`|`E_WARNING`|`E_WARNING`|
|27|アサーションの失敗 (assert.exception=1)|`E_WARNING`|[`AssertionError`]|[`AssertionError`]|
|28|foreachの型間違い|`E_WARNING`|`E_WARNING`|`E_WARNING`|

```php:No&#46;1&#32;未定義の変数の評価
$undefined . "";
/*
PHP 5.5.34        : Undefined variable: undefined
PHP 7.0.9         : Undefined variable: undefined
PHP 7.0.9 (strict): Undefined variable: undefined
*/
```

```php:No&#46;2&#32;未定義のグローバル定数の評価
UNDEFINED . "";
/*
PHP 5.5.34        : Use of undefined constant UNDEFINED - assumed 'UNDEFINED'
PHP 7.0.9         : Use of undefined constant UNDEFINED - assumed 'UNDEFINED'
PHP 7.0.9 (strict): Use of undefined constant UNDEFINED - assumed 'UNDEFINED'
*/
```

```php:No&#46;3&#32;未定義の配列オフセットの評価
$ary = [];
$ary[0] . "";
/*
PHP 5.5.34        : Undefined offset: 0
PHP 7.0.9         : Undefined offset: 0
PHP 7.0.9 (strict): Undefined offset: 0
*/
```

```php:No&#46;4&#32;未定義の連想配列インデックスの評価
$asc = [];
$asc["undefined"] . "";
/*
PHP 5.5.34        : Undefined index: undefined
PHP 7.0.9         : Undefined index: undefined
PHP 7.0.9 (strict): Undefined index: undefined
*/
```

```php:No&#46;5&#32;未定義のインスタンスプロパティの評価
$obj = new stdClass;
$obj->undefined . "";
/*
PHP 5.5.34        : Undefined property: stdClass::$undefined
PHP 7.0.9         : Undefined property: stdClass::$undefined
PHP 7.0.9 (strict): Undefined property: stdClass::$undefined
*/
```

```php:No&#46;6&#32;未定義の静的プロパティの評価
$obj = new stdClass;
$obj::$undefined . "";
/*
PHP 5.5.34        : Access to undeclared static property: stdClass::$undefined
PHP 7.0.9         : Access to undeclared static property: stdClass::$undefined
PHP 7.0.9 (strict): Access to undeclared static property: stdClass::$undefined
*/
```

```php:No&#46;7&#32;未定義のクラス定数の評価
$obj = new stdClass;
$obj::UNDEFINED . "";
/*
PHP 5.5.34        : Undefined class constant 'UNDEFINED'
PHP 7.0.9         : Undefined class constant 'UNDEFINED'
PHP 7.0.9 (strict): Undefined class constant 'UNDEFINED'
*/
```

```php:No&#46;8&#32;クラス経由でのインスタンスプロパティへのアクセス
class Klass
{
    public $prop;
}
Klass::$prop;
/*
PHP 5.5.34        : Access to undeclared static property: Klass::$prop
PHP 7.0.9         : Access to undeclared static property: Klass::$prop
PHP 7.0.9 (strict): Access to undeclared static property: Klass::$prop
*/
```

```php:No&#46;9&#32;アクセス権限のないプロパティへのアクセス
class Klass
{
    private static $prop;
}
Klass::$prop;
/*
PHP 5.5.34        : Cannot access private property Klass::$prop
PHP 7.0.9         : Cannot access private property Klass::$prop
PHP 7.0.9 (strict): Cannot access private property Klass::$prop
*/
```

```php:No&#46;10&#32;ゼロ除算
1 / 0;
/*
PHP 5.5.34        : Division by zero
PHP 7.0.9         : Division by zero
PHP 7.0.9 (strict): Division by zero
*/
```

```php:No&#46;11&#32;ゼロ剰余算
1 % 0;
/*
PHP 5.5.34        : Division by zero
PHP 7.0.9         : Modulo by zero
PHP 7.0.9 (strict): Modulo by zero
*/
```

```php:No&#46;12&#32;マイナスのビットシフト
1 << -1;
/*
PHP 5.5.34        : 
PHP 7.0.9         : Bit shift by negative number
PHP 7.0.9 (strict): Bit shift by negative number
*/
```

```php:No&#46;13&#32;配列と文字列の結合
$ary = [];
$ary . ""; // PHP 7 のコンパイル時エラー回避
/*
PHP 5.5.34        : Array to string conversion
PHP 7.0.9         : Array to string conversion
PHP 7.0.9 (strict): Array to string conversion
*/
```

```php:No&#46;14&#32;配列変数と整数の加算
$ary = [];
$ary + 1; // PHP 7 のコンパイル時エラー回避
/*
PHP 5.5.34        : Unsupported operand types
PHP 7.0.9         : Unsupported operand types
PHP 7.0.9 (strict): Unsupported operand types
*/
```

```php:No&#46;15&#32;配列リテラルと整数の加算
eval("[] + 1;"); // PHP 7 のコンパイル時エラー回避
/*
PHP 5.5.34        : Unsupported operand types
PHP 7.0.9         : Unsupported operand types
PHP 7.0.9 (strict): Unsupported operand types
*/
```

```php:No&#46;16&#32;配列のオフセットの型間違い
$ary = [];
$ary[[]] . "";
/*
PHP 5.5.34        : Illegal offset type
PHP 7.0.9         : Illegal offset type
PHP 7.0.9 (strict): Illegal offset type
*/
```

```php:No&#46;17&#32;&#95;&#95;toString&#40;&#41;未実装時の文字列キャスト
$obj = new stdClass;
(string)$obj;
/*
PHP 5.5.34        : Object of class stdClass could not be converted to string
PHP 7.0.9         : Object of class stdClass could not be converted to string
PHP 7.0.9 (strict): Object of class stdClass could not be converted to string
*/
```

```php:No&#46;18&#32;&#95;&#95;toString&#40;&#41;の中でスローされる例外
class Klass
{
    public function __toString()
    {
        throw new \Exception;
    }
}
$obj = new Klass;
(string)$obj;
/*
PHP 5.5.34        : Method Klass::__toString() must not throw an exception
PHP 7.0.9         : Method Klass::__toString() must not throw an exception, caught Exception: 
PHP 7.0.9 (strict): Method Klass::__toString() must not throw an exception, caught Exception: 
*/
```

```php:No&#46;19&#32;&#95;&#95;clone&#40;&#41;の直接実行
class Klass
{
    public function __clone()
    {
        return new self;
    }
}
$obj = new Klass;
eval("\$obj->__clone();");// PHP 5 のコンパイル時エラー回避
/*
PHP 5.5.34        : Cannot call __clone() method on objects - use 'clone $obj' instead
PHP 7.0.9         : 
PHP 7.0.9 (strict): 
*/
```

```php:No&#46;20&#32;ArrayAccess未実装時の配列形式アクセス
$obj = new stdClass;
$obj[0] = 1;
/*
PHP 5.5.34        : Cannot use object of type stdClass as array
PHP 7.0.9         : Cannot use object of type stdClass as array
PHP 7.0.9 (strict): Cannot use object of type stdClass as array
*/
```

```php:No&#46;21&#32;ユーザ定義関数のタイプヒンティングの無視
function func(stdClass $obj) { }
func(1);
/*
PHP 5.5.34        : Argument 1 passed to func() must be an instance of stdClass, integer given, called in Command line code on line 27 and defined
PHP 7.0.9         : Argument 1 passed to func() must be an instance of stdClass, integer given, called in Command line code on line 27
PHP 7.0.9 (strict): Argument 1 passed to func() must be an instance of stdClass, integer given, called in Command line code on line 27
*/
```

```php:No&#46;22&#32;ユーザ定義関数の引数不足
function func($a) { }
func();
/*
PHP 5.5.34        : Missing argument 1 for func(), called in Command line code on line 27 and defined
PHP 7.0.9         : Missing argument 1 for func(), called in Command line code on line 27 and defined
PHP 7.0.9 (strict): Missing argument 1 for func(), called in Command line code on line 27 and defined
*/
```

```php:No&#46;23&#32;組み込み関数のタイプ指定無視
strlen([]);
/*
PHP 5.5.34        : strlen() expects parameter 1 to be string, array given
PHP 7.0.9         : strlen() expects parameter 1 to be string, array given
PHP 7.0.9 (strict): strlen() expects parameter 1 to be string, array given
*/
```

```php:No&#46;24&#32;組み込み関数の引数不足
strlen();
/*
PHP 5.5.34        : strlen() expects exactly 1 parameter, 0 given
PHP 7.0.9         : strlen() expects exactly 1 parameter, 0 given
PHP 7.0.9 (strict): strlen() expects exactly 1 parameter, 0 given
*/
```

```php:No&#46;25&#32;組み込み関数の引数余剰
strlen("", "");
/*
PHP 5.5.34        : strlen() expects exactly 1 parameter, 2 given
PHP 7.0.9         : strlen() expects exactly 1 parameter, 2 given
PHP 7.0.9 (strict): strlen() expects exactly 1 parameter, 2 given
*/
```

```php:No&#46;26&#32;アサーションの失敗&#32;&#40;assert&#46;exception&#61;0&#41;
assert(false);
/*
PHP 5.5.34        : assert(): Assertion failed
PHP 7.0.9         : assert(): assert(false) failed
PHP 7.0.9 (strict): assert(): assert(false) failed
*/
```

```php:No&#46;27&#32;アサーションの失敗&#32;&#40;assert&#46;exception&#61;1&#41;
ini_set("assert.exception", "1");
assert(false);
/*
PHP 5.5.34        : assert(): Assertion failed
PHP 7.0.9         : assert(false)
PHP 7.0.9 (strict): assert(false)
*/
```

```php:No&#46;28&#32;foreachの型間違い
$num = 1;
foreach ($num as $k => $v);
/*
PHP 5.5.34        : Invalid argument supplied for foreach()
PHP 7.0.9         : Invalid argument supplied for foreach()
PHP 7.0.9 (strict): Invalid argument supplied for foreach()
*/
```

[`SplFixedArray`]: http://www.php.net/manual/ja/class.splfixedarray.php
[`register_shutdown_function`]: http://php.net/manual/ja/function.register-shutdown-function.php
[`set_error_handler`]: http://php.net/manual/ja/function.set-error-handler.php
[`trigger_error`]: http://php.net/manual/ja/function.trigger-error.php
[`Throwable`]: http://www.php.net/manual/ja/class.throwable.php
[`Exception`]: http://www.php.net/manual/ja/class.exception.php
[`ErrorException`]: http://www.php.net/manual/ja/class.errorexception.php
[`Error`]: http://www.php.net/manual/ja/class.error.php
[`ArithmeticError`]: http://www.php.net/manual/ja/class.arithmeticerror.php
[`AssertionError`]: http://www.php.net/manual/ja/class.assertionerror.php
[`DivisionByZeroError`]: http://www.php.net/manual/ja/class.divisionbyzeroerror.php
[`ParseError`]: http://www.php.net/manual/ja/class.parseerror.php
[`TypeError`]: http://www.php.net/manual/ja/class.typeerror.php
[`BadFunctionCallException`]: http://www.php.net/manual/ja/class.badfunctioncallexception.php
[`BadMethodCallException`]: http://www.php.net/manual/ja/class.badmethodcallexception.php
[`DomainException`]: http://www.php.net/manual/ja/class.domainexception.php
[`InvalidArgumentException`]: http://www.php.net/manual/ja/class.invalidargumentexception.php
[`LengthException`]: http://www.php.net/manual/ja/class.lengthexception.php
[`LogicException`]: http://www.php.net/manual/ja/class.logicexception.php
[`OutOfBoundsException`]: http://www.php.net/manual/ja/class.outofboundsexception.php
[`OutOfRangeException`]: http://www.php.net/manual/ja/class.outofrangeexception.php
[`OverflowException`]: http://www.php.net/manual/ja/class.overflowexception.php
[`RangeException`]: http://www.php.net/manual/ja/class.rangeexception.php
[`RuntimeException`]: http://www.php.net/manual/ja/class.runtimeexception.php
[`UnderflowException`]: http://www.php.net/manual/ja/class.underflowexception.php
[`UnexpectedValueException`]: http://www.php.net/manual/ja/class.unexpectedvalueexception.php