Jex’s Note

PHP - Htmlentities 與 Htmlspecialchars 的差別

相同處

兩者都會轉換以下符號 :

  • & (ampersand) : &
  • " (double quote) : "
  • ' (single quote) : '' or '
  • < (less than) : &lt;
  • > (greater than) : &gt;

兩者都不會轉換英文數字括號分號

不同

  • htmlspecialchars 只轉換以上的 HTML 符號
  • htmlentities 除了轉換以上的 HTML 符號, 也轉換中文

Example

不包含中文 :

程式碼 :

echo htmlentities("<script type='text/javascript'>alert(1);</script>");
echo htmlspecialchars("<script type='text/javascript'>alert(1);</script>");

轉換後的原始碼 :

&lt;script type='text/javascript'&gt;alert(1);&lt;/script&gt;
&lt;script type='text/javascript'&gt;alert(1);&lt;/script&gt;

顯示結果 :

<script type='text/javascript'>alert(1);</script>
<script type='text/javascript'>alert(1);</script>

兩者結果相同

包含中文 :

程式碼 :

echo htmlentities("<script type='text/javascript'>alert(1);</script>測試");
echo htmlspecialchars("<script type='text/javascript'>alert(1);</script>測試");

轉換後的原始碼 :

&lt;script type='text/javascript'&gt;alert(1);&lt;/script&gt;&aelig;&cedil;&not;&egrave;&copy;&brvbar;
&lt;script type='text/javascript'&gt;alert(1);&lt;/script&gt;測試

顯示結果 :

<script type='text/javascript'>alert(1);</script>a﹐?ec|
<script type='text/javascript'>alert(1);</script>測試

兩者結果不同

效能比較 :

測試程式碼 :

$orig = '<div style="background:#ffc">Hello World</div>';
$converted_htmlspecialchars = htmlspecialchars($orig);
$converted_htmlentities = htmlentities($orig);
//先判斷轉出來後的結果是否相同
if ($converted_htmlspecialchars != $converted_htmlentities)
{
        echo "special and ent not equal<br />";
}
else
{
        echo "They are equal!<br />";
}
$iRepeatNTimes = 100000;
$startTime = microtime(true);
for($i = 0;$i < $iRepeatNTimes; $i++)
{
    $s = htmlspecialchars($orig);
}
echo "It took " . (microtime(true) - $startTime) . " to finish<br />";
$startTime = microtime(true);
for($i = 0;$i < $iRepeatNTimes; $i++)
{
    $s = htmlentities($orig);
}
echo "It took " . (microtime(true) - $startTime) . " to finish<br />";

and i repeat the 2 loops above just to see how they vary to the initial values

結果 :

第一次 :

They are equal!
It took 0.097237110137939 to finish
It took 0.23933792114258 to finish

第二次 :

They are equal!
It took 0.090903043746948 to finish
It took 0.24001097679138 to finish

第三次 :

They are equal!
It took 0.09278392791748 to finish
It took 0.23876214027405 to finish

第四次 :

They are equal!
It took 0.0883948802948 to finish
It took 0.23934412002563 to finish

第五次 :

They are equal!
It took 0.096112966537476 to finish
It took 0.23781609535217 to finish

結論 :

  1. 如果只有數字、英文、符號, 這兩者轉換後的結果完全沒差
  2. 但如果有包含中文, 結果就會不同。 htmlentities 會轉換中文
  3. htmlspecialchars 速度比 htmlentities 快 2.576 倍

參考資料 : http://blog.fesite.com/2007/08/23/php-htmlentities-htmlspecialchars/ http://www.sitepoint.com/forums/showthread.php?574723-don-t-use-htmlentities()-use-htmlspecialchars()-instead-faster-and-UTF-8-compat

Ajax (GET, POST, JSON)

jQuery

