Tag: PHP

[PHP] 為什麼應該使用MySQL Native Driver(mysqlnd)來取代MySQL Client Library(libmysqlclient)

還記得以前第一次自己架設LAMP開發環境的時候,就已經注意到安裝有這兩個package了

但那時候還不曉得有何差別,只記得書上叫我裝php5-mysql就照著裝XD

前陣子碰到一個需要安裝mysqlnd才能解決的問題,正好趁這個機會來瞭解一下差別

MySQL Native Driver is a replacement for the MySQL Client Library (libmysqlclient)

在PHP文件的myslqnd章節內一開頭就說明了mysqlnd是用來取代libmysqlclient

為何PHP官方會建議使用mysqlnd?

 

mysqlnd是屬於PHP Project的一部份

不像libmysqlclient是使用MySQL license,mysqlnd是使用PHP license這也避免了一些因為license所衍生的問題(看看前陣子Oracle告Google的新聞就知道為什麼了!)

mysqlnd的功能比libmysqlclient還多

從PHP 5.3.0開始mysqlnd library 已經是內建在PHP的library,mysqlnd提供的一些功能像是query caching、lazy connections、SSL這些在libmysqlclient的沒辦法用的

詳細差異請參閱Library feature comparison

mysqlnd的效能比libmysqlclient還好

由於mysqlnd是用C寫的PHP extension,所以它是使用PHP memory management system也支援PHP memory limit和memory_get_usage(),安裝mysqlnd可以使用memory_get_usage()來追蹤記憶體的使用情形,而是在libmysqlclient是不可能的,因為libmysqlclient要使用C語言的function malloc(),在官網有舉一個例子

One example of the memory efficiency is the fact that when using the MySQL Client Library, each row is stored in memory twice, whereas with the MySQL Native Driver each row is only stored once in memory.

libmysqlclient在儲存每一筆資料row到memory的時候會存兩次

而mysqlnd只會存一次

 

參考來源:

http://php.net/manual/en/mysqlnd.overview.php

http://php.net/manual/en/mysqlinfo.library.choosing.php

http://php.net/manual/en/intro.mysqlnd.php

https://dev.mysql.com/downloads/connector/php-mysqlnd/

Advertisements

[PHP] 使用 json_encode 的字串編碼必須為UTF-8

剛好有機會碰到這個問題,雖然發現原因也解決了,但也花了我一些時間,所以…筆記筆記

只要有利用PHP寫過API或將server side的資料吐給client side用js處理相關的應用,對PHP的json_encode應該不陌生,但這個好東西使用上也有一些限制,其中有一項就是它只吃UTF-8編碼的字串!官方文件也有提到:

All string data must be UTF-8 encoded.

因為最近碰到DB的charset不是UTF-8,然後我又依照以前的方式,把資料從DB撈出來之後就直接:

echo json_encode($arr);

但這會有問題,例如字串的編碼不是UTF-8,像這樣:

$arr = array(
'id' => 1,
'description' => '我不是UTF-8編碼的字串唷!'
);
echo json_encode($arr);

echo的結果會是:

{"id":1,"description":null}

一開始的時候還以為是我下的SQL select statement沒寫好,後來才發現是編碼的問題,這邊有兩個方法解決這個問題

1.使用urlencode和urldecode

原理:先使用urlencode將非UTF-8的字串轉換,然後echo json_encode的時候在urldecode:

echo urldecode(json_encode($str));

2.使用mb_convert_encodeing或iconv

先將非UTF-8的字串轉為UTF-8,然後在echo json_encode時,再把字串轉回非UTF-8編碼

echo mb_convert_encoding($str, "UTF-8", "BIG5");

恍然小悟

確認是編碼的問題後,也讓我突然理解為何echo json_encode($str)的中文字在瀏覽器上看都會變成\uxxxx

[Codeingter]config.php的cookie_secure

前言

前陣子的在開發的時候,有一項是要在登入頁做remeber me的功能

這其實還滿簡單的,畢竟只要有買過php的書幾乎都會有範例

不同的是之前是純粹用PHP寫,現在用的是CI

以前沒有用過CI提供的cookie存取功能,但有看過config檔裡面有相關的設定


/*
|--------------------------------------------------------------------------
| Cookie Related Variables
|--------------------------------------------------------------------------
|
| 'cookie_prefix' = Set a prefix if you need to avoid collisions
| 'cookie_domain' = Set to .your-domain.com for site-wide cookies
| 'cookie_path' = Typically will be a forward slash
| 'cookie_secure' = Cookies will only be set if a secure HTTPS connection exists.
|
*/
$config['cookie_prefix'] = "c_";
$config['cookie_domain'] = $_SERVER['HTTP_HOST'];
$config['cookie_path'] = "/";
$config['cookie_secure'] = TRUE;

所以就先設定了一下,但我當下沒有發現自己犯了一個錯…(官方的說明文件也沒有寫得很清楚,還是看code最準XD)

把remeber me功能的做完的時候,當下測試的時候發現登入功能GG了(…WTF)

一開始是馬上去看我剛剛寫的程式是不是有BUG,但還真的沒有啊…

神奇的是我把剛剛寫的code刪光光,登入功能依然GG…

所以我就開始想想我寫code之前做了什麼事?

config~我改了config,但我只是設定cookie相關的config而已

也沒有更動到session的東西啊….

秉持著爬source code的精神,終於讓我釐清事情的來龍去脈…容我慢慢道來

文件vs註解

我在改config相關的cookie設定前有先去看官方的說明文件

