验证email地址你的web应用需要做的一个常见的任务。就是检查一个用户是否输入了一个有效的email地址。你将毫无疑问地在网上找到一堆复杂的表达式,它们声称可以解决此问题,但是最简单的方法就是使用PHP的内建函数filter_var(),它能够检验email地址。 示例2 | filter_var( 'sgamgee@example.com' , FILTER_VALIDATE_EMAIL); |
3 | filter_var( 'sauron@mordor' , FILTER_VALIDATE_EMAIL); |
进一步阅读
净化HTML输入和输出对于简单的数据净化,使用htmlentities()函数, 复杂的数据净化则使用HTML Purifier库经HTML Purifier 4.4.0测试
在任何wbe应用中展示用户输出时,首先对其进行“净化”去除任何潜在危险的HTML是非常必要的。 一个恶意的用户可以制作某些HTML,若被你的web应用直接输出,对查看它的人来说会很危险。
虽然可以尝试使用正则表达式来净化HTML,但不要这样做。HTML是一种复杂的语言,试图 使用正则表达式来净化HTML几乎总是失败的。
你可能会找到建议你使用strip_tags() 函数的观点。虽然strip_tags()从技术上来说是安全的,但如果输入的不合法的HTML(比如, 没有结束标签),它就成了一个“愚蠢”的函数,可能会去除比你期望的更多的内容。由于非技术用户 在通信中经常使用<和>字符,strip_tags()也就不是一个好的选择了。
如果阅读了 验证邮件地址 一节, 你也许也会考虑使用filter_var() 函数。然而filter_var()函数在遇到断行时会出现问题, 并且需要不直观的配置以接近htmlentities()函数的效果, 因此也不是一个好的选择。
对于简单需求的净化 如果你的web应用仅需要完全地转义(因此可以无害地呈现,但不是完全去除)HTML,则使用 PHP的内建htmlentities()函数。 这个函数要比HTML Purifier快得多,因为它不对HTML做任何验证—仅转义所有东西。 htmlentities()不同于类似功能的函数htmlspecialchars(), 它会编码所有适用的HTML实体,而不仅仅是一个小的子集。 示例03 | $evilHtml = '<div onclick="xss();">Mua-ha-ha! Twiddling my evil mustache...</div>' ; |
08 | $safeHtml = htmlentities( $evilHtml , ENT_QUOTES, 'UTF-8' ); |
对于复杂需求的净化对于很多web应用来说,简单地转义HTML是不够的。你可能想完全去除任何HTML,或者允许 一小部分子集的HTML存在。若是如此,则使用HTML Purifier 库。 HTML Purifier是一个经过充分测试但效率比较低的库。这就是为什么如果你的需求并不复杂 就应使用htmlentities(),因为 它的效率要快得多。 HTML Purifier相比strip_tags() 是有优势的,因为它在净化HTML之前会对其校验。这意味着如果用户输入无效HTML,HTML Purifier相比strip_tags()更能保留HTML的原意。HTML Purifier高度可定制,允许你为HTML的一个子集建立白名单来允许这个HTML子集的实体存在 输出中。
但其缺点就是相当的慢,它要求一些设置,在一个共享主机的环境里可能是不可行的。其文档 通常也复杂而不易理解。以下示例是一个基本的使用配置。查看文档 阅读HTML Purifier提供的更多更高级的特性。 示例 <?php// Include the HTML Purifier libraryrequire_once('htmlpurifier-4.4.0/HTMLPurifier.auto.php');// Oh no! The user has submitted malicious HTML, and we have to display it in our web app!$evilHtml='<div onclick="xss();">Mua-ha-ha! Twiddling my evil mustache...</div>';// Set up the HTML Purifier object with the default configuration.$purifier=newHTMLPurifier(HTMLPurifier_Config::createDefault());$safeHtml=$purifier->purify($evilHtml);// $safeHtml is now sanitized. You can output $safeHtml to your users without fear!?> 陷阱 - 以错误的字符编码使用htmlentities()会造成意想不到的输出。在调用该函数时始终确认 指定了一种字符编码,并且该编码与将被净化的字符串的编码相匹配。更多细节请查看 UTF-8一节。
- 使用htmlentities()时,始终包含ENT_QUOTES和字符编码参数。默认情况下,htmlentities() 不会对单引号编码。多愚蠢的默认做法!
- HTML Purifier对于复杂的HTML效率极其的低。可以考虑设置一个缓存方案如APC来保存经过净化的结果 以备后用。
进一步阅读
PHP and UTF-8没有一行式解决方案。小心、注意细节,以及一致性。PHP中的UTF-8糟透了。原谅我的用词。
目前PHP在低层次上还不支持Unicode。有几种方式可以确保UTF-8字符串能够被正确处理, 但并不容易,需要深入到web应用的所有层面,从HTML,到SQL,到PHP。我们旨在提供一个简洁、 实用的概述。
PHP层面的UTF-8基本的字符串操作,如串接 两个字符串、将字符串赋给变量,并不需要任何针对UTF-8的特殊东西。然而,多数 字符串函数,如strpos() 和strlen,就需要特殊的考虑。这些 函数都有一个对应的mb_*函数:例如,mb_strpos() 和mb_strlen()。这些对应的函数 统称为多字节字符串函数。这些多字节字符串 函数是专门为操作Unicode字符串而设计的。
当你操作Unicode字符串时,必须使用mb_*函数。例如,如果你使用substr() 操作一个UTF-8字符串,其结果就很可能包含一些乱码。正确的函数应该是对应的多字节函数, mb_substr()。
难的是始终记得使用mb_*函数。即使你仅一次忘了,你的Unicode字符串在接下来的处理中 就可能产生乱码。
并不是所有的字符串函数都有一个对应的mb_*。如果不存在你想要的那一个,那你就只能 自认倒霉了。
此外,在每个PHP脚本的顶部(或者在全局包含脚本的顶部)你都应使用 mb_internal_encoding 函数,如果你的脚本会输出到浏览器,那么还得紧跟其后加个mb_http_output() 函数。在每个脚本中显式地定义字符串的编码在以后能为你减少很多令人头疼的事情。
最后,许多操作字符串的PHP函数都有一个可选参数让你指定字符编码。若有该选项, 你应 始终显式地指明UTF-8编码。例如,htmlentities() 就有一个字符编码方式选项,在处理这样的字符串时应始终指定UTF-8。
MySQL级别的UTF-8如果你的PHP脚本访问MySQL,你有机会再数据库中,以非UTF-8字符串保存你的字符串,尽管你遵从了上述所有注意事项。 为了确保你的字符串以UTF-8格式,从PHP到MySQL,确保你的数据库和表单都设置为utf8mb4字符集,在争论你的数据库中任何其他查询之前,注意MySQL查询`set names utf8mb4`。对于一个例子,看看章节connecting to and querying a MySQL database。这是非常重要的。 为了完成UTF-8的支持,注意你必须使用`utf8mb4`字符集,而不是`utf8`字符集!查看Further Reading寻找原因。 在浏览器级别上使用UTF-8
使用 mb_http_output() 函数来确保你的PHP 输出给浏览器的文件编码为UTF-8。HTML 页面文件中<head>标签下有字符编码标签( charset <meta> tag )。 例
03 | mb_internal_encoding( 'UTF-8' ); |
06 | mb_http_output( 'UTF-8' ); |
09 | $string = 'Aš galiu valgyti stiklą ir jis manęs nežeidžia' ; |
12 | $string = mb_substr( $string , 0, 10); |
17 | $link = new \PDO( 'mysql:host=your-hostname;dbname=your-db' , |
21 | \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, |
22 | \PDO::ATTR_PERSISTENT => false, |
23 | \PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8mb4' |
29 | $handle = $link ->prepare( 'insert into Sentences (Id, Body) values (?, ?)' ); |
30 | $handle ->bindValue(1, 1, PDO::PARAM_INT); |
31 | $handle ->bindValue(2, $string ); |
35 | $handle = $link ->prepare( 'select * from Sentences where Id = ?' ); |
36 | $handle ->bindValue(1, 1, PDO::PARAM_INT); |
40 | $result = $handle ->fetchAll(\PDO::FETCH_OBJ); |
44 | <meta http-equiv= "Content-Type" content= "text/html; charset=UTF-8" /> |
45 | <title>UTF-8 test page</title> |
49 | foreach ( $result as $row ){ |
延伸阅读
时间和日期在PHP的早些时候,我们不得不使用关于date(),gmdate(),date_timezone_set(),strtotime() 的一系列眼花缭乱的组合,来完成日期和时间的操作。遗憾的是,你依然能够在网上找到许多这些困难的,旧样式功能的教程。 对于我们幸运的是,我们正在讨论的PHP版本有了更加友好的DateTime类特性。这个类封装了所有的功能,更多旧日期函数到一个易用的类,更令人高兴地是使得时间转换更加容易。在PHP中,总是使用DateTime类来说检查,比较,改变,显示日期。 Example03 | $date = new DateTime( '2011-05-04 05:00:00' , new DateTimeZone( 'UTC' )); |
06 | $date ->add( new DateInterval( 'P10D' )); |
08 | echo ( $date ->format( 'Y-m-d h:i:s' )); |
12 | $date ->setTimezone( new DateTimeZone( 'America/Los_Angeles' )); |
15 | echo ( $date ->format( 'Y-m-d h:i:s' )); |
17 | $later = new DateTime( '2012-05-20' , new DateTimeZone( 'UTC' )); |
21 | echo ( 'Yup, you can compare dates using these easy operators!' ); |
24 | $difference = $date ->diff( $later ); |
26 | echo ( 'The 2nd date is ' . $difference [ 'days' ] . ' later than 1st date.' ); |
Gotchas==Got You如果你不能指定一个时区,DateTime::__construct()将会设置结果的时区同运行的电脑时区一致。这在后面会导致很大的烦恼。当你创建新的日期的时候,总是会指定UTC时区,除非你知道你在做什么。 如果你在DateTime::__construct()中使用UNIX时间戳,时区将会总是指定为UTC,而不管你在第二个参数中指定的是什么。 传递归零数据(例如"0000-00-00",一个通常由MySQL产生的值,作为一个DateTime行的默认值)到DateTime::__construct(),将会产生一个无法解释的值,而不是"0000-00-00"。 在32位系统中使用 DateTime::getTimestamp()不会显示草果2038的数据。64系统没有此问题。
进一步阅读
检查一个值是null还是false使用===运算符检查null和false布尔值PHP宽松的类型系统,提供了许多检查一个变量值的不同方法。然而它也展现了许多问题。使用==去检查一个值是null还是false,如果这个值确实是空字符串或者0,则返回false。isset()检查一个变量是否有值,而不是那个值是null或者false,因此不合适用在这里。 is_null()函数准确检查一个值是否为null,is_bool()函数检查是否为布尔值(比如false),但是有更好的选择:===运算符。===检查值是否一样,但是和PHP宽松类型世界中的equivalent不一样。它也比is_null()和is_bool()微微快一些,而且也被一些人认为比使用一个比较函数更简洁。 Example07 | print( 'Oops! $x is 0, not null!' ); |
11 | print( 'Great, but could be faster.' ); |
23 | if ( strpos ( 'abc' , 'a' ) !== false) |
24 | print( 'Found it for real this time!' ); |
Gotchas==Got you- 当测试一个函数的返回值的时候,函数能够返回0或者布尔值,像strpos()一样,总是使用===和!==,你或许会遇到问题。
进一步阅读
|