請注意,此函式不會驗證「非拉丁」網域。
if (filter_var('уникум@из.рф', FILTER_VALIDATE_EMAIL)) {
echo '有效';
} else {
echo '無效';
}
(PHP 5 >= 5.2.0, PHP 7, PHP 8)
filter_var — 使用指定的過濾器過濾變數
使用FILTER_VALIDATE_*
驗證過濾器、FILTER_SANITIZE_*
淨化過濾器或自定義過濾器來過濾變數。
value
純量值在過濾之前會在內部被轉換為字串。
filter
FILTER_VALIDATE_*
常數作為驗證過濾器,使用FILTER_SANITIZE_*
或FILTER_UNSAFE_RAW
作為淨化過濾器,或使用FILTER_CALLBACK
作為自定義過濾器。
注意: 預設值是
FILTER_DEFAULT
,它是FILTER_UNSAFE_RAW
的別名。這將導致預設情況下不進行任何過濾。
options
FILTER_FLAG_*
的位元遮罩。 如果filter
接受選項,則可以使用陣列的"flags"
欄位來提供旗標。 成功時返回過濾後的資料。失敗時返回false
,除非使用了FILTER_NULL_ON_FAILURE
旗標,在這種情況下會返回null
。
範例 #1 filter_var() 範例
<?php
var_dump(filter_var('bob@example.com', FILTER_VALIDATE_EMAIL));
var_dump(filter_var('https://example.com', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED));
?>
上述範例將輸出
string(15) "bob@example.com" bool(false)
範例 #2 驗證陣列項目的範例
<?php
$emails = [
"bob@example.com",
"test@example.local",
"invalidemail"
];
var_dump(filter_var($emails, FILTER_VALIDATE_EMAIL, FILTER_REQUIRE_ARRAY));
?>
上述範例將輸出
array(3) { [0]=> string(15) "bob@example.com" [1]=> string(18) "test@example.local" [2]=> bool(false) }
範例 #3 傳遞陣列作為 options
的範例
<?php
$options = [
'options' => [
'min_range' => 10,
],
'flags' => FILTER_FLAG_ALLOW_OCTAL,
];
var_dump(filter_var('0755', FILTER_VALIDATE_INT, $options));
var_dump(filter_var('011', FILTER_VALIDATE_INT, $options));
?>
上述範例將輸出
int(493) bool(false)
範例 #4 直接或透過 陣列 提供旗標
$str = 'string'; var_dump(filter_var($str, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)); var_dump(filter_var($str, FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]));
上述範例將輸出
NULL NULL
FILTER_VALIDATE_*
FILTER_SANITIZE_*
請注意,此函式不會驗證「非拉丁」網域。
if (filter_var('уникум@из.рф', FILTER_VALIDATE_EMAIL)) {
echo '有效';
} else {
echo '無效';
}
實際上,這對手冊來說並不是很有幫助的註釋(所以不要按讚),但由於搜尋引擎找不到很多關於此錯誤訊息的資訊,尤其沒有任何有用的提示,它可能會為某些人節省一些時間。
如果您收到類似「filter_var(): 含有 ID 2097152 的未知過濾器」或其他數字的錯誤訊息,您只是不小心混淆了參數。因此,不要使用
<?php
filter_var($ip, FILTER_FLAG_IPV6)
?>
您應該嘗試使用
<?php
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)
?>
它會起作用 ;) 我知道,這不是設計函數最直觀的形式,而且很容易像在一般檢查中那樣把所有東西都丟到一個參數中,但是,是的,它就是這樣。
我發現了一些 FILTER_VALIDATE_EMAIL 拒絕,但 RFC5321 允許的地址
<?php
foreach (array(
'localpart.ending.with.dot.@example.com',
'(comment)localpart@example.com',
'"this is v@lid!"@example.com',
'"much.more unusual"@example.com',
'postbox@com',
'admin@mailserver1',
'"()<>[]:,;@\\"\\\\!#$%&\'*+-/=?^_`{}| ~.a"@example.org',
'" "@example.org',
) as $address) {
echo "<p>$address is <b>".(filter_var($address, FILTER_VALIDATE_EMAIL) ? '' : 'not')." valid</b></p>";
}
?>
結果
localpart.ending.with.dot.@example.com 不合法
(comment)localpart@example.com 不合法
"this is v@lid!"@example.com 不合法
"much.more unusual"@example.com 不合法
postbox@com 不合法
admin@mailserver1 不合法
"()<>[]:,;@\"\\!#$%&'*+-/=?^_`{}| ~.a"@example.org 不合法
" "@example.org 不合法
文件並沒有說明 FILTER_VALIDATE_EMAIL 應該要通過 RFC5321,然而你可能會遇到這些例子(尤其是第一個)。所以這是一個注意事項,而不是錯誤報告。
請注意,FILTER_VALIDATE_BOOLEAN 會嘗試變得更聰明,識別像是 Yes、No、Off、On 等詞彙,無論是字串還是原生 true 和 false 類型,並且在驗證字串時不區分大小寫。
<?php
$vals=array('on','On','ON','off','Off','OFF','yes','Yes','YES',
'no','No','NO',0,1,'0','1','true',
'True','TRUE','false','False','FALSE',true,false,'foo','bar');
foreach($vals as $val){
echo var_export($val,true).': '; var_dump(filter_var($val,FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE));
}
?>
輸出
'on': 布林值(真)
'On': 布林值(真)
'ON': 布林值(真)
'off': 布林值(假)
'Off': 布林值(假)
'OFF': 布林值(假)
'yes': 布林值(真)
'Yes': 布林值(真)
'YES': 布林值(真)
'no': 布林值(假)
'No': 布林值(假)
'NO': 布林值(假)
0: 布林值(假)
1: 布林值(真)
'0': 布林值(假)
'1': 布林值(真)
'true': 布林值(真)
'True': 布林值(真)
'TRUE': 布林值(真)
'false': 布林值(假)
'False': 布林值(假)
'FALSE': 布林值(假)
true: 布林值(真)
false: 布林值(假)
'foo': 空值
'bar': 空值
「hek」關於 HTML5 擁有模式因此減輕 PHP 過濾需求的說明完全錯誤:您仍然必須在伺服器端過濾輸入。 HTML5 表單輸入是客戶端,意味著它們完全在用戶的控制之下。只有當您在 PHP 中收到數據時,它才是伺服器端並在您的控制之下。一旦數據在您的控制之下,您就必須正確地過濾/清理它。
無論伺服器端語言為何,這都是事實。我會建議版主刪除「hek」的說明,因為它會誤導人們,造成可怕的後果。
Steve
這也是一個有效的網址
http://example.com/"><script>alert(document.cookie)</script>
請注意
網址中的問號也是有效的
<?php
echo filter_var("http://test???test.com", FILTER_VALIDATE_URL)?"有效":"無效"; #有效
?>
FILTER_VALIDATE_URL 允許
filter_var('javascript://comment%0Aalert(1)', FILTER_VALIDATE_URL);
其中 %0A(URL 編碼的換行符),在某些情況下,會將註釋從 JS 代碼中分割出來。
這可能導致 XSS 漏洞。
我寫了一個與 PHP 的 filter_var() 實作完全相容的 JavaScript 電子郵件驗證器。
mpyw/FILTER_VALIDATE_EMAIL.js:與 PHP 的 filter_var($value, FILTER_VALIDATE_EMAIL) 相容的電子郵件驗證
https://github.com/mpyw/FILTER_VALIDATE_EMAIL.js
請注意,帶有 FILTER_VALIDATE_URL 的 filter_var() 使用已過時的 RFC2396。這表示它將一些目前有效的字元(例如「_」)視為無效。
在許多情況下,使用 php parse_url() 可能更有益,它使用目前有效的 RFC3986。
使用 FILTER_CALLBACK 需要傳遞一個陣列作為選項
<?php
function toDash($x){
return str_replace("_","-",$x);
}
echo filter_var("asdf_123",FILTER_CALLBACK,array("options"=>"toDash"));
// 返回 'asdf-123'
?>
您很可能實際上想要偵測所有保留範圍,而不僅僅是私有 IP,並且還有另一個常數可以用於它們,應該與它進行位元或運算。
<?php
function is_private_ip($ip) {
return !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
}
?>
以下是使用多個旗標的方法(對於像我一樣透過範例學習效果更好的人)
<?php
echo "|asdf".chr(9).chr(128)."_123|";
echo "\n";
// 「位元且」指的是邏輯或 / 位元 |
echo filter_var("|asdf".chr(9).chr(128)."_123\n|" ,FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
/*
結果:
|asdf �_123|
|asdf_123|
*/
?>
請注意,僅使用 FILTER_VALIDATE_URL 來驗證網址輸入可能會導致 XSS 漏洞
$url = 'javascript://%0Aalert(document.cookie)';
if (filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED)) {
echo '<a href="' . $url . '">點擊</a>';
}
您至少應該另外檢查實際使用的通訊協定。
這是一個帶有旗標的 filter 語法實際範例,因為似乎沒有任何地方有單行程式碼可以做到這一點
'hours' => array('filter'=>FILTER_SANITIZE_NUMBER_FLOAT, 'flags' => FILTER_FLAG_ALLOW_FRACTION, 'options'=> '.')
請注意,即使網址不正確,以下內容也會返回 true。因為它只驗證網域、子網域、路徑和查詢,而不驗證通訊協定。
<?php
filter_var( 'http://https://example.com', FILTER_VALIDATE_URL );
?>
詳情請參閱 https://php.dev.org.tw/manual/en/filter.filters.validate.php
您可以使用多個 FLAGS 來驗證 IP 位址
//驗證輸入是否為 IPv4 位址
$_FILTERS = array('flags' => FILTER_FLAG_IPV4);
//驗證輸入是否為 IPv4 位址且不是私有 IP。
$_FILTERS = array('flags' => FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE);
//驗證輸入是否為 IPv4 位址且不是保留 IP。
$_FILTERS = array('flags' => FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE);
//驗證輸入是否為 IPv4 位址,不是私有 IP 也不是保留 IP。
$_FILTERS = array('flags' => FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
filter_var($_input, FILTER_VALIDATE_IP, $_FILTERS);
我看過一些關於 FILTER_VALIDATE_URL 的報告,我也想補充我的,因為在像這樣一段愚蠢的程式碼中
<?php
$ckOrigin = 'https://forum.myw3host.comhttps://forum.myw3host.comhttps://forum.myw3host.com/viewtopic.php?p=45#p45';
if(filter_var($ckOrigin, FILTER_VALIDATE_URL)){
echo '網址有效';
}
?>
因為我確信如果網址錯誤它會返回 false,所以我花了很多時間才意識到它反而會失敗在像上面這樣的字串中,並且它返回 true。
回覆 https://php.dev.org.tw/manual/en/function.filter-var.php#128235
如果使用 FILTER_FLAG_PATH_REQUIRED,它就能正常運作。
var_dump( filter_var('http://test???test.com/path/?t=1', FILTER_VALIDATE_URL) ); // true
var_dump( filter_var('http://test???test.com/path/?t=1', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED) ); // false
請注意,FILTER_FLAG_PATH_REQUIRED 接受單個斜線 (/),所以
<?php
$options = array('flags' => FILTER_FLAG_PATH_REQUIRED);
filter_var('http://example.com', FILTER_VALIDATE_URL, $options); // 返回 false
filter_var('http://example.com/', FILTER_VALIDATE_URL, $options); // 返回 'http://example.com/'
?>
我不建議在一般的網站上使用這個函式來驗證電子郵件地址。問題在於,根據 RFC 3696(名稱檢查和轉換的應用技術),以下電子郵件地址將被視為有效:
customer/department=shipping@example.com
$A12345@example.com
!def!xyz%abc@example.com
_somename@example.com
"Abc@def"@example.com
在 2020 年的線上網路應用程式中,這些我幾乎都不會接受 :-/
"(comment)localpart@example.com"
根據 RFC5322(附錄 A.6.3),這是一個無效的電子郵件地址。
「此外,地址、日期和訊息識別碼中的註釋和空格都是過時語法的一部分。」
請注意,當 FILTER_VALIDATE_INT 與 FILTER_FLAG_ALLOW_HEX 旗標一起使用時,例如字串「2f」將無法成功驗證,因為您必須使用「0x」前綴,否則它會將資料視為 10 進位。
範圍選項也夠聰明,可以識別不同進位制中超出邊界的情況。
以下是一個範例:
<?php
$foo = '256';
$bar = '0x100';
var_dump(validate_int($foo)); // false,太大
var_dump(validate_int($bar)); // false,太大
function validate_int($input)
{
return filter_var(
$input,
FILTER_VALIDATE_INT,
// 我們必須傳遞一個關聯式陣列
// 才能包含範圍檢查選項。
array(
'flags' => FILTER_FLAG_ALLOW_HEX,
'options' => array('min_range' => 1, 'max_range' => 0xff)
)
);
}
?>
FILTER_VALIDATE_URL 不支援國際化網域名稱 (IDN)。無論有效與否,任何包含 Unicode 字元的網域名稱都無法通過驗證。
我們可以用自製的解決方案來規避這個問題,但 C 語言程式碼就是 C 語言程式碼,所以我採用了以下程式碼,它建立在 filter_var() 的基礎上。
<?php
$res = filter_var($uri, FILTER_VALIDATE_URL);
if ($res) return $res;
// 檢查是否包含 Unicode 字元。
$l = mb_strlen($uri);
if ($l !== strlen($uri)) {
// 將寬字元替換為「X」。
$s = str_repeat(' ', $l);
for ($i = 0; $i < $l; ++$i) {
$ch = mb_substr($uri, $i, 1);
$s[$i] = strlen($ch) > 1 ? 'X' : $ch;
}
// 再次檢查。
$res = filter_var($s, FILTER_VALIDATE_URL);
if ($res) { $uri = $res; return 1; }
}
?>
邏輯很簡單。非 ASCII 字元的長度會超過一個位元組。我們將每個這類字元替換為「X」並再次檢查。
另一種方法是在呼叫 filter_var() 之前將 URI 轉換為 Punycode,但 PHP 缺乏 Punycode 的原生支援。我認為我的方法很有效。如果您有其他想法或認為有改進空間,請發送電子郵件給我。
一些布林值轉換
<?php
var_dump(filter_var('oops', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// NULL
var_dump(filter_var('false', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(false)
var_dump(filter_var('true', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(true)
var_dump(filter_var(0, FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(false)
var_dump(filter_var(1, FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(true)
var_dump(filter_var('TRUE', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(true)
var_dump(filter_var('', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(false)
var_dump(filter_var('FALSE', FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE)));
// bool(false)
過濾整數時,有一件很重要的事情需要記住,`max_range` 選項的值必須小於或等於 `PHP_INT_MAX` 的值。
filter_var($someVariable, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => SOME_VALUE_GREATER_THAN_PHP_INT_MAX)));
即使 `$someVariable` 是預期範圍內的有效整數,這也會失敗。
當您嘗試在 32 位元系統上驗證無號 MySQL INT 類型(其最大值為 4294967295)的潛在鍵值時,可能會出現這種情況,其中 `PHP_INT_MAX` 的值為 2147483647。
需要注意的是,雖然函數的第一個參數的資料類型被聲明為「mixed」,但这只是事实的一半。
雖然它接受任何資料類型,但在驗證或清理之前,第一個參數始終會被轉換為字串。
看來這個函數的設計嚴格來說是用於使用者輸入的字串。例如:來自線上表單。當用於其他用途時,您可能會看到問題。因此,請仔細閱讀文件!
特別要注意的是,目前有一個尚未解決的問題 (#49510),關於在使用 FILTER_NULL_ON_FAILURE 旗標時,布林值過濾器的問題。要注意的是,(字串) FALSE 和 FALSE 都無法被識別為布林值,而且會回傳 NULL(而不是您可能預期的 FALSE)。
因此我個人建議,目前為止,要讓 filter_var() 函式超越其原始用途(並允許未來的擴充和客製化)的最佳方法,是將它們封裝在您自己的類別中。這將允許您處理非字串輸入的非預期行為,並新增您的自訂檢查,或者回溯移植可能在未來 PHP 版本中新增的過濾器或清理器。
(尤其是因為 PHP 目前仍然缺少一些較特殊的 HTML5 輸入類型(例如「color」)的過濾器和清理器。因此,我們確實有可能在未來的某個時間點需要自訂過濾器或回溯移植。)
回覆 Andi
這不是有效的網址,因為字元沒有經過編碼
http://example.com/"><script>alert(document.cookie)</script>
這是有效的網址
http://example.com/%22%3E%3Cscript%3Ealert%28document.cookie%29%3C%2Fscript%3E