結論:
1、使用php magic_quotes_gpc是錯誤的。從PHP 5.4.0 起已刪除此功能。http://www.php.net/manual/zh/function.get-magic-quotes-gpc.php
2、使用php addslashes是錯誤的。因為它不對應mysql或pgsql。PHP官方強烈建議使用對應mysql的mysqli_real_escape_string()和對應PostgreSQL的pg_escape_string()。http://php.net/manual/en/function.addslashes.php
3、保存到數據庫之前使用htmlspecialchars是錯誤的。應直接保存到數據庫。通過API提供給手機app看,C++不需要轉義HTML。通過web看時,才需要轉義。見下圖。
4、使用php mysql_escape_string是錯誤的,已廢棄,應使用mysqli::real_escape_string。http://php.net/manual/en/function.mysql-escape-string.php
5、數據入庫前根據不同數據庫進行不同的轉義,這放在dao層。controller、model都不應該知道。
錯誤的轉義方式:
正確的轉義方式:
詳細分析如下。
為什么要轉義:
1、字符與保留字沖突,比如HTML中的小與號<用於標簽
2、ASCII字符集中沒有此字符,比如HTML中©以前經常寫成©
第2種情況現在已經無需考慮,因為 Windows 從 XP 開始、Linux 從 GNU glibc 2.2 開始,操作系統都支持Unicode字符集了,目前開發程序主流是使用Unicode的utf-8編碼。
本文只討論第1種情況。
需要轉義的內容:
需要轉義 | 不需要轉義的字符(常見錯誤) | |
HTML | <、& | > |
mysql | 換行、回車等 | 換頁、空格、<、>、& |
postgresql | 換頁、換行、回車等 | |
JSON | "、\和控制字符 |
PHP常見轉義方法:
轉義范圍 | |
不應使用addslashes | 單引號'、雙引號"、反斜線\與 NUL |
htmlspecialchars | &、'、"、<、> |
不應使用mysql_escape_string | 對應mysql 4 |
mysqli::real_escape_string | 對應mysql 5+ |
pg_escape_string | 對應PostgreSQL |
php PDO::quote | 自動判斷數據庫是Mysql還是PostgreSQL等 |
PDO::prepare() | 自動判斷數據庫是Mysql還是PostgreSQL等 |
對字符串"hello world\fjim\n\r"進行轉義,結果如下:
mysqli::real_escape_string

<?php $mysqli = new mysqli('localhost', 'root', '1', 'test'); $a = "hello world\fjim\n\r"; var_dump($mysqli->real_escape_string($a)); $sql = "INSERT INTO user VALUES ('2','lucy','". $mysqli->real_escape_string($a) . '\');'; var_dump($sql); $mysqli->query($sql); ?>
PDO::quote mysql 與 mysqli::real_escape_string 結果一樣。

<?php $dsn = 'mysql:dbname=test;host=localhost'; $user = 'root'; $password = '1'; $dbh = new PDO($dsn, $user, $password); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $a = "hello world\fjim\n\r"; var_dump($dbh->quote($a)); $sql = "INSERT INTO user VALUES ('2','lucy',". $dbh->quote($a) . ');'; var_dump($sql); $stmt = $dbh->query($sql); exit; ?>
pg_escape_string

<?php $db = pg_connect("host=localhost port=5432 dbname=test user=root password=1"); $a = "hello world\fjim\n\r"; var_dump(pg_escape_string($a)); $sql = "INSERT INTO schema1.user VALUES ('2','lucy','". pg_escape_string($a) . '\');'; var_dump($sql); pg_query($db, $sql);exit; ?>
PDO::quote pgsql 與pg_escape_string 結果相同。

<?php $dsn = 'pgsql:host=localhost;port=5432;dbname=test;user=root;password=1'; $dbh = new PDO($dsn); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $a = "hello world\fjim\n\r"; var_dump($dbh->quote($a)); //$stmt = $dbh->query('SELECT "id","name","desc" FROM schema1."user"'); //$sql = "INSERT INTO schema1.user VALUES ('1','jim','hello world\fjim\n\r');"; $sql = "INSERT INTO schema1.user VALUES ('2','lucy',". $dbh->quote($a) . ');'; var_dump($sql); $stmt = $dbh->query($sql); exit; ?>
HTML:
HTML標簽使用小與號<開頭,所以需要轉義。而大與號>不用轉義。官方文檔:http://www.w3.org/TR/xhtml1/#h-4.8
mysql:
官方文檔:http://dev.mysql.com/doc/refman/5.1/zh/language-structure.html#string-syntax
mysql在默認的空sql-mode下,value的string使用單引號'或雙引號"引起來,官方文檔:http://dev.mysql.com/doc/refman/5.1/zh/language-structure.html#string-syntax
mysql在默認的空sql-mode下,識別符(庫、表、索引、列和別名)使用反勾號`引起來,官方文檔:http://dev.mysql.com/doc/refman/5.1/zh/language-structure.html#legal-names
mysql在ANSI_QUOTES sql-mode下,value的string使用單引號'引起來,官方文檔:http://dev.mysql.com/doc/refman/5.1/zh/language-structure.html#string-syntax
mysql在ANSI_QUOTES sql-mode下,識別符(庫、表、索引、列和別名)使用雙引號"或反勾號`引起來,http://dev.mysql.com/doc/refman/5.1/zh/database-administration.html#server-sql-mode
這里以默認的空sql-mode為例。
如果string使用雙引號引起來,那么string中的單引號無需轉義。如果string使用單引號引起來,那么string中的雙引號無需轉義。
使用musqldump導出sql就會看到,mysql的string使用單引號'引起來,而雙引號一定被轉義。
比如INSERT INTO `asdf` VALUES ('a\"b'); 和 INSERT INTO `asdf` VALUES ('a"b'); 結果是一樣的。
postgresql:
value的string使用單引號'引起來或者不引。
JSON:
官方文檔:http://www.json.org/json-zh.html
JSON的string,必須使用雙引號"引起來,不能使用單引號'引起來。如果字符串中出現雙引號,需要轉義,比如{"name" : "John \"Cliff\" Barxter"}。
參考資料:
http://www.ibm.com/developerworks/cn/linux/i18n/unicode/linuni/
http://msdn.microsoft.com/zh-cn/magazine/cc163490.aspx
采用支持 Unicode 的單源代碼庫使開發時間得以縮短,Unicode 為 Microsoft 帶來的好處是顯而易見的。就 Windows® 2000 來說,在發布英文產品后需要花費幾個月的時間來准備其本地化版本。而對於 Windows XP,這一周期已縮短為幾周時間。