Codeigniter3.0のバリデーション一覧(途中)

#Codeigniter3.0.3のバリデーションルールまとめ

遅れて申し訳ありません。
申し訳ついでに、面白くないテーマですが、バリデーションルールについてまとめました。

## 基本ルール
「/system/libraries/Form_validation.php」
内にある、
「required」以下のメソッドに内容についてソース読みましょう、読みましょう。

### 調査対象
- Codeigniter3.0.3

### 基礎知識
バリデーションルールって、こんな感じで記述すると思います。

```exsample.php
	// 例１：一番単純な書き方 	
	// $_POST['first_name'] = 'tomcat';
	$this->form_validation->set_rules('first_name', 'lang:first_name', 'max_length[100]');
```
バリデーションルール名がそのままメソッド名となります。
フォームに入力された値が第一引数として渡ります。
[]に囲まれた部分（例1での100）が、バリデーションルールの第二引数として渡ります。

```Form_validation.php
	// 例1に対応したバリデーションルール
	// ここには $str = 'tomcat'; $val = '100'; が来ます。
	public function max_length($str, $val)
	{
		if ( ! is_numeric($val))
		{
			return FALSE;
		}

		return ($val >= mb_strlen($str));
	}
```

## 本編

### required

```Form_validation.php
	public function required($str)
	{
		return is_array($str) ? (bool) count($str) : (trim($str) !== '');
	}
```
- 要素があるかどうかをチェック
- 配列対応

### regex_match

```Form_validation.php
	public function regex_match($str, $regex)
	{
		return (bool) preg_match($regex, $str);
	}
```
正規表現を使用した、文字チェック
詳しくはPHPマニュアル参照のこと
http://php.net/manual/ja/function.preg-match.php

### matches

```Form_validation.php
	public function matches($str, $field)
	{
		return isset($this->_field_data[$field], $this->_field_data[$field]['postdata'])
			? ($str === $this->_field_data[$field]['postdata'])
			: FALSE;
	}
```
他のカラムと一致するかの確認
パスワードの確認、メールアドレスの確認なのでよく使う

### differs

```Form_validation.php
	public function differs($str, $field)
	{
		return ! (isset($this->_field_data[$field]) && $this->_field_data[$field]['postdata'] === $str);
	}
```
他のカラムと一致しない
パスワードの再設定とか？
matchesの反対

### is_unique

```Form_validation.php
	public function is_unique($str, $field)
	{
		sscanf($field, '%[^.].%[^.]', $table, $field);
		return isset($this->CI->db)
			? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)
			: FALSE;
	}
```
特定のテーブルの特定のカラムで、ユニークかどうか調べる
自分自身のレコードを除外できない、削除済レコードの除外等の小賢しい処理を挟めないので、実用性は・・・



### min_length

```Form_validation.php
	public function min_length($str, $val)
	{
		if ( ! is_numeric($val))
		{
			return FALSE;
		}

		return ($val <= mb_strlen($str));
	}
```
最低文字数
パスワードの設定などに

### max_length

```Form_validation.php
	public function max_length($str, $val)
	{
		if ( ! is_numeric($val))
		{
			return FALSE;
		}

		return ($val >= mb_strlen($str));
	}
```
最大文字数
input系ではほぼ必須？

### exact_length

```Form_validation.php

	public function exact_length($str, $val)
	{
		if ( ! is_numeric($val))
		{
			return FALSE;
		}

		return (mb_strlen($str) === (int) $val);
	}
```
指定文字数かどうか
郵便番号とか？長さが決まっているもの

### valid_url

```Form_validation.php

	public function valid_url($str)
	{
		if (empty($str))
		{
			return FALSE;
		}
		elseif (preg_match('/^(?:([^:]*)\:)?\/\/(.+)$/', $str, $matches))
		{
			if (empty($matches[2]))
			{
				return FALSE;
			}
			elseif ( ! in_array($matches[1], array('http', 'https'), TRUE))
			{
				return FALSE;
			}

			$str = $matches[2];
		}

		$str = 'http://'.$str;

		// There's a bug affecting PHP 5.2.13, 5.3.2 that considers the
		// underscore to be a valid hostname character instead of a dash.
		// Reference: https://bugs.php.net/bug.php?id=51192
		if (version_compare(PHP_VERSION, '5.2.13', '==') OR version_compare(PHP_VERSION, '5.3.2', '=='))
		{
			sscanf($str, 'http://%[^/]', $host);
			$str = substr_replace($str, strtr($host, array('_' => '-', '-' => '_')), 7, strlen($host));
		}

		return (filter_var($str, FILTER_VALIDATE_URL) !== FALSE);
	}
```

URLとして正しいかどうかチェックする
ここに来て、ちょっと面白いルールが出てきました。
まず、今回この記事を書くにあたって、注目した部分が以下の一文です。
`preg_match('/^(?:([^:]*)\:)?\/\/(.+)$/', $str, $matches)`
勉強不足で初めて知ったのですが、ここの正規表現の「(?:〜〜)」の部分は*キャプチャしない*
という意味らしいです。（キャプチャとはここでいう`$matches`に入れないと考えていただければ差し支えありません。）
この場合ですと、外側の丸カッコは必要ないだけではなく、丸カッコがネストしています。
そこでキャプチャしないという指定をしてあげることで、`$matches`の中にどういう順番で文字列が格納されるのかとてもわかりやすくなります。
さて、その後の記述ですが、PHPのバージョンについてコメント付きの条件式が出てきますが、3.0ではPHP5.4以上なのでそのうち外されるんでしょうか？
何にせよ、歴史を感じる記述です。（2.0の頃、ここに関する面白いやりとりが公式リポジトリのissueでありましたが、今ログが見つかりませんでした・・・残念）

