PHP驗證、過濾參數還在用正則?使用過濾器函數也可以

什麼是 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
    
使用此函數可以用來檢查是否是GETPOSTCOOKIESERVERENV等全局變量裏是否有指定變量存在。
    
當然,也可以使用 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 &#38; 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"));

參考文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章