其中有一項是設定$config[‘cookie_secure’]

文件是這樣寫的:

The secure boolean is only needed if you want to make it a secure cookie by setting it to TRUE.

所以我就把cookie_secure設為TRUE <—(這就是我犯的錯誤)

但後來在檢查config.php的時候我發現它的註解的解釋是:

/*
| 'cookie_secure' = Cookies will only be set if a secure HTTPS connection exists.
*/

所以,如果將CI config.php的cookie_secure的參數設為TRUE

代表利用$this->input->set_cookie()設定的cookie只能透過https的方式傳輸!

但我做的那個網站並沒有用https…這就是最根本的問題 (如果官方的文件和助解一樣的話我應該就不會把cookie_secure設成TRUE了)

還有還有

debug到這邊應該就可以告一個段落了~~

但是!!我還是沒搞懂為何cookie_secure設為TRUE之後,我TMD的就不能登入

基於爬爬source code的精神,發現..在/system/libraries/Session.php有一段程式碼解決了我最後一個疑惑:


 // Fetch the cookie
 $session = $this->CI->input->cookie($this->sess_cookie_name);

 // No cookie? Goodbye cruel world!...
 if ($session === FALSE)
 {
   log_message('debug', 'A session cookie was not found.');
   return FALSE;
 }

 // Decrypt the cookie data
 if ($this->sess_encrypt_cookie == TRUE)
 {
   $session = $this->CI->encrypt->decode($session);
 }
 else
 {
   // encryption was not used, so we need to check the md5 hash
   $hash = substr($session, strlen($session)-32); // get last 32 chars
   $session = substr($session, 0, strlen($session)-32);

   // Does the md5 hash match? This is to prevent manipulation of session data in userspace
   if ($hash !== md5($session.$this->encryption_key))
   {
     log_message('error', 'The session cookie data did not    match what was expected. This could be a possible hacking attempt.');
     $this->sess_destroy();
     return FALSE;
   }
 }

CI在讀取seesion的時候會有一個檢查機制

1.先檢查Server上的session和client端cookie裡面加密後的session是否存在

2.檢查是否為加密的值

3.比對seesion和cookie是否match

總結因果

1.目前開發的網站不是走HTTPS

2.把config.php的cookie_secure的參數設為TRUE

3.帳密都輸入正確時,程式存一個seesion在Server上,CI則自動在cookie端存一個加密後的session值

4.轉跳到登入後的頁面時,會檢查是否有登入,但這個機制觸發了..

5.CI在讀取seesion時會去比對seesion和cookie是否match

6.但無法取得這個cookie的值…所以return FALSE…死在

log_message('debug', 'A session cookie was not found.');

 

 

 

[Codeingter] 回傳insert、update、delete的執行結果

話說我自己在做MVC開發的時後,有個習慣就是一定要回傳SQL的執行結果

因為這關係到在controller的寫法

但,前幾天在開發的時候遇到一個問題,明明table的資料沒有更新到,但卻回傳true

花了一點時間看了ci的source code和Google

證明是我的理解錯誤…

例如:這邊return的用意是讓我可以在controller判斷在Model的執行結果


public function updateData($id, $data){

  $this->db->where('id', $id);
  return $this->db->update('mytable', $data);

}

但是!!這裡需要理解的地方是$this->db->update() return的結果是「執行update sql是否成功」,不代表資料是否有被更新

所以,比較好的寫法是:


public function updateData($id, $data){

  $this->db->where('id', $id);
           ->update('mytable', $data);

  return ($this->db->affected_rows() > 0) ? TRUE : FALSE; 

}

更簡潔的寫法


public function updateData($id, $data){

  $this->db->where('id', $id);
           ->update('mytable', $data);

  return $this->db->affected_rows() > 0;

}

這樣做的好處是,在controller可以明確的知道table的資料到底有沒有被異動

[PHP]關於上傳檔案的亂數命名方式

以前的習慣-1:

$rand_name = md5(rand(0, 1000)).$ext;

以前的習慣-2:

$rand_name = md5($_SESSION['id'] . rand(0, 1000)).$ext;

現在認為更好的方式:

$encrypt_name= md5(uniqid(mt_rand())).$ext;

原因:

uniqid() — Generate a unique ID

可以產生在毫秒內百萬分之一秒內不重複的ID字串

mt_rand()-Generate a better random value

產生亂數的速度比rand()快四倍

參考來源:

http://php.net/manual/en/function.mt-rand.php

http://php.net/manual/en/function.uniqid.php

PHP var keyword

這幾天再複習PHP OOP的觀念,有一個我覺得怪怪的地方,就是PHP 宣告class member variable的方法一開始我看的書是說用var keyword,而且用var來宣告member variable預設的Property Visibility是public,但是書本後面又說宣告member variable也可以用public private protected,但我不懂這兩種方法差在哪裡?後來去翻了一下PHP Manual,解解惑~

PHP4

A class is a collection of variables and functions working with these variables. Variables are defined by var and functions by function. A class is defined using the following syntax:

PHP5

Class properties must be defined as public, private, or protected. If declared using var, the property will be defined as public.


Note: The PHP 4 method of declaring a variable with the var keyword is still supported for compatibility reasons (as a synonym for the public keyword). In PHP 5 before 5.1.3, its usage would generate an E_STRICT warning.

總結:在PHP4宣告member variable得用var keyword,但是在PHP5可以使用public private protected這些keyword,所以已經可以不必再使用var keyword,雖然在PHP5還是有相容這部分,但如果考慮到程式碼的嚴謹性,盡量避免使用比較好。