什麼是 PHP 過濾器?
PHP 過濾器就是用於驗證和過濾接收數據的一組系統函數,即filter函數。
爲什麼要使用過濾器?
我們編寫的程序幾乎都有依賴外部輸入的數據,這些數據包括:
- 來自用戶表單的輸入數據
- Cookies緩存的數據
- 其他應用程序(比如 web 服務)的數據
- 定義的服務器變量
- 數據庫查詢結果
這些非安全來源的數據是未知的,存在風險的。
我們應該始終對外部數據進行過濾,不要相信任何外部數據!
風險包括接收的數據格式不符合我們的預期,數據類型不符合我們的預期等;如果不對這些數據進行驗證和過濾,那麼不僅給我們的程序留有很大的安全漏洞,而且會對後續的功能流程的數據使用會帶來很大困擾,比如會出現數據提交錯亂、數據展示等問題。
基本操作就是我們需要N多個判斷,就像這樣:
function validate()
{
if (is_numeric($_POST['id'])) {
$id = intval($_POST['id']);
} else {
throw new Exception('錯誤的id');
}
if (!empty($_POST['name']) && is_string($_POST['name'])) {
$name = $_POST['name'];
} else {
throw new Exception('錯誤的name');
}
// 或許還有N多類似的判斷
.
.
.
return ['id' => $id, 'name' => $name];
}
如果傳過來的數據有很多參數,就要寫很多對應的判斷,是不是感覺有點麻煩?
麻煩就對了,所有就有了驗證器的類庫,比如webmozart/assert
function validate()
{
try {
Assert::keyExists($_POST, 'name', 'The key not exists. Got: %s');
Assert::string($_POST['name'], 'The path is expected to be a string. Got: %s');
Assert::numeric($_POST['id'], 'The value is not numeric. Got: %s');
} catch (InvalidArgumentException $th) {
return $th->getMessage();
}
}
但是這種驗證器類庫只有驗證的功能,並沒有過濾的功能。
PHP 的過濾器擴展的設計目的是使數據過濾更輕鬆快捷,不僅可以驗證,還可以過濾數據,所以我們要通過使用過濾器,確保應用程序獲得正確的輸入類型和符合格式的數據。
Filter 函數的使用
1. filter_has_var – 函數檢查是否存在指定輸入類型的變量
#成功時返回 TRUE, 或者在失敗時返回 FALSE。
bool filter_has_var( int $type, string $variable_name)
#type 可選類型INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, INPUT_ENV.
#variable_name 要檢查的變量的名稱
#示例1:
#地址欄輸入鏈接:http://127.0.0.1/test.php?name=test
<?php
if (!filter_has_var(INPUT_GET, "name")) {
echo ("Input type does not exist");
} else {
echo ("Input type exists");
}
#輸出結果:
Input type exists
使用此函數可以用來檢查是否是GET、POST、COOKIE、SERVER、ENV等全局變量裏是否有指定變量存在。
當然,也可以使用 isset($_GET["name"]) 、isset($_POST["name"])、isset($_SERVER["name"])等方式來判斷全局變量裏的參數是否存在
2. filter_var() - 通過一個指定的過濾器來過濾單一的變量
<?php
$email_a = '[email protected]';
$email_b = 'bogus';
$int = 123;
if (filter_var($email_a, FILTER_VALIDATE_EMAIL)) {
echo "Email address '$email_a' is considered valid.\n";
}
if (filter_var($email_b, FILTER_VALIDATE_EMAIL)) {
echo "Email address '$email_b' is considered valid.\n";
} else {
echo "Email address '$email_b' is considered invalid.\n";
}
if (!filter_var($int, FILTER_VALIDATE_INT)) {
echo ("不是一個合法的整數");
} else {
echo ("是個合法的整數");
}
以上程序會輸出:
Email address '[email protected]' is considered valid.
Email address 'bogus' is considered invalid.
是個合法的整數
Validating 和 Sanitizing 兩種過濾器:
Validating 過濾器:
- 用於驗證用戶輸入
- 嚴格的格式規則(比如 URL 或 E-Mail 驗證)
- 如果成功則返回預期的類型,如果失敗則返回 FALSE
Sanitizing 過濾器:
- 用於允許或禁止字符串中指定的字符
- 無數據格式規則
- 始終返回字符串
選項和標誌
選項和標誌用於向指定的過濾器添加額外的過濾選項。
不同的過濾器有不同的選項和標誌。
在下面的實例中,我們用 filter_var() 和 “min_range” 以及 “max_range” 選項驗證了一個整數:
$var = 300;
$int_options = array(
"options" => array(
"min_range" => 0,
"max_range" => 256
)
);
if (!filter_var($var, FILTER_VALIDATE_INT, $int_options)) {
echo ("不是一個合法的整數");
} else {
echo ("是個合法的整數");
}
由於整數是 “300”,它不在指定的範圍內,以上代碼的輸出將是:
不是一個合法的整數
淨化輸入
$email = '([email protected])';
$sanitized_email = filter_var($email, FILTER_SANITIZE_EMAIL);
if (filter_var($sanitized_email, FILTER_VALIDATE_EMAIL)) {
echo "This (email) sanitized email address is considered valid.\n";
echo "Before: $email\n";
echo "After: $sanitized_email\n";
}
3.filter_input – 通過名稱獲取特定的外部變量,並且可以通過過濾器處理它
#如果成功的話返回所請求的變量。如果過濾失敗則返回 FALSE ,如果variable_name 不存在的話則返回 NULL. 如果標示 FILTER_NULL_ON_FAILURE 被使用了,那麼當變量不存在時返回 FALSE ,當過濾失敗時返回 NULL
mixed filter_input( int $type, string $variable_name[, int $filter = FILTER_DEFAULT[, mixed $options]] )
#type 可選類型INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, INPUT_ENV.
#variable_name 參數名稱
#filter 過濾規則,可以在過濾規則頁面找到想要用的過濾規則;如果不指定,將使用FILTER_DEFAULT,它相當於FILTER_UNSAFE_RAW。這將導致在默認情況下不進行過濾。
#options 一個選項的關聯數組,或者按位區分的標示。如果過濾器接受選項,可以通過數組的 "flags" 位去提供這些標示。
#示例1():
<?php
$search_html = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_SPECIAL_CHARS);
$search_url = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_ENCODED);
echo "You have searched for $search_html.\n";
echo "<a href='?search=$search_url'>Search again.</a>";
#輸出結果:
You have searched for Me & son.
<a href='?search=Me%20%26%20son'>Search again.</a>
#示例2(驗證該值是否是有效的電子郵件地址):
<?php
if (!filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL)) {
echo "E-Mail is not valid";
} else {
echo "E-Mail is valid";
}
#示例3(驗證ip是否是內網ip)
#如果是內網IP則返回false,否則返回原IP
// $ip = '192.168.1.197';
// $ip = '39.104.162.134';
// $ip = '47.110.85.106';
// $ip = '219.143.3.146';
$result = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
var_dump($result);
4. filter_input_array – 獲取一系列外部變量,並且可以通過過濾器處理它們
#這個函數當需要獲取很多變量卻不想重複調用filter_input()時很有用。
#如果成功的話返回一個所請求的變量的數組,如果失敗的話返回 FALSE 。對於數組的值,如果過濾失敗則返回 FALSE ,如果variable_name 不存在的話則返回 NULL 。如果標示 FILTER_NULL_ON_FAILURE 被使用了,那麼當變量不存在時返回 FALSE ,當過濾失敗時返回 NULL 。
mixed filter_input_array( int $type[, mixed $definition[, bool $add_empty = true]] )
#type 可選類型INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, INPUT_ENV
#definition 一個定義參數的數組。一個有效的鍵必須是一個包含變量名的string,一個有效的值要麼是一個filter type,或者是一個array 指明瞭過濾器、標示和選項。如果值是一個數組,那麼它的有效的鍵可以是 filter,用於指明 filter type,flags 用於指明任何想要用於過濾器的標示,或者 options 用於指明任何想要用於過濾器的選項。
#這個參數也可以是一個filter constant的整數。那麼數組中的所有變量都會被這個過濾器所過濾。
#示例1:
<?php
error_reporting(E_ALL | E_STRICT);
/* data actually came from POST
$_POST = array(
'product_id' => 'libgd<script>',
'component' => '10',
'versions' => '2.0.33',
'testscalar' => array('2', '23', '10', '12'),
'testarray' => '2',
);
*/
$args = array(
'product_id' => FILTER_SANITIZE_ENCODED,
'component' => array('filter' => FILTER_VALIDATE_INT,
'flags' => FILTER_REQUIRE_ARRAY,
'options' => array('min_range' => 1, 'max_range' => 10)
),
'versions' => FILTER_SANITIZE_ENCODED,
'doesnotexist' => FILTER_VALIDATE_INT,
'testscalar' => array(
'filter' => FILTER_VALIDATE_INT,
'flags' => FILTER_REQUIRE_SCALAR,
),
'testarray' => array(
'filter' => FILTER_VALIDATE_INT,
'flags' => FILTER_REQUIRE_ARRAY,
)
);
$myinputs = filter_input_array(INPUT_POST, $args);
var_dump($myinputs);
echo "\n";
輸出結果:
array(6) {
["product_id"]=>
array(1) {
[0]=>
string(17) "libgd%3Cscript%3E"
}
["component"]=>
array(1) {
[0]=>
int(10)
}
["versions"]=>
array(1) {
[0]=>
string(6) "2.0.33"
}
["doesnotexist"]=>
NULL
["testscalar"]=>
bool(false)
["testarray"]=>
array(1) {
[0]=>
int(2)
}
}
注意:
在 INPUT_SERVER 數組中並沒有 REQUEST_TIME ,因爲它是被稍後插入到$_SERVER 中的。
5. filter_list – 返回所支持的過濾器列表
#返回一個所支持的過濾器的名稱的列表,如果沒有這樣子的過濾器的話則返回空數組。這個數組的索引不是過濾器id,你可以通過 filter_id() 去根據名稱獲取它們
array filter_list( void)
#示例1(輸出所有支持的過濾器):
<?php
print_r(filter_list());
輸出結果:
Array
(
[0] => int
[1] => boolean
[2] => float
[3] => validate_regexp
[4] => validate_url
[5] => validate_email
[6] => validate_ip
[7] => string
[8] => stripped
[9] => encoded
[10] => special_chars
[11] => unsafe_raw
[12] => email
[13] => url
[14] => number_int
[15] => number_float
[16] => magic_quotes
[17] => callback
)
6. filter_id() 函數 – 返回指定過濾器的 ID 號
#如果獲取成功則返回過濾器id,如果過濾器不存在則返回 FALSE
int filter_id( string $filtername)
#filtername 必須是過濾器名稱(不是過濾器 ID 名)
#如果不清楚過濾器名稱, 使用 filter_list() 函數來獲取所有被支持的過濾器的名稱
echo(filter_id("validate_email")); // 返回過濾器ID 274
使用 Filter Callback
通過使用 FILTER_CALLBACK 過濾器,可以調用自定義的函數,把它作爲一個過濾器來使用。這樣,我們就擁有了數據過濾的完全控制權,也同時解決了filter函數只能處理部分類型的數據的問題。
您可以創建自己的自定義函數,也可以使用已存在的 PHP 函數。
將您準備用到的過濾器的函數,按指定選項的規定方法進行規定。在關聯數組中,帶有名稱 “options”。
在下面的實例中,我們使用了一個自定義的函數把所有 “_” 轉換爲 “.”:
function convertSpace($string)
{
return str_replace("_", ".", $string);
}
$string = "www_runoob_com!";
echo filter_var($string, FILTER_CALLBACK, array("options"=>"convertSpace"));