【PHP入門講座】 想定外の入力への対応

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

# 想定外の入力でエラーが発生するケース

まずは名前を「test」として、 `welcome.php` を実行しましょう。現在アドレスバーに表示されているURLは

`http://localhost/welcome.php?my_name=test`

という形になっていると思います。今回はここから色々いじってみます。

## 1. `welcome.php` に直接アクセスされたとき

### 問題の発生

アドレスバーのURLを `http://localhost/welcome.php` にして直接アクセスしてみましょう。

```
こんにちは、
Notice: Undefined index: my_name in C:\xampp\htdocs\welcome.php on line 20
さん！
```

という **Notice(通知)** が発生するはずです。これは7行目、

```html+php
<p>こんにちは、<?=h($_GET['my_name'])?>さん！</p>
```

において、 **`$_GET['my_name']` が未定義(Undefined)だよ！** という意味のエラーです。URLからパラメータ `my_name` を削ってしまったので、この変数が存在しなくなってしまったのです。

### 解決策

**`isset`** 構文を使って、 `$_GET['my_name']` が定義されているかどうか確認します。ここで `if` `else` 文が初登場しますが、

```php
if (/* 条件式 */) {
    // 条件式が成立するとき
} else {
    // 条件式が成立しないとき
}
```

というように条件分岐させるためのものだと思ってください。

以下のように `welcome.php` の書き換えを行ってください。

```html+php
<?php

/**
 * HTML特殊文字をエスケープする関数
 */
function h($str) {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

// $_GET['my_name'] が定義されているかどうか調べる
if (isset($_GET['my_name'])) {
    // 定義されていればそのまま$my_nameに代入
    $my_name = $_GET['my_name'];
} else {
    // 未定義ならば「名無し」として設定
    $my_name = '名無し';
}

// Content-Typeヘッダー送信
header('Content-Type: application/xhtml+xml; charset=utf-8');

?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja">
<head>
  <title>Hello!!</title>
</head>
<body>
  <p>こんにちは、<?=h($my_name)?>さん！</p>
</body>
</html>
```

### 確認

もう一度直接アクセスを行い、「こんにちは、名無しさん！」と表示されれば成功です。

## 2. `welcome.php` に `my_name` が配列として渡されたとき

### 問題の発生

アドレスバーのURLを `http://localhost/welcome.php?my_name[a]=b` にして直接アクセスしてみましょう。

```
こんにちは、
Warning: htmlspecialchars() expects parameter 1 to be string, array given in C:\xampp\htdocs\welcome.php on line 7
さん！
```

という **Warning(警告)** が発生するはずです。これは20行目、

```php
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
```

において、 **第1引数 `$str` が文字列じゃないといけないのに配列が渡されたよ！** という意味のエラーです。配列に関しては後で詳しく学習しますが、イメージとしては最初述べたように箱に例えるならば **「重箱」** に相当します。この例では、

```php
$str['a'] = 'b';
```

のような形で受け取ってしまっており、 **「str」という箱の中にある「a」という箱の中の値が「b」** という状況を示しています。 `htmlspecialchars` 関数は配列を処理することが出来ないので、このエラーを発生しています。

### 解決策

**`is_string`** 関数を使って、 `$_GET['my_name']` が **文字列(String)** になっているかどうか確認します。

ここで論理演算子 `&&` が初登場しますが、日本語に直訳すると **「且つ」** を意味します。

以下のように `welcome.php` の書き換えを行ってください。

```html+php
<?php

/**
 * HTML特殊文字をエスケープする関数
 */
function h($str) {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

// $_GET['my_name'] が定義されており、且つ文字列であるかどうか調べる
if (isset($_GET['my_name']) && is_string($_GET['my_name'])) {
    // 定義されていて文字列であればそのまま$my_nameに代入
    $my_name = $_GET['my_name'];
} else {
    // 未定義か配列ならば「名無し」として設定
    $my_name = '名無し';
}

// Content-Typeヘッダー送信
header('Content-Type: application/xhtml+xml; charset=utf-8');

?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja">
<head>
  <title>Hello!!</title>
</head>
<body>
  <p>こんにちは、<?=h($my_name)?>さん！</p>
</body>
</html>
```

### 確認

もう一度直接アクセスを行い、「こんにちは、名無しさん！」と表示されれば成功です。

# エラーは発生しないが想定外の入力として処理したいケース

## 3. 名前が空欄で送信されたとき

### 問題の発生

`form.php` から名前が空欄のまま送信してみましょう。

```
こんにちは、さん！
```

このときにもぜひ「名無しさん」と表示させたいですよね。

### 解決策

**`!==`** 演算子を使って、 `$_GET['my_name']` が **空欄(空文字列)** になって<ins>いない</ins>かどうか確認します。

以下のように `welcome.php` の書き換えを行ってください。

```html+php
<?php

/**
 * HTML特殊文字をエスケープする関数
 */
function h($str) {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

if (
    isset($_GET['my_name']) && // $_GET['my_name'] が定義されており、
    is_string($_GET['my_name']) && // 且つ文字列であり、
    $_GET['my_name'] !== '' // 且つ空欄でないかどうか調べる
) {
    // 条件をすべて満たせばそのまま$my_nameに代入
    $my_name = $_GET['my_name'];
} else {
    // 1つでも満たさないものがあれば「名無し」として設定
    $my_name = '名無し';
}

// Content-Typeヘッダー送信
header('Content-Type: application/xhtml+xml; charset=utf-8');

?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja">
<head>
  <title>Hello!!</title>
</head>
<body>
  <p>こんにちは、<?=h($my_name)?>さん！</p>
</body>
</html>
```

### 確認

もう一度空欄にして送信を行い、「こんにちは、名無しさん！」と表示されれば成功です。