Aug 25
PHP 自 5.3.0 開始,引入 mysqlnd 這個 extension。
在 5.3.0 的 Full ChangeLog 中可以看到這行:
Added mysqlnd extension as replacement for libmysql for ext/mysql, mysqli and PDO_mysql. (Andrey, Johannes, Ulf)
依照個人的過往經驗,native driver 的效能都會比較好,而 mysqlnd 也不例外(有興趣的可以看看這篇文章)。
比較可惜的是,mysqlnd 在 Windows 平台的 PHP 是預設套件;在 *NIX 平台,使用前必須在編譯前就先做好設定(configure;參數可參考 PHP 官方的mysqlnd 安裝文件)。
剛才,很高興看到 FreeBSD 的 PHP5 ports 加入了 option,讓我們可以輕鬆搞定。
Technorati Tags: FreeBSD, MySQL, mysqlnd, PHP
(Visited 630 times)
Aug 08
發現 MongoDB 是好一陣子前的事,而我也在某台機器上把它裝起來。
之前玩的時候,發現 MongoDB 不須帳號與密碼就可以連接,而且也無法配置使用者權限,所以把它丟在旁邊。
前幾天,我在網路上看到網友詢問 MongoDB 有沒有辦法作 JOIN,得到的答案是否定的。
趁著今天在家閒閒沒事作,書也看完幾個段落,就透過 PHP 來試試這種 ODBMS 如何實作資料關聯。
MongoDB 與常見的資料庫(例如:MySQL)有些微的不同:
|
資料庫 |
資料表 |
| MongoDB 稱之為 |
Database(DB) |
Collection |
| MySQL 稱之為 |
Database(DB) |
Table |
先把這個觀念講清楚,底下的範例程式碼才不會看得霧煞煞。
首先,先建立 DB 與 Collection:
// 連接 MongoDB
$m = new Mongo();
// 連接資料庫,名稱就是 test
$testDB = $m->selectDB( 'test' );
// 建立 Collection,名稱分別是 user 與 sex
$testDB->createCollection( 'user' );
$testDB->createCollection( 'sex' );
- MongoDB 沒有 createDB 這種指令。只要選擇資料庫,建立 Collection 之後,系統就會自動產生 DB。
- 指令列模式下,選擇資料庫的指令跟 MySQL 相同(USE DB_NAME)。
再來,存入性別資料:
// 連接 MongoDB
$m = new Mongo();
// 連接資料庫,名稱就是 test
$testDB = $m->selectDB( 'test' );
// 在 sex 這個 Collection 裡面放入資料,好讓程式分辨男性與女性
$testDB->sex->insert( array('sex_name' => 'Female') );
$testDB->sex->insert( array('sex_name' => 'Male') );
// 把 sex 這個 Collection 的資料倒出來看
$cursor = $testDB->sex->find();
$array = iterator_to_array($cursor);
var_dump($array);
- 以往我們儲存在 Table 的 data row 會有個 id 值,方便我們建立關聯。MongoDB 這種資料庫則是在 Collection 裡面放物件,可以不需要 id 值。
開始存入使用者名稱,並紀錄他(她)們的性別:
// 連接 MongoDB
$m = new Mongo();
// 連接資料庫,名稱就是 test
$testDB = $m->selectDB( 'test' );
// 找出男性資料,並取得關聯值
$male = $testDB->sex->findOne( array('sex_name' => 'Male') );
$refMale = $testDB->sex->createDBRef( $male );
// 找出女性資料,並取得關聯值
$female = $testDB->sex->findOne( array('sex_name' => 'Female') );
$refFemale = $testDB->sex->createDBRef( $female );
// 存入使用者資料
$testDB->user->insert( array('name' => 'BoyName', 'sex' => $refMale) );
$testDB->user->insert( array('name' => 'GirlName', 'sex' => $refFemale) );
// 取出並顯示使用者資料
$cursor = $testDB->user->find();
$array = iterator_to_array($cursor);
foreach ( $array as $user ) {
// 找出性別的關聯物件
$sexRef = $testDB->user->getDBRef($user['sex']);
echo "Name: {$user['name']}\tSex: {$sexRef['sex_name']}\n";
}
以上,簡單的試玩心得。
Technorati Tags: Database, MongoDB, Object, ODBMS, PHP
(Visited 922 times)
Jul 14
昨晚在 PTT 的 PHP 板回了一篇文章,提到 PHP 的內建常數;現在來分享一下,順便作個紀錄,方便日後查詢。
PHP 有不少實用的內建常數,方便我們寫程式時直接呼叫。
有興趣研究的人,可以看看這個網頁,或是用 get_defined_constants() 把內建常數全部倒進陣列,再用 var_dump() 或 print_r() 顯示。
下表列出一些我覺得常用的內建常數,範例值來自 64 位元版本的 FreeBSD ports 安裝之 PHP 5.3.2:
| 常數名稱 |
常數型態 |
範例值或說明 |
可用版本 |
| PHP_VERSION |
string |
"5.3.2" |
無限制 |
| PHP_MAJOR_VERSION |
integer |
5 |
5.2.7+ |
| PHP_MINOR_VERSION |
integer |
3 |
5.2.7+ |
| PHP_RELEASE_VERSION |
integer |
2 |
5.2.7+ |
| PHP_EXTRA_VERSION |
string |
"" |
5.2.7+ |
| PHP_OS |
string |
"FreeBSD" |
無限制 |
| PHP_PREFIX |
string |
"/usr/local" |
4.3.0+ |
| PHP_BINDIR |
string |
"/usr/local/bin" |
無限制 |
| PHP_LIBDIR |
string |
"/usr/local/lib/php" |
無限制 |
| PHP_DATADIR |
string |
"${prefix}/share" |
無限制 |
| PHP_SYSCONFDIR |
string |
"/usr/local/etc" |
無限制 |
| PHP_LOCALSTATEDIR |
string |
"/usr/local/var" |
無限制 |
| PHP_CONFIG_FILE_PATH |
string |
"/usr/local/etc" |
無限制 |
| PHP_CONFIG_FILE_SCAN_DIR |
string |
"/usr/local/etc/php" |
無限制 |
| PHP_SHLIB_SUFFIX |
string |
"so" |
4.3.0+ |
| PHP_EOL |
string |
此變數可用來判斷 "\n"、"\r"、"\r\n"(三個通吃,超好用)。 |
4.3.0+ |
| PHP_MAXPATHLEN |
integer |
1024 |
5.3.0+ |
| PHP_INT_MAX |
integer |
9223372036854775807 |
4.0.4+ 與 5.0.5+ |
| DATE_COOKIE |
string |
"l, d-M-y H:i:s T"
輸出範例:
Wednesday, 14-Jul-10 20:25:07 CST
|
5.1.1+ |
| DATE_ISO8601 |
string |
"Y-m-d\TH:i:sO"
輸出範例:
2010-07-14T20:26:18+0800
|
5.1.1+ |
| DATE_RFC822 |
string |
"D, d M y H:i:s O"
輸出範例:
Wed, 14 Jul 10 20:27:39 +0800
|
5.1.1+ |
| DATE_RFC850 |
string |
"l, d-M-y H:i:s T"
輸出範例:
Wednesday, 14-Jul-10 20:28:44 CST
|
5.1.1+ |
| DATE_RFC1036 |
string |
"D, d M y H:i:s O"
輸出範例:
Wed, 14 Jul 10 20:29:40 +0800
|
5.1.1+ |
DATE_RFC1123
DATE_RSS
|
string |
"D, d M Y H:i:s T"
輸出範例:
Wed, 14 Jul 2010 20:31:51 CST
|
5.1.1+ |
|
DATE_RFC2822
|
string |
"D, d M Y H:i:s O"
輸出範例:
Wed, 14 Jul 2010 20:31:51 +0800
|
5.1.1+ |
DATE_RFC3339
DATE_ATOM
DATE_W3C
|
string |
"Y-m-d\TH:i:sP"
輸出範例:
2010-07-14T20:36:18+08:00
|
5.1.1+ |
Technorati Tags: constant, PHP
(Visited 1028 times)
May 27
以前,我喜歡用 ADOdb 來連接資料庫;自從 PDO 被放進 PHP 5.1 後,它便成了我的新寵...
我在 PHP 5.1.6 環境上幫人家抓蟲時,看到以下兩段程式。
-
$pdo = New PDO(......);
$sql = "SELECT .....";
$st = $pdo->prepare($sql);
$st->execute();
$rows = $st->fetchAll();
-
$pdo = New PDO(......);
$sql = "SELECT .....";
$st = $pdo->query($sql);
$rows = $st->fetchAll();
雖然以上兩段程式都可以取出 $rows ,但重複並交叉執行的話,就會出現問題。
- 這段程式可以正確取出 $rows1 與 $rows2:
$pdo = New PDO(......);
$sql = "SELECT .....";
$st = $pdo->query($sql);
$rows1 = $st->fetchAll();
$sql = "SELECT .....";
$st = $pdo->prepare($sql);
$st->execute();
$rows2 = $st->fetchAll();
- 這段程式只能正確取出 $rows1 ,$rows2 會是個空的陣列:
$pdo = New PDO(......);
$sql = "SELECT .....";
$st = $pdo->prepare($sql);
$st->execute();
$rows1 = $st->fetchAll();
$sql = "SELECT .....";
$st = $pdo->query($sql);
$rows2 = $st->fetchAll();
這問題有兩種解法:
Technorati Tags: PDO, PDOStatement, PHP
(Visited 2530 times)
Apr 16
FreeBSD ports tree 的 lang/php5 升級到 5.3.2, www/eaccelerator 也升級到 0.9.6。
但是... 目前,FreeBSD ports tree 的 www/pecl-APC 依然是 3.0.19,無法在 PHP 5.3 運作;為了讓它正常運作,jnlin送過 PR(我沒有先查詢,也送了一個
)。
其實 eAccelerator 0.9.6 在 2010 年 2 月初就已經釋出了,可是,從 changelog 看來,很多不錯的功能被砍掉(反璞歸真?):
Changes in this version:
* Support for PHP 5.3.
* The encoder is removed
* The user cache functions are removed
* The session handler is removed
* The minimal PHP version supported is now 5.1
* Some internal refactoring to clean up the code
* Fixed some bugs (and probably added some)
這麼看來,2008 年的這篇文章的參考價值就低了些...
為了滿足我自己的好奇心,就把 APC 3.1.3p1 裝起來測試看看。
系統/硬體:
- CPU: Intel(R) Pentium(R) 4 CPU 3.00GHz
- Memory: 6G(DDR2 800 MHz, Dual-Channel)
- FreeBSD 8.0-RELEASE-p2 amd64
- Apache HTTPD 2.2.14
- PHP 5.3.2
設定:
- eAccelerator 0.9.6:
eaccelerator.shm_size="128"
eaccelerator.cache_dir="/tmp/eaccelerator"
eaccelerator.enable="1"
eaccelerator.optimizer="1"
eaccelerator.debug="0"
eaccelerator.log_file="/var/log/eaccelerator.log"
eaccelerator.name_space = ""
eaccelerator.check_mtime="1"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="0"
eaccelerator.shm_prune_period="0"
eaccelerator.shm_only="0"
eaccelerator.compress="1"
eaccelerator.compress_level="9"
eaccelerator.keys="shm_and_disk"
- APC 3.1.3p1:
apc.enabled=1
apc.shm_segments=1
apc.shm_size=128
apc.ttl=7200
apc.user_ttl=7200
apc.num_files_hint=1024
apc.mmap_file_mask=/tmp/apc.cache
apc.enable_cli=1
測試結果(ab -c5 -n500):
|
Pure PHP |
eAccelerator |
APC |
| WordPress 2.9.2 |
3.00 req./sec. 332.834 ms/req. |
7.61 req./sec. 131.487 ms/req. |
8.87 req./sec. 112.750 ms/req. |
| MediaWiki 1.15.3 |
1.81 req./sec. 552.822 ms/req. |
2.75 req./sec. 363.124 ms/req. |
5.93 req./sec. 168.580 ms/req. |
| Gallery 2.3.1 |
3.51 req./sec. 284.979 ms/req. |
7.91 req./sec. 126.421 ms/req. |
7.38 req./sec. 135.506 ms/req. |
依照結果看來,APC 3.1.3p1 的確略勝 eAccelerator 0.9.6。 
可惜的是.. Xcache 目前的最新版本(1.3.0)沒辦法在 PHP 5.3 跑...
Technorati Tags: Apache, APC, benchmark, eAccelerator, PHP, ports
(Visited 3128 times)
Mar 08
我利用前兩天的空閒時間寫了個 PHP 的小程式,運用 PHP 的 PDO 元件,對 MySQL 進行小測試。
Server 作業系統與硬體:
- 作業系統:FreeBSD 8.0-RELEASE
- CPU:Intel(R) Pentium(R) 4 CPU 3.00GHz (3042.62-MHz 686-class CPU),Hyper-Threading 開啟
- RAM:2G DDR2 800
- 放資料庫的 HDD:WDC WD800JB-00JJA0,UDMA 100
MySQL 5.1.44 的設定(/etc/my.cnf):
# defult setting (maybe changed)
key_buffer_size = 128M
max_allowed_packet = 4M
table_open_cache = 4096
sort_buffer_size = 1M
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M
# added for tunning by Joe Horn
skip-name-resolve
connect_timeout = 60
join_buffer_size = 1M
max_connect_errors = 10000
max_connections = 100
max_heap_table_size = 1G
query_cache_size = 16M
slave_net_timeout = 30
sync_binlog=1
thread_cache_size = 512
thread_concurrency = 8
tmp_table_size = 1G
table_definition_cache = 512
# choose one ( depend on query amount )
#concurrent_insert=2
low_priority_updates=1
這台機器跑 Super Smack 的結果:
# super-smack -d mysql update-select.smack 80 1000
Query Barrel Report for client smacker
connect: max=14ms min=1ms avg= 4ms from 80 clients
Query_type num_queries max_time min_time q_per_s
select_index 80000 101 6 717.91
update_index 80000 108 1 717.91
我用測試程式產生了一百萬筆資料的 MyISAM table,各種測試各循環 1000 次,產生如下的測試結果(後面的數字單位是秒):
SELECT BY PK_COL WITH_QUERY_CACHE: 0.058
SELECT BY PK_COL WITHOUT_QUERY_CACHE: 0.062
SELECT BY PK_COL LIMIT WITH_QUERY_CACHE: 0.058
SELECT BY PK_COL LIMIT WITHOUT_QUERY_CACHE: 0.062
SELECT BY UNIQUE_COL WITH_QUERY_CACHE: 0.058
SELECT BY UNIQUE_COL WITHOUT_QUERY_CACHE: 0.062
SELECT BY UNIQUE_COL LIMIT WITH_QUERY_CACHE: 0.058
SELECT BY UNIQUE_COL LIMIT WITHOUT_QUERY_CACHE: 0.062
SELECT BY INDEX_COL WITH_QUERY_CACHE: 0.058
SELECT BY INDEX_COL WITHOUT_QUERY_CACHE: 0.061
SELECT BY INDEX_COL LIMIT WITH_QUERY_CACHE: 0.058
SELECT BY INDEX_COL LIMIT WITHOUT_QUERY_CACHE: 0.063
SELECT BY COL WITH_QUERY_CACHE: 0.786
SELECT BY COL WITHOUT_QUERY_CACHE: 0.063
SELECT BY COL LIMIT WITH_QUERY_CACHE: 0.059
SELECT BY COL LIMIT WITHOUT_QUERY_CACHE: 0.062
UPDATE BY PK_COL WITH_QUERY_CACHE: 1.139
UPDATE BY PK_COL WITHOUT_QUERY_CACHE: 1.199
UPDATE BY PK_COL LIMIT WITH_QUERY_CACHE: 0.125
UPDATE BY PK_COL LIMIT WITHOUT_QUERY_CACHE: 0.142
UPDATE BY UNIQUE_COL WITH_QUERY_CACHE: 2.734
UPDATE BY UNIQUE_COL WITHOUT_QUERY_CACHE: 1.203
UPDATE BY UNIQUE_COL LIMIT WITH_QUERY_CACHE: 0.147
UPDATE BY UNIQUE_COL LIMIT WITHOUT_QUERY_CACHE: 0.163
UPDATE BY INDEX_COL WITH_QUERY_CACHE: 1.183
UPDATE BY INDEX_COL WITHOUT_QUERY_CACHE: 1.063
UPDATE BY INDEX_COL LIMIT WITH_QUERY_CACHE: 0.138
UPDATE BY INDEX_COL LIMIT WITHOUT_QUERY_CACHE: 0.154
UPDATE BY COL WITH_QUERY_CACHE: 4704.859
UPDATE BY COL WITHOUT_QUERY_CACHE: 4641.191
UPDATE BY COL LIMIT WITH_QUERY_CACHE: 0.156
UPDATE BY COL LIMIT WITHOUT_QUERY_CACHE: 0.167
根據測試結果,大概可以看到出以下幾個要點:
- 根據第 21 行與 22 行的差異看來,MySQL Query cache 還是有點用處,不過效用不大。
- 根據第 13 行與第 29 行的結果看來,有沒有設定 Primary Key、UNIQUE、INDEX 的影響不小。
- 根據 UPDATE 語法的測試結果看來,有沒有 LIMIT 頗重要。
Technorati Tags: FreeBSD, INDEX, LIMIT, MySQL, PDO, PHP, Primary Key, Query Cache, Super Smack, UNIQUE
(Visited 5152 times)
Dec 10
我們在開發 PHP 專案時,時常會把一些常用變數放在某個 PHP 檔案,接著用 require()、require_once()、incluce()、include_once() 等函式將之引入。
若系統目錄很複雜,就會在很多檔案裡面看到類似這樣的語法:
require_once('../../Config.php');
然而,當這種系統必須變動目錄結構與檔案所在目錄時,開發人員就得額外多花些時間來更改上述之程式碼。
其實,PHP 的設定檔中,有個很少被注意到的變數,就是 auto_prepend_file 。
假設目前有個 PHP 專案,專案根目錄之系統絕對路徑為 /var/www/html/Project,
共用設定檔為 /var/www/html/Project/Config.php 。
我們可以在 /var/www/html/Project 下建立 .htaccess 檔案,內容如下:
php_value auto_prepend_file "/var/www/html/Project/Config.php"
而 /var/www/html/Project 目錄下所有的檔案,以及所有子目錄中的檔案都會自行引入 /var/www/html/Project/Config.php 。
PS. 由於 auto_prepend_file 的設定是透過 require() 來實作,使用這種方法要特別注意以下兩點:
- 一旦設定了 auto_prepend_file ,該檔案就必須要存在,否則就會有 error ,導致該目錄下的所有 PHP 檔案無法正常執行。
- include_path 會影響 auto_prepend_file。
Technorati Tags: auto_prepend_file, htaccess, include, include_once, PHP, php_value, require, require_once
(Visited 6011 times)
Sep 27
以下是在 PTT 的 PHP 板看到某篇文章後的心得,以及小小的經驗分享。
對 Smarty 稍有經驗的人,應該都知道樣板內可使用 {include} 這個 tag 來嵌入其他 template file。
然而,因為 Smarty 內的變數都是全域變數,所以我對這個 tag 的看法是「能不用,就不用」。
用常見的網站論壇系統舉個簡單的例子:
若 B 設計師在其 template file 中使用了 {include} 來嵌入 A 設計師的 template file,就可能會產生預期之外的顯示結果。
當然,若是開發團隊已事先溝通好各項變數的命名,就不會有這種情況。
但為了減少此類風險,降低 debug 的難度,我們會選擇使用這種方式:
- 在系統全域共用的函式檔案中增加負責顯示 HTML header 的 function,例如 function page_header($title) { ...} ,並在 function 中 assign 變數,引入 A 設計師開發的 template file。
- 在論壇文章內容顯示的程式檔中,呼叫 page_header($title),再 assign 文章標題的變數,引入 B 設計師開發的 template file。
當然,若嵌入的 template file 內沒有任何變數,就不須考量以上的狀況,開發/設計人員可以大膽地隨意使用。
Technorati Tags: include, PHP, Smarty
(Visited 6639 times)
Sep 09
昨晚,有個學長透過 MSN 問我有無簡單好用的 PHP 圖形驗證碼,又讓我想到 reCAPTCHA。
上一次使用時,reCAPTCHA 僅提供顏色變更;如今,reCAPTCHA 已經開始支援多國語言了。
剛才稍微玩了一下,寫了這個簡單的網頁。
reCAPTCHA 的多國語言化相關資訊可以參考 這裡,而我使用的中文化程式碼片段為:
<script type="text/javascript">
var RecaptchaOptions = {
custom_translations : {
visual_challenge : "取得圖形驗證碼",
audio_challenge : "取得音效驗證碼",
refresh_btn : "重新整理圖形",
instructions_visual : "輸入兩個英文單字:",
instructions_audio : "輸入您聽到的聲音:",
help_btn : "獲得協助",
play_again : "重新播放音效",
cant_hear_this : "將音效下載為 MP3",
incorrect_try_again : "錯誤! 請再試一次"
}
};
</script>
Technorati Tags: CAPTCHA, il8n, localization, PHP, reCAPTCHA
(Visited 7436 times)
Nov 10
這幾天接了一個 PHP 的系統要作,為了防止 Spambot,必須加入視覺驗證碼。
在眾多視覺驗證碼的 solution 中,我第一個想到的就是 reCAPTCHA 。
不過,根據以前用過的經驗,reCAPTCHA 還沒有多國語言支援的能力。
我看了一下他們給的 PHP library 跟 使用說明頁 。
很遺憾... 目前還是沒有支援多國語言。
在 reCAPTCHA 的 Forum 則是找到 這個討論串 。
雖然會擔心收到抱怨,不過依照目前的情況看來,只能先硬著頭皮上,然後靜待佳音了。
回覆 那個討論串 的文章催促一下,進度會不會比較快呀?
Technorati Tags: PHP, reCAPTCHA, translation
(Visited 1830 times)
Recent Comments