### valid_email

```Form_validation.php


	public function valid_email($str)
	{
		if (function_exists('idn_to_ascii') && $atpos = strpos($str, '@'))
		{
			$str = substr($str, 0, ++$atpos).idn_to_ascii(substr($str, $atpos));
		}

		return (bool) filter_var($str, FILTER_VALIDATE_EMAIL);
	}
```
メールアドレスとして正しいかチェックする
もうひとつ面白いルールです。
`idn_to_ascii`とは、誤解を恐れず言うとマルチバイト対応です。
つまり、日本語ドメインなどのメールアドレスをちゃんと扱えるということになります。
日本語ドメインメールアドレスはGoogleが対応すると告知した以上、今後ある程度は普及すると考えられます。
https://googleblog.blogspot.jp/2014/08/a-first-step-toward-more-global-email.html

### valid_emails

```Form_validation.php

	public function valid_emails($str)
	{
		if (strpos($str, ',') === FALSE)
		{
			return $this->valid_email(trim($str));
		}

		foreach (explode(',', $str) as $email)
		{
			if (trim($email) !== '' && $this->valid_email(trim($email)) === FALSE)
			{
				return FALSE;
			}
		}

		return TRUE;
	}
```
カンマ区切りの複数のメールアドレスが正しいか、チェックする
カンマでぶった切って、`valid_email`に投げてくれます。

### valid_ip

```Form_validation.php

	public function valid_ip($ip, $which = '')
	{
		return $this->CI->input->valid_ip($ip, $which);
	}
```
IPアドレスとして正しいかチェックする
処理の本体は`/system/libraries/Input.php`にあります。

```Input.php
	public function valid_ip($ip, $which = '')
	{
		$which = strtolower($which);

		// First check if filter_var is available
		if (is_callable('filter_var'))
		{
			switch ($which) {
				case 'ipv4':
					$flag = FILTER_FLAG_IPV4;
					break;
				case 'ipv6':
					$flag = FILTER_FLAG_IPV6;
					break;
				default:
					$flag = '';
					break;
			}

			return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flag);
		}

		if ($which !== 'ipv6' && $which !== 'ipv4')
		{
			if (strpos($ip, ':') !== FALSE)
			{
				$which = 'ipv6';
			}
			elseif (strpos($ip, '.') !== FALSE)
			{
				$which = 'ipv4';
			}
			else
			{
				return FALSE;
			}
		}

		$func = '_valid_'.$which;
		return $this->$func($ip);
	}

	// --------------------------------------------------------------------

	/**
	* Validate IPv4 Address
	*
	* Updated version suggested by Geert De Deckere
	*
	* @access	protected
	* @param	string
	* @return	bool
	*/
	protected function _valid_ipv4($ip)
	{
		$ip_segments = explode('.', $ip);

		// Always 4 segments needed
		if (count($ip_segments) !== 4)
		{
			return FALSE;
		}
		// IP can not start with 0
		if ($ip_segments[0][0] == '0')
		{
			return FALSE;
		}

		// Check each segment
		foreach ($ip_segments as $segment)
		{
			// IP segments must be digits and can not be
			// longer than 3 digits or greater then 255
			if ($segment == '' OR preg_match("/[^0-9]/", $segment) OR $segment > 255 OR strlen($segment) > 3)
			{
				return FALSE;
			}
		}

		return TRUE;
	}

	// --------------------------------------------------------------------

	/**
	* Validate IPv6 Address
	*
	* @access	protected
	* @param	string
	* @return	bool
	*/
	protected function _valid_ipv6($str)
	{
		// 8 groups, separated by :
		// 0-ffff per group
		// one set of consecutive 0 groups can be collapsed to ::

		$groups = 8;
		$collapsed = FALSE;

		$chunks = array_filter(
			preg_split('/(:{1,2})/', $str, NULL, PREG_SPLIT_DELIM_CAPTURE)
		);

		// Rule out easy nonsense
		if (current($chunks) == ':' OR end($chunks) == ':')
		{
			return FALSE;
		}

		// PHP supports IPv4-mapped IPv6 addresses, so we'll expect those as well
		if (strpos(end($chunks), '.') !== FALSE)
		{
			$ipv4 = array_pop($chunks);

			if ( ! $this->_valid_ipv4($ipv4))
			{
				return FALSE;
			}

			$groups--;
		}

		while ($seg = array_pop($chunks))
		{
			if ($seg[0] == ':')
			{
				if (--$groups == 0)
				{
					return FALSE;	// too many groups
				}

				if (strlen($seg) > 2)
				{
					return FALSE;	// long separator
				}

				if ($seg == '::')
				{
					if ($collapsed)
					{
						return FALSE;	// multiple collapsed
					}

					$collapsed = TRUE;
				}
			}
			elseif (preg_match("/[^0-9a-f]/i", $seg) OR strlen($seg) > 4)
			{
				return FALSE; // invalid segment
			}
		}

		return $collapsed OR $groups == 1;
	}
```
`filter_var`はここでも大活躍ですね。



### alpha

```Form_validation.php
	public function alpha($str)
	{
		return ctype_alpha($str);
	}
```
PHPのデフォルト関数に渡しているだけです。
シンプルでいいですね。
参考文献：　http://php.net/manual/ja/function.ctype-alpha.php

### alpha_numeric

```Form_validation.php
	public function alpha_numeric($str)
	{
		return ctype_alnum((string) $str);
	}
```
こちらもPHPのデフォルト関数に渡しているだけです。
シンプルでいいですね。
参考文献：　http://php.net/manual/ja/function.ctype-alnum.php