$(document).ready(function(){
    $.ajax({
        url: '/test/test_response.php',
        type: "POST",
        data: 'test=1&test2=33',
        beforeSend: function(html) {
            $('#loading').removeClass('hide');
        },
        success: function(response) {
            alert(response);
        },
        complete: function () {
           $('#loading').addClass('hide');
        },
        error: function(xhr, statusText, err){
          alert("Error:" + xhr.status);
        }
    });
});

PHP

POST :

header('Content-Type: application/json');
echo 'test='.$_POST['test'].'; test2= '.$_POST['test2'];

GET :

echo 'test='.$_GET['test'].'; test2= '.$_GET['test2'];

JSON :

$value = json_decode(file_get_contents('php://input'));         //取得JSON字串並且解析
echo json_encode($value);                                       //將$value轉成JSON格式傳回

JSON and JSONP

JSON 中文被編碼問題

如果 ajax 設定值沒有 dataType 的話, 就代表是以預設方式接收, 此時接收到 php 回傳的 json 格式時, 中文會被編碼, 長得像 :

\u518d\u807d\u9019\u9996\u6b4c\u7684\u9ede

有四種方式可以解決:

第一種 : 在 jQuery 的 $.ajax({ 的屬性裡 , 將 dataType 指定為 json
$.ajax({
    url: 'index.php/ajax_load/content_loading/',
    type: "POST",
    dataType: "json",
    (以下略..)

則 ajax 回傳的 json 格式就會自動被轉換成物件了, 所以就可以用一般我們使用陣列的方式來取值

例如使用foreach來alert出陣列裡的資料 :

for(qq in response)                (註 : response 為 php 回傳的 json 資料)
{
    alert(response[qq]);
}
第二種 : 與第一種原理一樣, 只不過我們手動把 json 轉為物件
response = JSON.parse(response);

這時 response 已被轉換為物件了, 就可以用陣列的方式來取值了

第三種 : 在 php 轉換 json 格式時動手腳, php 在轉換 json 時多加一個屬性 , 使它不將 unicode 轉碼

echo json_encode($json);

改成

echo json_encode($json, JSON_UNESCAPED_UNICODE);

中文就會正常地顯示了!

第四種 : 將 php 轉換json前的資料做urlencode , jQuery收到再urldecode

什麼是JSONP (JSON with Padding) :

目前以 ajax 傳輸資料首選的方法是用JSON格式 , 但是不同的網域就必須要以 JSONP 來跨域 , JSONP 不是一個新技術 , 是一種讓不同網域傳輸資料的方法 , 可以不必受限同源策略的限制 , 而達到不同網域以 ajax 傳輸資料的方法

未使用JSONP時, 會遇到的問題

假設我用localhost 以 ajax 請求一個線上網域的PHP檔, 來模擬不同網域 , 並且希望接收到回傳的JSON字串

localhost :

var car = new Array('toyota','BenZ','mazada');
var json = JSON.stringify(car);
$(document).ready(function(){
        $.ajax({
                url: 'http://jexpoyi.com/test/jsonp_response.php',
                type: "json",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                data: json,
                success: function(response) {
                        alert(response);
                }
        });
});

遠端PHP檔(放在線上) :

$value = json_decode(file_get_contents('php://input'));
echo json_encode($value);

結果 : 什麼回應都沒有, 因為同源策略的限制, 所以沒辦法取得到json資料

使用 JSONP

localhost :

<script type='text/javascript'>
function ajax(){
        $.ajax({
                url: 'http://jexpoyi.com/test/jsonp_response2.php',
                data: {name: 'Jex'},
                dataType: "jsonp",
                jsonp: 'callback',
                jsonpCallback: 'jsonpCallback',
                success: function() {
                        alert('success');
                }
        });
}
function jsonpCallback(data){
        alert(data.message);
}
</script>
<input type='button' value='click' onclick='ajax();'/>

遠端PHP檔(放在線上) :

<?php
    header("content-type: text/javascript");

    if(isset($_GET['name']) && isset($_GET['callback']))
    {
        $obj->name = $_GET['name'];
        $obj->message = "你好 " . $obj->name;

        echo $_GET['callback']. '(' . json_encode($obj) . ');';
    }
?>

結果 : OK!! 點擊 click按鈕 就會alert遠端回傳來的資料了!

PHP - RegExp(正則表達式) 語法

preg_match

return preg_match('/pattern/flag', $str);    //返回TRUE, FALSE

preg_replace

example 1

$str = 'aabbccddeeffccggii';
$str = preg_replace('/cc/', '\\1(cc)被取代了', $str);

結果為 : aabb(cc)被取代了ddeeff(cc)被取代了ggii

example 2

$str = 'qq-qq-QQ';
$reg = '/qq/';
echo preg_replace($reg,'哈',$str);    //參數分別為 (pattern , 要取代的字串 , 原字串)

結果為 : 顯示結果為 : 哈-哈-QQ

取得比對結果存進正則變數

(1)

$str = 'aabbccddeeffccggiiCC';
$str = preg_replace('/cc/i', "[\\0]", $str);    //忽略大小寫 , 結果為 : aabb[cc]ddeeff[cc]ggii[CC]
echo $str;

\\0 是取得比對到的字串 , 我們將比對到的字串加上中括號。 似乎pattern未加上括號只能用\0去取變數

(2)

$str = 'aabbccddeeffccggiiCC';
$str = preg_replace('/(cc)/i', "[\\1]", $str);    //忽略大小寫 , 結果為 : aabb[cc]ddeeff[cc]ggii[CC]
echo $str.'<br>';

註 : pattern 對要比對的字串加上括號 , \\1 是取得比對到的字串 , 最後將比對到的字串加上中括號

其他

pattern escape

  • / : \\/
  • ( : \(
  • ) : \)
  • . : \.

擲出第一張長寬各大於100的圖, 如果都沒有就擲第一張圖

$url = $this->input->get_post('url');
if ( ! $html_content = @file_get_contents($url))
{
    return $this->_outputJSON(array('status' => 'fail', 'error' => 'Wrong url'));
}
$patten = "/<img.*src=\"(((http[s]?):\/\/|www\.)([^\s\[\\\"'])+)\".*>/";
if (preg_match_all($patten, $html_content, $matches, PREG_SET_ORDER))
{
    foreach ($matches as $match)
    {
        list($width, $height) = getimagesize($match[1]);
        if ($width >= 100 OR $height >= 100)
        {
            return $this->_outputJSON(array('status' => 'ok', 'url' => $match[1]));
        }
    }
    return $this->_outputJSON(array('status' => 'ok', 'url' => $matches[0][1]));
}
return $this->_outputJSON(array('status' => 'fail', 'error' => 'Image not found'));

如果只要單一match的話使用

if (preg_match($patten, $html_content, $matches, PREG_OFFSET_CAPTURE))

比對 src=" ... " 符合相對路徑的 path

$reg = '(src=["|\'](?!.*(http[s]?))(?!.*(www\.))[\/]?(([^\s\[\\"\'])+)["|\'])';

match連結並且顯示頭4個字元

$message = 'google : www.google.com.tw ,yahoo : http://www.google.com.tw';
$message = preg_replace("/(((http[s]?):\/\/|www\.)([^\s\[\\\"'])+)/", substr("$0", 0, 4), $message);
echo $message;

錯誤結果:

    google : www.google.com.tw ,yahoo : http://www.google.com.tw

match到但放到substr裡沒反應

改成:

$message = 'google : www.google.com.tw ,yahoo : http://www.google.com.tw';
$message = preg_replace_callback(
    "/(((http[s]?):\/\/|www\.)([^\s\[\\\"'])+)/",
    function ($matches) {
        if (substr(strtolower($matches[0]), 0, 4) == 'www.')
        {
            return '<a href="http://'. $matches[0] .'" target="_blank">'. $matches[0] .'</a>';
        }
        return '<a href="'. $matches[0] .'" target="_blank">'. $matches[0] .'</a>';
    },
    $message
);
return $message;

得到預期的結果:

google : www. ,yahoo : http

Javascript - RegExp(正則表達式) 語法

match

example 1

str = 'aabbddeeffggiiCC';
reFee=/cc/i;                        //補上i(忽略大小寫) 才會為true
document.write(reFee.test(str));    //檢查有沒有cc,會返回 true

example 2

str = 'aabbddeeffCCggii';
reFee = new RegExp("cc","i");       //沒補上"i"會返回false , 補上"i"是true
alert(reFee.test(str));             //檢查有沒有cc,會返回true

replace

str = 'aabbccddeeffCCggiicc';
var re = new  RegExp('cc');                     // 只比對第一個結果 : aabb哈哈ddeeffCCggiicc
//var re = new  RegExp('cc','g');               // 字串為'cc'的全部比對 , 結果為 : aabb哈哈ddeeffCCggii哈哈
//var re = new  RegExp('cc','ig');              // 不分大小寫的全部比對 , 結果為 : aabb哈哈ddeeff哈哈ggii哈哈
document.write(str.replace(re,'哈哈'));

取得比對結果存進RegExp變數 :

var str = 'aabbccddeeffccggiiCC';
var re =  new RegExp(/(cc)/ig);                 //忽略大小寫及全部比對,才會為true
document.write(str.replace(re,'[$1]'));         //結果為 : aabb[cc]ddeeff[cc]ggii[CC]
var result = str.match(re);                     //結果為 cc,cc,CC
for(var item in result){
    alert(result[item]);                        //分別alert出 cc ; cc ; CC
}

format 1000000 > $ 1,000,000

"$ " + x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");

其他

處理圖片將 [img] 轉為 <img>

function con_img_code(str)
{
    var reg = new RegExp(/\[img src='((http[s]{0,1}|ftp):\/\/[a-zA-Z0-9\.\-\_\/]+\.([a-zA-Z]{2,4})(:\d+)?(\/[a-zA-Z0-9\.\-~!@#$%^&*+?:_\/=<>]*)?)']/g);
    var str2 = str.replace(reg, "<img class="width_700" src="$1">");
    str2 = str2.replace(/  /g, "&nbsp;&nbsp;");
    str2 = str2.replace(/\n/g,"<br />");
    return str2;
}

註 :

  1. 從資料庫撈出資料並且找出符合的圖片格式
  2. 取代成 <img> 並且指定 class 屬性
  3. 第5行跟第6行非必要,主要是處理空白及換行

正則表達式, 符號對照及範例

需要被脫逸的符號 :

  • \ : 可以脫逸字元
  • [ ] : [xyz] 比對中括孤內的任一個字元 , 但不會將配對到的字串存入RegExp變數中
  • ( ) : 合起來 (x) 比對x並將符合的部份存入一個變數, /(a) and (b)/ 可以對 ‘aaa and bb’ 中的’aaa’和’bb’, 並將這兩個比對得到的字串設定至數字 RegExp.$1 和 RegExp.$2
  • . : 比對任何一個字元 , 但換行符號不算
  • ^ : 啟始位置
  • $ : 結束位置
  • * : 比對前一個字元 , 零次或更多次 , 等於{0,}
  • ? : 比對前一個字元 , 零次或一次 , 等於{0,1}
  • + : 比對前一個字元 , 一次或更多次 , 也等於 {1,}
  • | : 或者 , 等於OR , 邏輯運算子(Logical Operators)

左中括號([)需脫逸 , 但右中括號(])不需脫逸 , 左下到右上的斜線(/)也需要脫逸

符號對照 :

  • \d = [0-9] : 0~9(數字)
  • \D = [^0-9][^\d] : 非數字
  • \l = [a-z] : 小寫英文
  • \L = [^a-z] : 非小寫英文
  • \u = [A-Z] : 大寫英文
  • \U = [^A-Z] : 非大寫英文
  • \n : 比對換行符號
  • \r : 比對 carriage return
  • \s : 比對任一個空白字元(White space character) , 等於 [\f\n\r\t\v]
  • \S : 比對任一個非空白字元 , 等於 [^ \f\n\r\t\v]
  • \t : 比對字位字元(Tab)
  • \v : 比對垂直定位字元(Vertical tab)
  • \w : 比對數字、字母字元或底線 , 等於 [A-Za-z0-9_]
  • \W : 比對非數字、字母字元或底線 , 等於 [^A-Za-z0-9_]
  • \K : start at this position
  • .{6,30} : 6 < 字串長度 < 30
  • ?=.* : 用來判斷右邊緊接的字元是否符合比對條件
  • (?!.*[^\x21-\x7e]) : 不允許特殊符號、數字、英文以外的字元
  • (?=.{10,}) : 檢查字元長度是否超過10
  • (?!.*[\W]) : 不允許非(數字、字母、底線)的字元
  • (?=.*\d):這是 Positive Lookahead,用來判斷右邊緊接著的字元是否符合比對條件,如果符合條件才會繼續比對下去。拿這個實例來說,右邊的字元必須包含一個數字才算符合這個條件。
  • (?=.*[a-zA-Z]):跟上面出現過得 Positive Lookahead 一樣,這是說右邊的字必須包含一個 a 到 z 或是 A 到 Z 的字元,說穿了就是右邊的字要包含一個英文字的意思。

比對方式 :

  • g:全域比對(Global match)
  • i:忽略大小寫(Ignore case)
  • s : 表示我要進行跨行(\n)做比對 //似乎沒有這功能 , 有待查證
  • o : 表示我只要比對一次 //似乎沒有這功能 , 有待查證
  • gi:全域比對並忽略大小寫

語法結構 :

re = /pattern/flag
re = new RegExp("pattern", "flag");

pattern 是正規表示法比對規則,flag 則是比對的方式

一些範例 :

數量 : 不能為0的正整數

/^[1-9]\d*$/
  • [1-9] : 開頭要1~9
  • \d* : 0~9的數字出現 0個或多個

價格 : 0 或是正整數

/^(0|[1-9]\d*)$/

0 : 零, | : 或者, [1-9]\d* : 正整數

整數或最多兩位小數

/^(0|[1-9]\d*)(\.\d{1,2})?$/

中文姓名 : 檢查中文字2~5個字

javascript :

/^[\u4e00-\u9fa5]{2,5}$/

中文的正則, 如果把javascript語法搬到php會出現 Warning : preg_match()[function.preg-match] : Compilation failed : PCRE does not support \L , \I , \N , \U or \u at offset 9 的錯誤

解決方法 : 將\u改成\x , 並且將後面的中文代碼用{ } 括起來 , 在表示法最後的斜線後加一個 u

php :

/^[\x{4e00}-\x{9fa5}]{2,5}$/u
  • [\x{4e00}-\x{9fa5}] : 繁體中文字
  • {2,5} : 出現2~5次

email : email格式

/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/

室內電話 : 離鳥區碼最多4碼 , 後面最少5碼最多8碼 . 例如 : 02-42365514

/^[0]{1}[0-9]{1,3}[-]{1}[0-9]{5,8}$/     (前面4碼後面8碼 , 會超過10碼條件 , 所以要再判斷一次 , 如下一行)
/^[0-9-]{10,11}$/                        (共10或11碼)

要驗證兩次 , 如果下次有空的話再改成一行表示法 , 因為很久沒用這個 , 所以有點懶

地址 : 禁止中,英,數,減號( - )以外的字元,至少要連續中文4個字,至少一位數字,字元數共8~40位

/^(?!.*[^\u4e00-\u9fa5a-zA-Z0-9-])(?=.*[\u4e00-\u9fa5]{4,})(?=.*\d).{8,40}$/

網址

/((http[s]{0,1}|ftp):\/\/[a-zA-Z0-9\.\-]+\.([a-zA-Z]{2,4})(:\d+)?(\/[a-zA-Z0-9\.\-~!@#$%^&amp;*+?:_\/=<>]*)?)/g
or
((http[s]{0,1}|ftp):\/\/[a-zA-Z0-9\.\-\_\/]+\.([a-zA-Z]{2,4})(:\d+)?(\/[a-zA-Z0-9\.\-~!@#$%^&amp;*+?:_\/=<>]*)?)

正規取代驗證多餘空白:

^(.*[^\s])?\s+$`    取代成 `\1
  • ( )裡的東西代表$1
  • [^\s] 結尾無空白
  • \s+結尾為空白

驗證密碼 至少各一位英文字母、數字,共8~12位 :

/^(?!.*[^a-zA-Z0-9])(?=.*\d)(?=.*[a-zA-Z]).{8,12}$/
  • `.{6,30}:表示字串長度要大於等於 6,小於等於 30。
  • ?=.* : 用來判斷右邊緊接著的字元是否符合比對條件,如果符合條件才會繼續比對下去。
  • (?!.*[^\x21-\x7e]) : 不允許特殊符號, 數字, 英文字母以外的字元輸入
  • (?=.{10,}) : 檢查輸入的字元長度是否有超過 10. 如果資料庫裡的密碼欄位長度是 20, 那就改成 (?=.{10,20}).
  • (?!.*[\W]) : 不允許非 (數字、字母、底線)的字元 \W [^a-zA-Z0-9] \w=[a-zA-Z0-9]
  • .*:表示非 “\r”、”\n” 字元出現了 0 或一次以上。

解析: 第一個是判斷不能有小寫及數字以外的字元,第二個是數字至少要有一個,第三個是小寫至少要有一個,第四個是要8~12位

不允許逗號及從頭到尾都是0

^(?!.*[,])(?!.*(^0+$))(.*)$

含有中文字元

(.*?\p{Han})

前後不能有空白

^[^\s]+[A-Za-z\s]{2,21}[^\s]+$

結果 :

  • ABC DEF : true
  • ABC DEF : false
  • ABC DEF : false

[^\s]+ 代表不能有空白, 所以在頭尾各放一個去限制頭尾不能有空白

檢查 05:30 時間格式

^([0-1]+[0-9]|2[0-3]):[0-5][0-9]$

只 match 中間的部份

字串

import javax.faces.application.FacesMessage;

regex

(?<=import ).*?(?=;)

結果

javax.faces.application.FacesMessage

\K example

$ echo 'hello world' | grep -oP 'hello \K(world)'
world
$ echo 'hello world' | grep -oP 'hello (world)'
hello world

配對尾數後面是 0+

^(\d+[1-9]\.?|\d+\.\d+[1-9])(0+)$
  • 前面的 ( … ) 是配對 match 1, 但因為有分整數跟小數, 所以判斷式要分開寫, 用 | (or) 隔開
  • 後面的 (0+) 是配對 match 2

配對結果

  • 12304000 - match 1: 12304, match 2: 000
  • 0.33444400 - match 1: 0.33444, match 2: 00
  • 123.00324000 - match 1: 123.00324, match 2: 000
  • 177.0000 - match 1: 177, match 2: 0000
  • 0.1771 - not matched
  • 10.87 - not matched
  • 10.0087 - not matched
  • 10.872000 - match 1: 10.872, match 2: 000

allowed me any chars , but it cant allowed (0-9) numbers

^([^0-9]*)$

不允許字串 not

^(?!.*not).*$

配對結果

  • You can not see me. (not matched)
  • Hi, can you do it?
  • Don’t monkey around.
  • Do not touch it. (not matched)

開頭不是某個字元

1) 開頭不是 my

^(?!my)\w+$
  • my name is Cathy
  • her name is Lily (matched)
  • this build is amazing
  • my chest is sore (matched)

2) 開頭不是 1/3/5

^(?!.*[135]).*$
  • 1
  • 2 (matched)
  • 3
  • 4 (matched)
  • 5