【PHP入門講座】 NULLと未定義の違い

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

# NULLと未定義

## NULL型

**NULL** という型があります。PHPでは `null` と書きます。大文字小文字は区別されません。以下のコードを実行してみましょう。

```php:PHPコード
<?php
$var = null;
var_dump($var);
```

```text:実行結果
NULL
```

`TRUE` `FALSE` `NULL` をまとめて **三値** と呼ぶことがありますが、これらを漢字一文字で表すと

- `TRUE` … 真
- `FALSE` … 偽
- **`NULL` … 無**

として解釈されます。命題「今日は学校に行く日か？」で例えると、

- `TRUE` …今日は学校に行く日である
- `FALSE` … 今日は休みの日である
- **`NULL` … そもそも中退してた**

のようなイメージです。

## 未定義(Undefined)

`NULL` は **無** と言いながらも…

> ```php:PHPコード
> <?php
> $var = null;
> var_dump($var);
> ```

確かにこの例では<ins>変数 `$var` は値 `NULL` を持つものとして存在</ins>していました。ここで別の例を考えてみましょう。

```php:PHPコード
<?php
var_dump($var);
```

```text:実行結果
PHP Notice:  Undefined variable: var in ...
NULL
```

**「変数varが未定義だよ！」** という **Notice** を発生しながらも、何故か `NULL` と表示されていますね。PHPでは、<ins>未定義の値を取得しようとしたときに、Noticeを発生しながら `NULL` を代わりに取得します</ins>。

また、配列において未定義のキーから値を取得しようとしたときにも、エラーメッセージは微妙に違いますが同様のNoticeが発生します。

```php:PHPコード
<?php
$var = array('A', 'B');
var_dump($var[2], $var['foo']);
```

```text:実行結果
PHP Notice:  Undefined offset: 2 in ...
NULL
PHP Notice:  Undefined index: foo in ...
NULL
```

PHPプログラマは、

- Noticeを気にせずに手抜きコーディングをする人
- **Noticeの発生に気を付けて丁寧にコーディングする人**

に大分されると思いますが、もちろんここでは後者を目指してコーディングを行っていきます。<ins>Noticeが発生するだけで処理速度が大幅に低下</ins>するので、これを潰すメリットはかなり大きいです。

# Noticeを出さずに未定義をチェックする方法

ここからは少し復習も入ります。

**【PHP入門講座】 想定外の入力への対応**
http://qiita.com/mpyw/items/f298828002bbc488fe89

こちらで既に少し触れていますが、 **`isset`** 構文を用いる方法があります。 `isset` は関数ではなく言語構文であるが故に、<ins>未定義の変数から値を取得しようとしてもNoticeを発生しない</ins>という強力な特長を持っています。

## `isset` が真を返す条件

### 通常の変数

- 変数が **定義** されている
- 且つ、 **`NULL` 以外** の値を取る

```php:PHPコード
<?php
$v2 = null;
$v3 = false;
var_dump(isset($v1), isset($v2), isset($v3));
```

```text:実行結果
bool(false)
bool(false)
bool(true)
```

### 配列のキー

- キーが **定義** されている
- 且つ、 **`NULL` 以外** の値を取る

```php:PHPコード
<?php
$v = array();
$v[1] = null;
$v[2] = false;
var_dump(isset($v[0]), isset($v[1]), isset($v[2]));
```

```text:実行結果
bool(false)
bool(false)
bool(true)
```

## `NULL` と未定義を区別する方法

まず真っ先に思いつくのは、値を取得しようとしてみてエラーが発生したかどうかをチェックする、という方法でしょう。しかし、これにはいろいろ制約が多く、現実的な方法ではありません。そこで別の方法を考えてみましょう。

### 配列におけるキーが存在するかどうか

**`array_key_exists`** 関数を利用します。これは、キーが定義されているときに必ず `TRUE` を返します。 <ins>`NULL` でも `TRUE` となります。</ins>これが `isset` 構文との大きな違いです。

**array_key_exists**
http://php.net/manual/ja/function.array-key-exists.php

```php:PHPコード
<?php
$v = array();
$v[1] = null;
$v[2] = false;
var_dump(
    array_key_exists(0, $v),
    array_key_exists(1, $v),
    array_key_exists(2, $v)
);
```

```text:実行結果
bool(false)
bool(true)
bool(true)
```

### グローバル空間における変数が存在するかどうか

**グローバル空間** とは、関数内ではない、要するに **一番外側の空間** を指します。今まで扱ってきた場所はほとんどグローバル空間です。グローバル空間にある変数は **グローバル変数** といいます。

PHPでは、グローバル空間にある変数を **スーパーグローバル変数 `$GLOBALS`** で配列として取得することが出来ます。スーパーグローバル変数に関しては後で詳しく学習するので、ここでは軽く流してください。

```php:PHPコード
<?php
$v2 = null;
$v3 = false;
var_dump(
    array_key_exists('v1', $GLOBALS),
    array_key_exists('v2', $GLOBALS),
    array_key_exists('v3', $GLOBALS)
);
```

```text:実行結果
bool(false)
bool(true)
bool(true)
```

### 関数内における変数が存在するかどうか

グローバル空間では変数 `$GLOBALS` が用意されていましたが、関数内では `get_defined_vars` 関数の返り値を利用します。

**get_defined_vars**
http://php.net/manual/ja/function.get-defined-vars.php

```php:PHPコード
<?php
function test() {
    $v2 = null;
    $v3 = false;
    var_dump(
        array_key_exists('v1', get_defined_vars()),
        array_key_exists('v2', get_defined_vars()),
        array_key_exists('v3', get_defined_vars())
    );
}
test();
```

```text:実行結果
bool(false)
bool(true)
bool(true)
```

## `NULL` と未定義を区別しようと思うな！！！

いろいろやってきましたが、正直 **面倒くさい** ですよね。普通に配列をチェックするだけなら素直に `array_key_exists` を使えばいいような気もしますが、<ins>動作速度は `isset` の方が速い</ins>です。

**【結論】 `NULL` と未定義の区別が必要なデータ構造は設計を誤っている。**