2008年12月30日 星期二

順其自然

最近在進行程式執行環境的設定,由於前輩留下許多的程式遺產及系統都是在windows系統下執行的,因此自己寫的程式也必須在windows下執行,為了讓自己的程式能夠在windows環境下執行,經歷了許多的苦頭,許多奇奇怪怪的問題總是百思不得其解,不過,到後來,問題找到解答了,也明白一句話,順其自然。

選擇程式語言,經過了滿多的掙扎,走過了許多徘徊,難以抉擇的十字路口,最終還是選擇php,這理由很多。想過以basic,搭配.net或asp在windows上跑,這些程式語言的撰寫界面親切,而且中文資源眾多,比較好入門,不過新台弊要準備多一些,想到這就讓我打退堂鼓,我不想砸太多錢在這些軟體上,也不想上網抓非法軟體,因為太懶惰了,而且msdn的文件,就算是中文的也看不太懂,翻譯的不太順。php是開放原始碼的軟體,可以隨意散佈使用,不用揹負太多法律及金錢的壓力,比較適合我的個性。開源軟體當然不只php,也曾買了很多perl的書,不過到後來沒有看完。Perl也不錯,只是不像php那麼風行,比較少人用。就看你喜歡哪一種了。

現在我明白,其實學哪套程式語言是沒有什麼差別的,只要學通一種,再學另一種程式語言,其實是很快的,所謂的很快是指基本的語法(80%的功能),背後的精髓就另當別論,專精一二種,其實就夠了,其他的語言在你精通一二種程式語言後,就可以觸類旁通了。

原來程式搭配的資料庫系統是mssql,這套跟php搭配,有點奇怪,因為mssql是微軟的,並非開放原始碼軟體,不過沒辦法,原來的系統是建構在mssql上的,總不能不用吧!為了使用mssql,還k了一個禮拜的書,寫了一堆mssql上的預儲程序來和php搭配使用,php最常搭配的資料庫是mysql,一般常見的應用系統都是以php搭配mysql,與mssql搭配使用的程式語言通常是.net的語言。

原來的資料庫規畫跟我預期的有落差,沒有規畫primary key,foreign key,維護很不方便。不過,也沒辦法了,只好硬幹,以php搭配mssql及預儲程序,總是可以把程式寫出來的,不過在後續的執行環境設定上,讓我吃足苦頭,實際上線後,程式常當,為了找原因,讓我頭痛很久。

後來找到一個解決辦法,在iis上設定應用程式集區,一個小時回收記憶體一次,要當就不會超過一個小時了,但終究不是一個好的解決辦法,而且回收記憶體,雖然對php的session不會有影響(php的session存在硬碟上),但asp的session會全部清空,問題是這台機器上有一堆asp寫的程式。

繼續找資料,才知道php雖然是thread safe的程式,但是許多的dll檔卻不是thread safe的程式,所以一旦碰到非thread safe的dll,php就會不穩定,雖然知道原因,但卻不知道是哪個dll是非thread safe的,但是要解決這個問題就必須以fastcgi的方式執行,這樣非thread safe的dll在這樣的環境下執行也不會有問題,只是在fastcgi下,mssql的dll檔根本跑不起來,這真是糟糕,沒辦法連資料庫,程式就沒作用。

最後終於找到原因了,把php模組需求跟中心的同事說後,同事在linux上架完系統後,我的程式一樣不能連mssql,自己找了php manual後才發現原來win上的ntwdblib.dll檔是非thread safe的,不穩定的原凶就是它了,把php連接mssql的底層函式庫改成dblib就一切正常,找到原因後,心頭真是舒服,不過就要麻煩同事架linux系統了,這樣跑起來才會穩定,也不用再頭痛了。

許多的事情順其自然是比較不會遇到這麼多的曲折,像這次以php搭配mssql就不是一個自然的搭配,開放原始碼搭配商業軟體,許多的問題,開放原始碼社群及微軟這家公司是沒有多餘的心力解決的,到時候就只能自求多福了,以最自然的方式進行,問題還是會比較少,因為這些問題都有人幫你解決了,只要專心寫程式就可以了,不用跟這些環境設定奮戰。

2008年12月16日 星期二

php5.2.8不能載入php_mssql

php5.2.8有bug
無法載入php_mssql及php_pdo_mssql
搞了我好幾天,終於確定問題
換用php5.2.6就ok了

ms sql2000的php driver

http://blogs.msdn.com/sqlphp/archive/2007/10/09/welcome.aspx

上面的文章有說明,新版的mssql 2005 driver for php也支援 mssql 2000

這真是好消息,有微軟的正式支援,總是比較安心,不會心裡毛毛的。

2008年12月13日 星期六

win上apache的效能

http://www.ithome.com.tw/itadm/article.php?c=50012&s=2

2008年12月6日 星期六

連接db的問題

使用Zend_Db連接資料庫時,最好使用$db->closeConnection()關閉每次
使用後的資料庫連結,以免產生一堆有的沒的問題,
這是上線後的網站實際發生的狀況,使用$db->closeConnection()後就少了一些問題,
win上的記憶體管理做得似乎並不好。

Zend Framework出現空白頁

錯誤訊息顯示:
Fatal error: Cannot redeclare class Zend_Loader in .....lib\Zend\Loader.php on line 31

查了一下php manual
require及include的差別

require:只要在程式中都會執行檔案含入的動作,不論是不是在判斷式中

include:只有判斷式正確,才會執行檔案含入的動作


看了一下含入的路徑,由於是windows平台
路徑是: d:\a\b\c/lib

改了一下程式碼

/lib改成 DIRECTORY_SEPARATOR.'lib'

路徑就成了d:\a\b\c\lib


經過這些手續,暫時消除這個問題,不過還要再觀察一陣子。

2008年10月14日 星期二

沒有轉址軟體時,使用zend framework的解法

http://akrabat.com/2008/06/03/zend-framework-urls-without-mod_rewrite/

2008年10月10日 星期五

mssql的分頁

http://blog.blueshop.com.tw/blackncc/archive/2007/01/06/49123.aspx

2008年10月9日 星期四

iis上支援轉址的免費軟體

http://cheeso.members.winisp.net/IIRF.aspx

2008年10月8日 星期三

連接mssql

http://www.zxbc.cn/html/20070506/13033.html

Zend_Framework 1.6的版本的protected $_pdoType已改為mssql

http://mlblog.osdir.com/php.zend.framework.general/2006-12/index.shtml

PDO, MSSQL and FreeTDS

Date: December 12, 2006
From: Todd Wolaver

Hi,


I'm using FreeTDS under Linux to access MSSQL databases, which won' t work with the framework since the PDO driver is dblib not mssql.

$db = Zend_Db::factory('pdo_mssql', $params);

will return:
"The mssql driver is not currently installed"

This is set in the Mssql.php PDO adapter (Zend_Db_Adapter_Pdo_Mssql):
43: protected $_pdoType = 'mssql';


If this is changed to dblib then it works. What would be the best way of implementing the dblib driver without changing the core code of the framework?

Thanks
Todd Wolaver


Zend_Framework 1.6的版本的protected $_pdoType已改為mssql

2008年9月26日 星期五

jQuery的身份證驗證plugin

/**
* 驗證身份證字號是否正確的jquery plugin
* 用法:jQuery('身份證字號的id').idCheck();
* 傳回值:
* 合法---傳回true
* 非法---傳回false
* 作者:羅仁治
* 版本:1.0
* 版權:隨你使用,但須保留這段說明文字。
*/
jQuery.fn.idCheck = function(){
var real_id = this.val();
real_id = real_id.toUpperCase();


var pattern = /^[A-Z]{1}[0-9]{9}$/;
if (!pattern.test(real_id)) {
return false;
}

if (check(real_id)) {
return true;
}
else {
return false;
}


function check(real_id){
var first = real_id.substr(0, 1);
switch (first) {
case 'A':
first = '10';
break;
case 'B':
first = '11';
break;
case 'C':
first = '12';
break;
case 'D':
first = '13';
break;
case 'E':
first = '14';
break;
case 'F':
first = '15';
break;
case 'G':
first = '16';
break;
case 'H':
first = '17';
break;
case 'I':
first = '34';
break;
case 'J':
first = '18';
break;
case 'K':
first = '19';
break;
case 'L':
first = '20';
break;
case 'M':
first = '21';
break;
case 'N':
first = '22';
break;
case 'O':
first = '35';
break;
case 'P':
first = '23';
break;
case 'Q':
first = '24';
break;
case 'R':
first = '25';
break;
case 'S':
first = '26';
break;
case 'T':
first = '27';
break;
case 'U':
first = '28';
break;
case 'V':
first = '29';
break;
case 'W':
first = '32';
break;
case 'X':
first = '30';
break;
case 'Y':
first = '31';
break;
case 'Z':
first = '33';
break;
}

var value = first.concat(real_id.substr(1, 8));

var sum = parseInt(value.substr(0, 1));

for (var i = 1; i <= 9; i++) {
sum = sum + parseInt(value.substr(i, 1)) * (10 - i);
}

single = 10 - (sum % 10);

if (parseInt(real_id.substr(9, 1)) == single) {
return true;
}
else {
return false;
}
}
};

2008年9月25日 星期四

身份證字號的validator

下面程式是參考
http://zh.wikipedia.org/wiki/%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B%E5%9C%8B%E6%B0%91%E8%BA%AB%E5%88%86%E8%AD%89


這篇文章的身份證字號檢查公式,轉換成Zend framework的validator,進行身份證號碼的驗證。

有了公式之後要進行驗證就比較容易,不用看程式碼去倒推公式,如果沒有公式,光看程式碼,看個老半天,也看不出個所以然來,還要去適應各種程式碼的寫法,太累了,這個公式真是造福大家,直接將公式轉換成Zend Framework的程式碼,就容易多了。

require_once('Zend/Validate/Abstract.php');

class App_Validate_IdCheck extends Zend_Validate_Abstract{
const SIZE='size';
const FORMAT='format';
const CHECK='check';

protected $_messageTemplates=array(
self::SIZE=>"'%value%' 身份證字串長度不符合。",
self::FORMAT=>"'%value%' 的第一個字元必須是英文字母,其他必須是數字。",
self::CHECK=>"'%value%' 是非法的身份證字號。"
);

public function isValid($value){
$this->_setValue($value);
if(strlen($value) !=10){
$this->_error(self::SIZE);
return false;
}

$value=strtoupper($value);
if(!ereg("[A-Z]{1}[0-9]{9}",$value)){
$this->_error(self::FORMAT);
return false;
}

if($this->check($value)){
return true;
}
else{
$this->_error(self::CHECK);
return false;
}
}

private function check($value){
$first=array('A'=>10,'B'=>11,'C'=>12,'D'=>13,'E'=>14,
'F'=>15,'G'=>16,'H'=>17,'I'=>34,'J'=>18,
'K'=>19,'L'=>20,'M'=>21,'N'=>22,'O'=>35,
'P'=>23,'Q'=>24,'R'=>25,'S'=>26,'T'=>27,
'U'=>28,'V'=>29,'W'=>32,'X'=>30,'Y'=>31,
'Z'=>33);
/**
* convert the first alpha character to two digits
* and combines to the rest digits
*/
$value=$first[$value[0]].substr($value,1);

$sum=(int)$value[0];
for($i=1;$i<=9;$i++){
$sum+=$value[$i]*(10-$i);
}

/**
* get the single-digit of 10-$sum
*/
$single=10 - ($sum % 10);

if($single == $value[10]){
return true;
}
else{
return false;
}
}
}

判斷身份證字號的處理邏輯

http://blog.nahoya.com/archives/2007_12/177

http://zh.wikipedia.org/wiki/%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B%E5%9C%8B%E6%B0%91%E8%BA%AB%E5%88%86%E8%AD%89

處理邏輯
1.判斷身份證字號的字串長度,長度必須等於10個字元。
2.判斷身份證字號的字串格式,第一個字元必須是英文字母,第二個字元必須是數字1或數字2,其他的字元必須是數字。
英文
縣市 / 地區
數字
A
台北市
10
B
台中市
11
C
基隆市
12
D
台南市
13
E
高雄市
14
F
台北縣
15
G
宜蘭縣
16
H
桃園縣
17
I
嘉義市
34
J
新竹縣
18
K
苗栗縣
19
L
台中縣
20
M
南投縣
21
英文
縣市 / 地區
數字
N
彰化縣
22
O
新竹市
35
P
雲林縣
23
Q
嘉義縣
24
R
台南縣
25
S
高雄縣
26
T
屏東縣
27
U
花蓮縣
28
V
台東縣
29
W
金門縣
32
X
澎湖縣
30
Y
陽明山管理局
31
Z
連江縣
33
3.每個縣市都有一個相對應的英文字母代表,每個英文字母也都有一個對應的二位數
4.將英文字母轉換為二個數字字元與原來的數字字串進行合併,轉換為十一個數字字元。
5.轉換後的數字字串總共有11個數字字元,將前十個數字進行如下的運算
第一個數字+
第二個數字*9+
第三個數字*8+
第四個數字*7+
第五個數字*6+
第六個數字*5+
第七個數字*4+
第八個數字*3+
第九個數字*2+
第十個數字*1
6.第5個步驟運算得到的和,只取個位數部份
7.用十減去第6步驟得到的值,與第4步驟得到的值的第十一個數字字元進行比對。
如果相同,表示身份證字號是正確的
如果不相同,表示身份證字號是不正確的

2008年9月14日 星期日

字型位置

ubuntu上字型放置的位置

/usr/share/fonts/truetype/

要用到gd函式庫配合php輸出時就要用到字型了

2008年9月12日 星期五

防堵apache的dos攻擊

http://www.novell.com/coolsolutions/feature/19958.html

第一步
安裝apache的evasive模組
apt-get install libapache2-mod-evasive

第二步
(1)/etc/apache2/mods-enabled

(2)/etc/apache2/mods-available

在(1)這個目錄下建mods-esave.conf檔,內容如下,
再將這個檔soft link到(2)這個目錄


DOSHashTableSize 3097
DOSPageCount 2
DOSSiteCount 50
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 10


第三步
重新啟動apache

第四步
連接測試網站,以非常快的速度快按重新整理,如果出現403 forbidden表示安裝成功。

2008年9月3日 星期三

樹狀目錄plugin

http://abeautifulsite.net/notebook.php?article=58

上面這個網址的jquery的plugin可以顯示目錄下的檔案結構,還附上php程式碼,可以直接拿來用在網站上,如果要改寫,應該不難,只要php檔輸出的格式跟css檔難搭到就可以了。

css:負責外貌
php:負責內容輸出
jquery plugin:負責取回php輸出的資料並將資料顯示或隱藏

2008年9月2日 星期二

下載檔案的問題

Zend Framework的response物件會暫存許多網頁內容,要將存入資料庫中的檔案下載前,必須清空response物件,不然會出現錯誤訊息,這個錯誤找了好久,才找到。

多加一道命令,清空respone物件

$this->getResponse()->clearBody();

ie跟firefox處理下載的中文檔名方式也不同,經由ie下載的檔案要先以urlencode()函數處理過,才能下載,經由firefox下載的檔案就不能以urlencode()函數處理過,不然下載的檔名會是編碼過的字串。

處理的邏輯,不難,只要判斷使用者的瀏覽器類型,再分別處理就可以了。

2008年9月1日 星期一

分頁的action helper

很久之前就想要寫一個分頁的物件,本來的構想是在server端把資料的總頁數抓出來後,再送到client,使用jQuery的pagination plugin進行處理,不過如果完全由jQuery的plugin進行處理,會有安全性的問題,一定要由server端再進行確認的處理,同樣的事情,server端及client端,兩邊都要做,真的是有點囉嗦,後來就決定完全由server端進行分頁的處理。

server端的分頁物件的撰寫邏輯是這樣的:

如果頁數少於或等於十筆,就顯示全部頁數。

如果頁數大於十筆,就分成三種狀況做處理

目前頁數小於第五頁,就顯示1~5頁及最後兩頁,其他的用三個點號顯示

總頁數減目前頁數的絕對值小於或等於五頁,就顯示前二頁及最後五頁,其他的用三個點號顯示
非以上兩種狀況,就顯示前二頁及最後兩頁及目前頁數與目前頁數的前二頁與後二頁,其他的用三個點號顯示

分頁的外觀設定,就由client端的css做設定,server端的程式就專注在頁數的計算,將程式邏輯及外觀設定做清楚的切割。

分頁物件的css設定

在樣板中將server傳來的page資料用div包起來,設定顯示的css碼即可

假設div的設定是
server傳來的page資料


非目前頁數的css設定

.page ul li a{
css設定資料
}

目前頁數的css設定

.page ul li span{
css設定資料
}

整個物件花了不少時間思考,該怎麼做,想通後,心裡滿高興的,雖然網路上有非常多的分頁物件可以參考,不過要一行行拆解,真是花時間,而且看得頭昏眼花,眼睛不舒服,就不太想再看這些程式碼了,最後還是決定自己寫一個,比較能夠掌控,而且整個物件的用法也有一致性,不會感覺怎麼有個物件的用法跟整個程式的搭配度不太吻合。

2008年8月31日 星期日

php無法執行

最近這幾天,在apache上都沒辦法跑php,我本以為是改了哪邊的設定檔,才造成只要一執行php檔,就造成php檔被下載,而不是被執行的狀況。

設定檔找了老半天,都找不到錯誤,不過把apache的php_gtk2的功能拿掉,立刻就正常了,真是見鬼了,前一陣子在玩php-gtk,只是弄個測試程式玩玩,沒想到會造成php在apache的環境下無法執行,不過也多了一次經驗。也不錯啦。

強化上傳目錄的安全性

如果在網頁程式中實做上傳檔案的功能,為了強化程式的安全性,會針對檔案的附檔名做過濾,並且限制上傳的目錄位置。這樣做的目的,只有一個,確保使用者不會亂來,在server上動手腳。

除了在程式中,使用程式碼確保server的安全外,還可以在apache中設定,再加上一道防護措施,
就是在上傳目錄中,新增.htaccess檔,這個檔案中再加上下面這道命令

php_flag engine off

那麼這個目錄下的php檔案都不會被apache解譯及執行。

要這麼做,你的apache的override必須設成All。

2008年8月10日 星期日

fibonacci數列的遞迴解法

fibonacci數列
1,1,2,3,5,8,13,21,55,.......

第一個及第二個數字是1
第三個以後的數字是前面兩個數字相加後的和

求fibonacci第n個數字的解法
使用遞迴的解法


function fibonacci($n){
if($n==1 ||$n==2){
return 1;
}else{
return fibonacci($n-1)+fibonacci($n-2);
}
}


echo fibonacci(10);

在我的機器上,如果代入fibonacci(40)
機器就會超過允許的執行時間,一個小小的程式,竟然耗用那麼多的時間,實在是可怕。

下面這行程式是一個無窮迴圈,會造成一樣的結果。
while(1){}

2008年8月7日 星期四

merge

merge的解法
1.將要merge的兩個陣列進行排序
2.將陣列a及陣列b的每個元素進行比較
3.比較後將元素放入另一個陣列c
4.如果陣列a或陣列b已經沒有元素可以進行比較,將還沒比較的元素加到陣列c的後面


public function mergeAction(){
$this->_helper->viewRenderer->setNoRender();

/**
* 製做陣列a
*/
$length_a=rand(8,10);

for($i=0;$i<$length_a;$i++)
$a[]=rand(100,200);

/**
* 將陣列a進行排序
*/
$a=$this->sort($a);


/**
* 製做陣列b
*/
$length_b=rand(11,13);

for($i=0;$i<$length_b;$i++)
$b[]=rand(100,200);

/**
* 將陣列b進行排序
*/
$b=$this->sort($b);

/**
* 將陣列a及陣列b,merge為單一陣列
*/
$com_a=0;
$com_b=0;
while($com_a<$length_a && $com_b<$length_b){
if($a[$com_a]<$b[$com_b]){
$c[]=$a[$com_a];
$com_a++;
}else{
$c[]=$b[$com_b];
$com_b++;
}
}

while($com_a<$length_a){
$c[]=$a[$com_a];
$com_a++;
}

while($com_b<$length_b){
$c[]=$b[$com_b];
$com_b++;
}


/**
* a陣列
*/
foreach($a as $num)
echo $num."
\n";

echo "


";

/**
* b陣列
*/
foreach($b as $num)
echo $num."
\n";

echo "


";

/**
* merge陣列a及陣列b後的c陣列
*/
foreach($c as $num)
echo $num."
\n";

}

public function sort(array $a){
$left=0;
$right=sizeof($a)-1;

while($left<$right){
$shift=0;
for($i=$left;$i<$right;$i++){
if($a[$i]>$a[$i+1]){
$tmp=$a[$i];
$a[$i]=$a[$i+1];
$a[$i+1]=$tmp;
$shift=$i;
}
}
$right=$shift;

for($i=$right;$i>$left;$i--){
if($a[$i]<$a[$i-1]){
$tmp=$a[$i];
$a[$i]=$a[$i-1];
$a[$i-1]=$tmp;
$shift=$i;
}
}
$left=$shift;
}
return $a;
}

css網頁排版


http://www.w3schools.com/css/tryit.asp?filename=trycss_float6

用css排版,排版的程式碼感覺比較清爽,沒有一堆的表格標籤,只是用dreamweaver進行css排版,不知道為什麼,版面總是亂七八糟的,不知道是不是因為使用的dreamweaver的版本老舊的關係,只好用eclipse,慢慢敲css程式碼到編輯器裡頭,還好eclipse的aptana plugin有支援css碼的自動完成功能,敲完第一個字母,就有相關的屬性顯示,不然光是查相關的屬性資料,就會覺得很煩。

w3schools的網站有一拖拉庫的範例,如果把每個範例都key個幾遍,相信一定功力大增,不過這種從下而上的學習法,如果套用到我身上,我可能會水土不服,我比較能夠適應從上而下的學習方式,也就是有整體概念了,再從細部追蹤每一個細節,當然,整體概念跟實做細節是環環相叩的,缺一不可。只是當我有全部的整體概念後,進行這些細部的實作,會感覺比較踏實,不會有不知道這些程式碼到底要幹嘛的感覺。

這個網站只有在我搞清楚整個css運作的來龍去脈以後,才能夠真正發揮他的功能,在真正清楚背後運作概念後,key in這些程式碼,說實在的,剛開始很興奮,到現在卻感覺有點枯燥。必須追蹤每一行程式碼的作用,感覺有點辛苦,應該要找一些更方便的工具,不要手工打這些碼,實在是很累。但這些基礎功,是必須的,不然,根基不穩是不能夠蓋房子的,就算蓋出來,也很容易塌的



div.container{
margin:0px;
border:1px solid gray;
width:100%;
}

div.header,div.footer{
color:white;
background-color:gray;
clear:both;
margin:0px 0px 0px 0px;
}

div.left{
float:left;
width:160px;
margin:0;
padding:10px;
}

div.content{
position:relative;
margin-left:190px;
margin-top:0px;
border-left:1px solid gray;
padding:10px;
}

h1.head{
font-size:60px;
padding:0;
margin:0;
}

2008年8月6日 星期三

二元搜尋法

二元搜尋法的觀念
1.資料已排序好
2.取排序好的中間位置的值,與想要找的值做比對
3.如果比對不成功,搜尋範圍就剩原來的一半,
4.一直重覆步驟2,3



/**
* 以shaker法進行排序,以便進行二元搜尋。
*/
$length=rand(8,10);

for($i=0;$i<$length;$i++)
$a[]=rand(100,200);

$left=0;
$right=$length-1;
while($left<$right){
$shift=0;
for($i=$left;$i<$right;$i++){
if($a[$i]>$a[$i+1]){
$tmp=$a[$i];
$a[$i]=$a[$i+1];
$a[$i+1]=$tmp;
$shift=$i;
}
}
$right=$shift;

for($i=$right;$i>$left;$i--){
if($a[$i]<$a[$i-1]){
$tmp=$a[$i];
$a[$i]=$a[$i-1];
$a[$i-1]=$tmp;
$shift=$i;
}
}
$left=$i;
}

foreach($a as $num)
echo $num."
\n";


/**
* 設定要找尋的值
*/
$search='192';


/**
* 二元搜尋法
*/
$low=0;
$high=$length-1;
$flag=0;

while($low<=$high){
$mid=(int)(($low+$high)/2);
if($search>$a[$mid]){
$low=$mid+1;
}elseif($search<$a[$mid]){
$high=$mid-1;
}else{
$flag=1;
break;
}
}

if($flag)
echo '所要找的值的陣列表示法是:'."\$a[$mid]"."
\n";
else
echo '找不到!';

2008年8月5日 星期二

shell排序法

gap為1,2,4,8,16,32...


$length=rand(8,20);

for($i=0;$i<$length;$i++)
$a[]=rand(100,200);

for($gap=(int)($length/2);$gap>0;$gap=(int)($gap/2)){
for($i=$gap;$i<$length;$i++){
for($j=$i;$j>=$gap;$j-=$gap){
if($a[$j-$gap]>$a[$j]){
$tmp=$a[$j-$gap];
$a[$j-$gap]=$a[$j];
$a[$j]=$tmp;
}else{
break;
}
}
}
}

foreach($a as $num)
echo $num."\n";



gap為1,4,13,40,121...


$length=rand(10,20);

for($i=0;$i<$length;$i++)
$a[]=rand(100,200);

for($gap=1;$gap<(int)($length/3);$gap=($gap*3+1))
;

while($gap>0){
for($i=$gap;$i<$length;$i++){
for($j=$i;$j>=$gap;$j-=$gap){
if($a[$j]<$a[$j-$gap]){
$tmp=$a[$j];
$a[$j]=$a[$j-$gap];
$a[$j-$gap]=$tmp;
}else{
break;
}
}
}
$gap=($gap-1)/3;
}

foreach($a as $num)
echo $num."\n";

2008年8月4日 星期一

jQuery編輯器

http://www.aptana.com/node/187
http://www.aptana.com/docs/index.php/Plugging_Aptana_into_an_existing_Eclipse_configuration

安裝aptana的eclipse plugin

安裝這個plugin後,編輯css,javascript,html比較方便,編輯器會以不同顏色顯示函數、變數等不同屬性的語句,如果不小心打錯字,或是記錯物件名稱,或是語法有錯誤,就會出現紅色的警告錯誤訊息,比較容易找到哪個地方出錯,可以方便地進行除錯。

左邊視窗有專案裡頭的檔案列表,可以方便地檢視專案裡頭的檔案及目錄列表,可以清楚掌握目前有哪些檔案在專案裡頭,如果要增刪檔案或是修改檔名,也是在同一個作業環境下進行,不用再切換到其他的檔案管理軟體進行作業,比較方便。

aptana的plugin有支援jQuery等許多的javascript的framework,尤其是支援jQuery,真的是太好了,因為jQuery最簡單了,其他的javascript的framework,只要光看語法,就讓我頭暈,不像jQuery的語法那麼直覺,上次研習,介紹了YUI這套framework,語法比起jQuery,簡直就是文言文跟白話文的對比,YUI的語法實在繁複,後來上網找另一套framework—dojo,也是覺得沒有jQuery那麼親切,後來還是決定用jQuery,整個jQuery的語法簡潔明瞭,就像網站上的slogan說的--write less, do more,而且現在jQuery發展一套UI,可以很方便的套用,試用了一下,真的是方便啊,最適合我這種懶人了。

安裝流程
文件開頭的第二個網址就列出了安裝步驟,而且是圖文並茂的詳細步驟。

開啟eclipse

點選help->software updates->Find and Install->search for new features to install->next

填入下載網址:http://update.aptana.com/install/studio/3.2/ 及name

再來看看畫面,一直按下一步就裝好了。

2008年7月31日 星期四

shaker排序法--單邊

只有單邊的shaker排序法,應該比雙邊的shaker排序法慢

$length=rand(8,10);

for($i=0;$i<$length;$i++)
$a[]=rand(200,300);

$left=0;
$right=$length-1;

while($left<$right){
$shift=0;
for($i=$left;$i<$right;$i++){
if($a[$i]>$a[$i+1]){
$tmp=$a[$i];
$a[$i]=$a[$i+1];
$a[$i+1]=$tmp;
$shift=$i;
}
}
$right=$shift;
}


foreach($a as $num)
echo $num."\n";
}

2008年7月30日 星期三

shaker排序法

$length=rand(8,10);
for($i=0;$i<$length;$i++)
$a[]=rand(100,200);

$left=0;
$right=$length-1;
while($left<$right){
for($i=$left;$i<$right;$i++){
if($a[$i]>$a[$i+1]){
$tmp=$a[$i];
$a[$i]=$a[$i+1];
$a[$i+1]=$tmp;
$shift=$i;
}
}
$right=$shift;

for($i=$right;$i>$left;$i--){
if($a[$i]<$a[$i-1]){
$tmp=$a[$i];
$a[$i]=$a[$i-1];
$a[$i-1]=$tmp;
$shift=$i;
}
}
$left=$shift;
}

foreach($a as $num)
echo $num."
\n";

2008年7月29日 星期二

氣泡排序法

執行時間
O(n^2)

error_reporting(E_ALL|E_STRICT);
ini_set('display_errors','on');

$length=rand(8,10);
for($i=0;$i<$length;$i++)
$a[]=rand(100,200);


for($i=0;$i for($j=count($a)-1;$j>$i;$j--){
if($a[$j]<$a[$j-1]){
$tmp=$a[$j];
$a[$j]=$a[$j-1];
$a[$j-1]=$tmp;
}
}
}


foreach($a as $value)
echo $value."\n";

2008年7月28日 星期一

直接插入法

執行時間
O(n^2)


$length=rand(8,15);
for($i=0;$i<$length;$i++)
$a[]=rand(100,200);

for($i=1;$i for($j=$i;$j>0;$j--){
if($a[$j]<$a[$j-1]){
$tmp=$a[$j];
$a[$j]=$a[$j-1];
$a[$j-1]=$tmp;
}
}
}

foreach($a as $value)
echo $value."\n";

直接選擇法

執行時間
O(n^2)



$a=array(8,49,9,29,30,46,20,31);

for($i=0;$i<(count($a)-1);$i++){ $min=$a[$i]; $min_pos=$i; for($j=$i+1;$j$a[$j]){
$min=$a[$j];
$min_pos=$j;
}
}

$tmp=$a[$i];
$a[$i]=$a[$min_pos];
$a[$min_pos]=$tmp;
}

foreach($a as $value){
echo $value."\n";
}

ubuntu的啟動管理

安裝啟動管理程式

apt-get intsll sysvconfig


啟動管理程式的使用

圖形介面的使用

sysvconfig

文字介面的使用

sysvconfig --enable apache2

sysvconfig--disable apache2

sysvconfig --listlinks |grep apache2

2008年7月23日 星期三

memcached的使用

http://dev.mysql.com/doc/refman/5.0/en/index.html
http://tw2.php.net/manual/en/book.memcache.php

Memcached

introduction
在一般的IT環境下,如果要擴增程式的使用人數,最大的問題會卡在程式的執行速度,經常存取的資訊,如果使用mysql做為後端存取的資料庫,速度就會變得慢,因為資訊的存取,必須透過執行sql語法,取得資料庫內相關的資料,而拖慢執行速度。

memcached是一個簡單的、具有高度伸縮性的key-based快取,快取來源不受限制,他也是一個備份的RAM,可以讓應用程式進行非常快速地存取,要使用memcached,必須在一台或多台主機上執行memcached,讓使用者共享快取內儲存的物件,因為每台主機都是使用RAM儲存資訊,存取的速度遠遠快過從硬碟上進行資訊的存取,這樣的話,相對於從資料庫存取資料,效能的增進是非常明顯的。快取只是容納資訊的空間,你可以在快取內儲存任何資料,如果儲存的是複雜的資料結構,可以在資料存進快取前,先進行複雜的資料庫操作,再將資料放進快取中,可以大幅減少mysql server的負擔。

使用memcached的一般性作法是修改應用程式,讓應用程式改為從memcached讀取資料,如果需要的資訊不在memcached裡頭,程式就改由mysql讀取資料,再將資料寫入memcached裡頭,以後如果要存取相同資料,就可以發揮memcached資料快速存取的優勢。

在memcached的架構中,所有的client可以送出key與任何一台memcached伺服器連繫,在這個架構中,每個client可以與圖中的任何伺服器連繫,在client上,當送出要求時,key會先被hash,這個hash值會被用來選取memcached伺服器,memcached伺服器由client決定,可以使程式維持輕量化。

當client送出key時,會執行同一套演算法,相同的key,會產生相同的hash值,同一部memcached伺服器就會被選為資料來源,使用這個方法,快取資料被分散放置在所有的memcached伺服器上,而這些快取資訊可以被所有的client存取,結果就是,以記憶體做為資料儲存處的分散式快取,這類型的快取尤其適合複雜的資料結構,存取速度遠遠快過直接從資料庫存取資料。

在memcached伺服器中的資料永遠不會放在磁碟上,記憶體中的快取可以從後端的資料庫(mysql)取得資料,如果memcached伺服器當掉了,可以改為由mysql取得資料,當然,速度會變得比較慢。


Installing memcached

在ubuntu上安裝memcached非常輕鬆,只要輸入下面這道指令,再按enter就安裝好了

apt-get install memcached

memcached的啟動

在ubuntu上啟動memcached伺服器

/etc/init.d/memcached start

參數配置

參考/etc/memcached.conf設定檔的說明會更清楚

-u 執行身份
-m 記憶體容量(快取容量,單位為MB)
-p 指定port
-d 以daemon方式執行
-t 指定執行緒的數量
-l 指定執行的網路介面


memcached deployment

memcached有許多不同的佈署方式及策略,正確的佈署方式取決於你的應用程式及環境,當在系統中佈署memcached,你必須考量下列的注意事項

*memcached只是一項快取機制,如果資料很重要,在存取的過程中不能因為漏失,再從不同的地方存取,就不能使用memcached。

*memcached沒有內建的安全機制,至少,你必須確定,memcached伺服器只能在內部網路存取,而且memcached伺服器使用的port不能被外部網路存取。如果memcached含有敏感性資料,這些資料在存進memcached前要先被加密。

*memcached不提供復原機制,因為memcached之間沒有任何的溝通協定,如果一台memcached故障了,你必須將這台memcached從列表中移除,再重新載入資料,再將資料寫入另一台memcached伺服器中,

*如果client及memcached分別在不同的機器上,資料傳送延遲性如果造成問題,可以把memcached服務移到client端的機器上。

*key的長度是由memcached伺服器決定,預設的最大長度是250byte

*只使用一台memcached絕不是一個好的想法,尤其是服務多個client時,最少提供二台
memcached,才可以恰當地管理機器當機的狀況,如果可以的話,應該多建置幾台memcached
伺服器。當增加或移除memcached伺服器時,key/value的配置及hash值就會受到影響,如果要避免這些問題,必須研究memcached的hash type。


Memory allocation within memcached

當你第一次啟動memcached伺服器,memcached參數指定的記憶體數量不會自動配置給memcached,相反地,當開始將資料存進memcached的快取時,才會進行記憶體的分配。

當你開始將資料存在快取中,memcached不會為每個存入快取的資料一一配置記憶體空間,而是一次配置一個區塊,以進行記憶體空間的有效利用,避免快取資料過期時,造成記憶體空間配置的零碎化。

memcached裡頭的快取記憶體配置,以page為單位,每一page預設是1M,一個或多個page組成一個slab。

當memcached啟動時,不會進行slab及page的配置,只有當資料存進快取時,才會在page中切出適當大小的chunk,把資料存進chunk中,每一個chunk,會儲存一筆資料的value跟key,而page中每筆chunk的大小都必須一樣。

例如,memcached啟動時,有一筆資料的大小是800k,須要存入快取,這時就會建立一個slab,這個slab中page的chunk大小就是1M,一個page就只有一個chunk。

如果存入快取的資料是250K,那麼,就會建立一個slab,這個slab會再建立一個page,這個page會再切成4個大小相同的chunk。如果還有6筆250K的資料要存入快取,這個slab會再建立第二個相同的page進行資料的儲存。

資料如果超過1M,由於chunk預設是不能超過一M,而且資料必須塞進chunk中,因此超過一M的資料就會無法存入快取當中。


Using namespaces

memcached是非常簡單的key/value型式的儲存系統,無法自動將資料進行分類,例如,如果你使用mysql資料表傳回的id當做存入memcached快取的key時,就有可能有二筆由mysql資料表傳回的資料,雖然內容不同,id卻相同,存入快取時這二筆資料就會相互覆蓋,造成資料讀取的錯誤。

一些程式的API會在資料存入快取時,自動建立命名空間,實務上,這些命名空間,只是在存取資料時,在key前加上區別的字串。

你可以自己實做命名空間,只要存取快取資料時,在key前加上自定的字串就可以了,如加上”user_”字串。


Data Expiry

memcached快取資料的過期有二種狀況,如果要插入新的資料到快取中,但是沒有適當地slab可以存資料時,最近最少使用的資料會被移除,以騰出空間讓新的資料存入。

這個方法可以確保被移除的資料,是不再被使用的,或是已經很久不被使用的,但如果memcached的快取容量,比正常狀況使用時小很多時,你就會看到很多尚在使用的資料被移除了。

啟動memcached時啟用 -M參數,會在記憶體不足時發出警告,而不是自動刪除舊有的資料。

第二種快取過期的狀況,是直接刪除快取資料,或是設定資料過期的時間。

一種典型的應用就是設定使用者儲存的session資料,快取過期的時間。


在php中使用memcached

api的安裝

apt-get install php5-memcache


程式範例

程式名稱:a.php
class Test{
public $model;
public $color;
}

$test=new Test();
$test->model='bmw';
$test->color='red';

$cache=new Memcache();
$cache->connect('localhost',11211);
$cache->set('car',$test);


程式名稱:b.php
class Test{
public $model;
public $color;
}

connect('localhost',11211);
$car=$cache->get('car');

echo $car->model;
echo “\n”;
echo $car->color;
echo “\n”;

跑完a.php程式後,再跑b.php,就會跑出下面的結果
bmw
red

程式中物件的二個屬性都是經由serialize後,由a.php存在快取中,如果其他的程式要取用這二個屬性,只要有快取的key值,就可以得到這個物件的屬性。

2008年7月7日 星期一

輸出mysql的schema

這是在mysql workbench的說明文件上看到的一個方法,如果要輸出資料庫裡頭已建好的資料庫架構,可以下這道指令,把整個資料庫的schema都輸出成sql指令檔,再用mysql workbench的輸入功能,將sql指令檔輸入到mysql workbench,就可以看到完整的資料庫架構圖。

指令用法:
mysqldump 資料庫名稱 --no-data>輸出檔名

2008年7月3日 星期四

忘記mysql的root密碼

http://blog.tmu.edu.tw/tedyeng/000096.html

一、停用mysql

二、重新啟動mysql
mysqld -u root --skip-grant-tables &

三、修改root密碼
use mysql;
UPDATE user SET password=password('new password') where user='root';
FLUSH PRIVILEGES;

2008年6月30日 星期一

gcin的大易輸入法

gcin的大易輸人法無法使用鍵盤輸入標點符號,要解決這個問題,有兩個方法可以解決:

1.在大易輸入法下,按住ctrl-alt-逗號,就會顯示符號表,用滑鼠點選符號就可以輸入標點符號,不過使用滑鼠輸入比不上用鍵盤快速,不太方便。

2.在EN圖示上按右鍵->設定->倉擷/行列/大易/嘸蝦米設定->編輯內定輸入法的使用者外加字詞

加入下面兩行

,, ,
.. 。

以後輸入逗號時,按兩次逗號就會出現逗號。
要輸入句號,按兩次點號就可以了。

第一欄是輸入的鍵盤符號
第二欄是輸入後的文字
第一欄及第二欄間以空白分隔

其他的標點符號如果要使用鍵盤輸入,也是用同樣的方式設定。

stored procedure


Stored procedure
http://dev.mysql.com/doc/refman/5.0/en/stored-procedures.html

什麼是stored procedure
stored procedure是由一群sql敘述及流程控制的語法組成,本質上就是mysql中的程式語言,只是stored procedure的程式語法,跟php的不一樣,能使用的環境只限於mysql資料庫中,專為處理資料庫的操作,為了處理資料庫中的資料,有一些語法與php這種泛用型的程式語言,語法方面就稍微有些不同。就像戰機有雖然都有戰鬥功能,但為了不同的目的,有不同設計,以發揮團隊作戰的整體力量,有的戰機是專用對付入侵的戰機,是屬於空優型戰機,有的戰機,武裝不強,但體型龐大,有匿蹤功能,可以攜帶大量彈藥轟炸敵軍,屬於轟炸機。
mysql的stored procedure的語法遵循SQL:2003的語法標準,IBM的DB2也一樣遵循這套標準,遵循相同的標準有相當明顯的好處,如果要轉換資料庫,stored procedure不同重新撰寫,除錯,可以輕易的進行轉換,不會因為換了資料庫,必須重頭學習另一種語法,所有的基礎重頭來過。
使用stored procedure可以減少php程式碼的數量並增加程式的效能,php程式中只要呼叫stored procedure,stored procedure便會執行stored procedure中所有的sql敘述,並將結果傳回php程式中,php不用再一一呼叫sql statement,執行各自獨立的sql statement,再一一判斷結果,進行處理,這些sql statement及判斷統統都包裝在stored procedure裡頭,php及mysql之間傳遞的資料數量大幅減少,由於php與mysql之間等待資料傳送的時間及次數減少,可以加快程式的速度,也由於只要呼叫stored procedure,而不用呼叫各自獨立的sql statement,也減少了程式碼的數量。
目前,stored procedure不支援遞迴呼叫,如果要使用遞迴呼叫,必須將mysql變數:max_sp_recursion_depth 設定為非零的數字,預設是零,不允許遞迴,設為1,允許遞迴一次,設為2,允許遞迴二次,以此類推。

stored procedure的管理
stored procedure都存在mysql資料庫中的proc資料表中(mysql.proc),這個資料表儲存所有的stroed procedure,這些資料透過管理工具,可以運作正常,但如果手動修改,就有可能發生不可預期的後果,絕對不要手動修改這些資料,而是要透過create procedure,alter proceudre或是drop procedure來進行管理。


要建立stored procedure就必須有create routine的權限

要修改stored procedure就必須有alter routine的權限

要執行stored procedure就必須有execute的權限

有create routine的權限就自動會有alter routine及execute的權限

建立stored procedure的語法
create [definer={usercurrent_user}] procedure sp_name([sp_parameter],[sp_parameter]...)
[characteristic]
stored procedure body
[characteristic]
language sql
目前沒有作用
[not] deterministic
如果沒設,預設為 not deterministic
{ CONTAINS SQL NO SQL READS SQL DATA MODIFIES SQL DATA }
目前沒有強制性,mysql只會當做註解
sql security {definerinvoker}
預設為definer
comment '註解內容'

修改
alter procedure sp_name ...

刪除
drop procedure sp_name;

執行stored procedure
call 預存程序名稱(參數);
以實際範例操作會比較清楚。
/**
*建立store procedure,名稱為sp_test
*/
delimiter //
create definer=current_user procedure sp_test(out version varchar(60),out day varchar(60))
language sql
not deterministic
contains sql
sql security definer
begin
select version() into version;
select curdate() into day;
end;
//
delimiter ;
/**
*執行stored procedure,stored procedure的名稱為sp_test
*/
call sp_test(@version,@day);
select @version,@day;

變數的處理
變數宣告
declare i int default 1;
變數的設定
set i=3;
select 4*5 into i;

例外處理
例外訊息列表
http://dev.mysql.com/doc/refman/5.0/en/error-messages-server.html
看下面的實例比較快
delimiter //
create procedure sp_h()
begin
declare no_table condition for 1146;
declare continue handler for no_table
begin
select 'hi!! world!';
end;
select * from a;
end
//
delimiter ;

cursor
只能讀
只能單向移動,不能跳過任一列
create procedure test()
begin
declare var_name varchar(30);
declare cur1 cursor for select name from a;
declare exit handler for sqlstate '02000' begin end;
open cur1;
repeat
fetch cur1 into var_name;
select var_name;
until 0 end repeat;
close cur1;
end;

宣告的順序
變數及condition
cursor
handler

流程控制
mysql的stored procedure流程控制語法跟一般的程式語言相同,有迴圈及判斷式的二類。迴圈及判斷式的用法與php的用法相同,只是語法不一樣,寫幾個具體範例熟悉一下迴圈及判斷式的語法會比較較快進入狀況,manual上的說明,範例太少了,自己出幾個題目,動手寫一些小程式會比較快熟悉stored procedure的迴圈判斷式的語法。
Mysql的stored procedure的iterate及leave是用在迴圈的語法,iterate的功能與php中的continue相同,leave就跟php中的break相同,都是控制迴圈進行的語法,讓程式能依照判斷式跳出迴圈或繼續重頭執行迴圈中的程式碼。
底下是使用stored procedure的語法寫的簡易程式,最好是使用記本先打好,再匯入mysql或是使用mysql的圖形化介面的管理工具,如navicat、Mysql Administrator,進行stored procedure的撰寫,才會比較快速有效率,不然是很難進行除錯的。工欲善其事,必先善其器,好的工具及方法,可以加快你進入stored procedure世界的速度。
程式很簡單,只要有程式基礎,看一下manual就知道程式是怎麼運作,但學習方法比較容易被忽略,如果只是看看過去,沒有實際撰寫的話,不容易深入了解stored procedure的運作。最好自己出題,實際寫幾個stored procedure,搭配manual上的說明,才能對stored procedure有完整的認識。
以loop迴圈寫的九九乘法表
delimiter //
create procedure sp_a()
begin
declare i int default 1;
declare j int default 1;
label_a: loop
if i<=9 then
set j=1;
label_b: loop
if j<=9 then
select i,j,i*j;
set j=j+1;
iterate label_b;
end if;
leave label_b;
end loop label_b;
set i=i+1;
iterate label_a;
end if;
leave label_a;
end loop label_a;
end;
//
delimiter ;


以repeat迴圈寫的九九乘法表
delimiter //
create procedure sp_b()
begin
declare i int default 1;
declare j int default 1;
repeat
set j=1;
repeat
select i,j,i*j;
set j=j+1;
until j>9 end repeat;
set i=i+1;
until i>9 end repeat;
end;
//
delimiter ;


以while迴圈寫的九九乘法表
delimiter //
create procedure sp_c()
begin
declare i int default 1;
declare j int default 1;
while i<=9 do
set j=1;
while j<=9 do
select i,j,i*j;
set j=j+1;
end while;
set i=i+1;
end while;
end;
//
delimiter ;


if判斷式的使用
delimiter //
create procedure sp_d(in age int)
begin
if age<18 then
select 'you are young man';
elseif age>=18 and age<40 then
select 'you have many life experiences';
else
select 'you ard old man';
end if;
end;
//
delimiter ;


case判斷式的使用
delimiter //
create procedure sp_e(in selector int)
begin
case selector
when 1 then
select 'yellow';
when 2 then
select 'black';
when 3 then
select 'red';
else
select 'no color selected';
end case;
end;
//
delimiter ;

2008年6月28日 星期六

看chm

在ubuntu上看chm檔的工具

apt-get install gnochm

大易輸入法

ubuntu上內定的輸入工具scim,大易輸入法是使用三碼輸入,跟我習慣的四碼輸入不太一樣,要重頭適應,實在是很痛苦.

上網找了一下資料,把scim換成gcin,就可以用四碼的大易輸入法了.但是標點符號,還是一樣沒辦法用大易四碼的方式打出來,輸入標點符號挺麻煩的.

scim換成gcin的步驟如下

apt-get install gcin
im-swith -s gcin
登出
重新登入

2008年6月24日 星期二

上傳檔案的設定

http://www.php.net/manual/en/features.file-upload.common-pitfalls.php

php相關設定
upload_max_filesize
memory_limit
max_execution_time
post_max_size

mysql相關設定
max_allowed_packet

撰寫上傳物件的問題

想要寫一個檔案上傳的物件,以方便以後程式碼的重複使用,不必重造輪子,做相同的事情,仔細思考很久,如果上傳的檔案都存在目錄裡,還必須考量到檔案備份、程式碼中檔案上傳的安全性,使用者上傳的檔案名稱也必須做管控,才能避免檔案重複,造成檔案彼此覆蓋的問題,因此決定將上傳物件,寫成將插入資料庫的型式,如果以物件導向的模式方式思考,其實應該實做一個interface或abstract,再分別撰寫要上傳至資料庫的物件,及上傳至目錄的物件,這樣才能顧及程式的擴展性及可維護性,不過現在沒想那麼多,先能用就好了。

檔案存到資料庫中,只要必份資料庫並小心權限設定,程式碼使用prepared statement操控資料庫,就可以避掉sql injection及檔案備份的問題,以後只要備份資料庫就可以了,而不用再備份檔案,也不用再花心思控管上傳檔案的安全性。比較省事。

上傳物件,在網路上有許多現成的程式碼可以參考,各種寫法看了讓人頭昏眼花,參考了php manual後,最後決定使用file_get_contents函數進行檔案的處理,這個函數是php manual上建議使用的函數,雖然php 6.0以後才會完整支援所有的參數,但轉換檔案為string時,這個函數的轉換效率是最好的。

如果使用fopen開啟檔案,再使用read()函數進行讀取,要記得加上參數'rb',如fopen(檔案,'rb'),因為windows系統對待binary跟文字檔是不同的,所以要記得加b,操作時才不會出錯。

測試上傳物件時,檔案比較大的物件上傳時,都會被切成只有64k,找程式碼找了半天,也找不到錯誤,後來,在mysql manual上找到原因了,blob的儲存上限是64k,mediumblob是16M,bigblob是4g,改欄位的資料型態就一切正常了。

另一個在測試時碰到的問題,就是中文檔名下載時會出會錯誤,只要使用urlencode()函數將檔名重新編碼,就不會有這個問題了。

2008年6月23日 星期一

多媒體播放器

在pchome上看到的二套多媒體播放器,中文檔名不會出現亂碼

看影片

smplayer



聽mp3

偏好設定的部份,取消「使用點陣字」勾選,就可顯示中文

audacious

2008年6月18日 星期三

Zend_Acl_Role的設計




/**

* 程式碼是以陳瑩光老師新版學務系統的認證模組改寫的。

*/




要實做Zend_Acl_Role的多重繼承架構,有四個區塊的觀念必須弄清楚,才能把整個架構完整的實做,這四個區塊都弄楚了,程式的實做就是水到渠成的事情。




第一個要弄清楚的區塊就是Zend_Acl_Role的多重繼承觀念,Zend_Acl_Role的繼承關係,跟Zend_Acl_Resource不一樣,Zend_Acl_Resource只能有一個parent,但Zend_Acl_Role卻可以有很多個parent,就像現實生活中的人物一樣,每個人都身兼多重身份,Zend_Acl_Role也可以同時繼承多個parent,與一個實例說明:假設一個Zend_Acl_Role的身份是專案經理,同時繼承了程式設計師、架構設計師及資料庫規畫師的身份,那麼專案經理就同時繼承了指定給這三個身份的所有資源。




第二個要弄清楚的區塊是資料庫結構,如果不使用資料庫,而將Zend_Acl_Role的資料寫在程式碼中,程式碼會變得很冗長,要一行一行coding,不是用幾個迴圈就可以解決的,也難以維護,如果資料非常龐雜,不但程式碼的行數會呈等比級數增加,也會很難搞清楚其中的繼承關係,程式也容易寫錯,為了一勞永逸,最好將這些資料都存入資料庫進行維護管理。存成xml或是ini檔也是可以解決部份的問題,不過資料如果太複雜,也會造成難以維護的結果,最好的解決辦法,就是使用資料庫。




第三個要弄清楚的就是陣列結構,使用陣列結構,只要從資料庫中抓一次資料,將資料轉為陣列結構,在後續的程序中直接使用陣列結構進行資料的操作,如果不使用陣列,那麼後續的程序,會不斷的讀取資料庫,雖然不能說這樣的作法有錯,但資料庫的資料都是存放在硬碟中的,每次讀取資料庫中的資料都會讀取硬碟,而陣列是存放在記憶體裡頭,在速度上,記憶體比硬碟快很多,因此,陣列的操作速度會比資料庫的操作速度快,為了加快速度,最好儘量使用陣列結構進行程式資料的存取。




第四個區塊就是程式的邏輯,這部份要花費很大的心思才能搞懂,如果弄懂了,程式的實做就很快了,只要照著你的思考順序,一步步的將解決方式寫出來就可以建構完整的程式碼了。




用freemind畫了四個區塊的架構圖,以後如果忘記了,不用看程式碼,也不用看說明,看這張圖就一目了然了。



以Zend Framework實做Zend_Acl_Role的雛型


class AclController extends Zend_Controller_Action{
public function init(){
$this->_helper->viewRenderer->setNoRender();
Zend_Loader::loadClass('Zend_Acl');
Zend_Loader::loadClass('Zend_Acl_Role');
Zend_Loader::loadClass('Zend_Acl_Resource');

$acl=new Zend_Acl();
Zend_Registry::set('acl',$acl);
}

public function roleAction(){

$baseDir=Zend_Registry::get('baseDir');

$config=new Zend_Config(require($baseDir.'/application/etc/db.php'));

$db=Zend_Db::factory($config->public);

$db->query('set names utf8');

$sql='select a.id,b.parent_id from acl_role as a left outer join acl_inheritance as b on a.id=b.child_id';
$stmt=$db->query($sql);
$rows=$stmt->fetchAll();

/**
* 將資料轉為所需的陣列結構
* Zend_Acl_Role註冊時所需的陣列結構
*/
$roles=array();
foreach($rows as $row){
if(!isset($roles[$row['id']]))
$roles[$row['id']]=array();
if(isset($row['parent_id']))
$roles[$row['id']][]=$row['parent_id'];
}

$acl=Zend_Registry::get('acl');
do{
$flag=false;

/**
* 註冊role
*/
foreach($roles as $key=>$role){
if(!$acl->hasRole($key) && (!isset($role) || $this->hasAllRole($role))){
$acl->addRole(new Zend_Acl_Role($key),$role);
}
/**
* 檢查第一維的key是否都己註冊為role
* 是---離開
* 否---繼續
*/
if(!$acl->hasRole($key))
$flag=true;
}
}while($flag);

}

public function hasAllRole(array &$role){
$acl=Zend_Registry::get('acl');
foreach($role as $parent_id){
if(!$acl->hasRole($parent_id))
return false;
}
return true;
}

}

2008年6月17日 星期二

Zend_Acl_Resource的樹狀結構



Zend_Acl_Resource的樹狀結構

要建立Zend_Acl_Resource的樹狀結構,對我來說難度滿高的,想了很久,都想不出解法,最後看了陳瑩光老師寫的新版學務系統中的認證模組,找到了解法,不過雖然解法找到了,卻不明瞭為何要這樣寫,思考了很久,才把需要了解的區塊,一個個弄清楚,再把整個解法背後運用的思考邏輯用freemind全部畫出來,再依照這張畫出來的圖,自己弄出一個建構Zend_Acl_Resource樹狀結構的原型,我自己寫的程式改良了原來程式碼的部份寫法,不過,沒有將錯誤機制考慮在內,也就是說,在資料庫內的資料都必須是正確的。



建立Zend_Acl_Resource樹狀結構的資料庫內的欄位資料

資料表:resource

欄位
id----------Primary key(resource的名稱)
parent_id---參照id或是null


要建立Zend_Acl_Resource的樹狀結構的第一步就是將資料從資料庫中抓到陣列中,如果不這麼做,那麼每建立一層樹狀結構就要把資料表的全部資料讀取一遍,是非常耗時的,如果把資料抓到陣列中,由於陣列是存放在記憶體中,比起存放在資料表中的資料,讀取速度快非常多,為了效能的考量,一定要先將資料讀入陣列中。

第二步就是開始建立整個樹狀結構,原理是利用迴圈的方式,第一次迴圈會找出所有id未註冊且parent_id是null的陣列值,註冊為最上層的resource,第二次迴圈會找出所有id未註冊且parent_id(參照到最上層resource的id)已註冊的陣列值,註冊為第二層的resource,第三次迴圈會找出所有id未註冊且parent_id(參照到第二層的resource的id)已註冊的陣列值,註冊為第三層的resource。這個迴圈會一直持續到所有id都註冊為止。


Zend_Loader::loadClass('Zend_Acl');
Zend_Loader::loadClass('Zend_Acl_Role');
Zend_Loader::loadClass('Zend_Acl_Resource');
Zend_Loader::loadClass('Zend_Db');
Zend_Loader::loadClass('Zend_Config');
Zend_Loader::loadClass('Zend_Registry');

$baseDir=Zend_Registry::get('baseDir');

$config=new Zend_Config(require($baseDir.'/application/etc/db.php'));

/**
* 讀取資料表欄位,將資料表內的資料讀入陣列
*/
$db=Zend_Db::factory($config->public);
$db->query('set names utf8');
$sql='select id,parent_id from resource';
$stmt=$db->query($sql);
$rows=$stmt->fetchAll();




/**
* 建立resource的樹狀結構
*/
$acl=new Zend_Acl();
do{
$flag=false;
foreach($rows as $row){

/**
* 建立resource樹狀結構的判斷流程程式碼
*/
if(!$acl->has($row['id']) && ($row['parent_id']===null || $acl->has($row['parent_id']) ) ){
$acl->add(new Zend_Acl_Resource($row['id']),$row['parent_id']);
}

/**
* 判斷所有id是否已註冊為resource,如果有未註冊的id,旗標設為true,繼續執行迴圈
* 如果所有的id都已註冊,就會跳出迴圈
*/
if(!$acl->has($row['id']))
$flag=true;

}
}while($flag);

2008年6月11日 星期三

php上傳物件

參考網址

http://www.devshed.com/c/a/PHP/Developing-a-Modular-Class-For-a-PHP-File-Uploader/1/


檔案上傳物件

上列網址是一個檔案上傳物件的實例,用了一些php內建的函數及物件,使用內建函數及物件,執行效率會提高(因為是用c寫的),程式內也用了迴圈的方式把全域陣列$_FILE中的key/value都對應到物件中的屬性,這樣就不用寫一堆討人厭的$_FILE['userfile']['name']這類一長串不好輸入的字元,整個程式碼很清爽。

撰寫這個物件的背景知識

使用的函數及物件
Exception物件
in_array()函數
move_uploaded_file()函數

使用的全域陣列
$_FILE['userfile']['name'] 原始檔名
$_FILE['userfile']['tmp_name'] 儲在server上的暫存檔名
$_FILE['userfile']['type'] 檔案的mime型態
$_FILE['userfile']['size'] 檔案大小,單位是byte
$_FILE['userfile']['error'] 上傳錯誤,錯誤代碼查php manual就知道了


從上列網址post上來的上傳物件的範例程式碼


class FileUploader{

private $uploadFile;

private $name;

private $tmp_name;

private $type;

private $size;

private $error;

private $allowedTypes=array
('image/jpeg','image/gif','image/png','text/plain','application/ms-word');

public function __construct($uploadDir='C:uploaded_files'){

if(!is_dir($uploadDir)){

throw new Exception('Invalid upload directory.');

}

if(!count($_FILES)){

throw new Exception('Invalid number of file upload parameters.');

}

foreach($_FILES['userfile'] as $key=>$value){

$this->{$key}=$value;

}

if(!in_array($this->type,$this->allowedTypes)){

throw new Exception('Invalid MIME type of target file.');

}

$this->uploadFile=$uploadDir.basename($this->name);

}

// upload target file to specified location

public function upload(){

if(move_uploaded_file($this->tmp_name,$this->uploadFile)){

return true;

}

// throw exception according to error number

switch($this->error){

case 1:

throw new Exception('Target file exceeds maximum allowed size.');

break;

case 2:

throw new Exception('Target file exceeds the MAX_FILE_SIZE value specified on the upload form.');

break;

case 3:

throw new Exception('Target file was not uploaded completely.');

break;

case 4:

throw new Exception('No target file was uploaded.');

break;

case 6:

throw new Exception('Missing a temporary folder.');

break;

case 7:

throw new Exception('Failed to write target file to disk.');

break;

case 8:

throw new Exception('File upload stopped by extension.');

break;

}

}

}

?>



物件使用範例




try{

if($_POST['send']){

require_once 'fileuploader.php';

$fileUploader=new FileUploader();

if($fileUploader->upload()){

echo 'Target file uploaded successfully!';

}

}

}




catch(Exception $e){

echo $e->getMessage();

exit();

}

?>

2008年6月10日 星期二

郵件過量

用evolution收信,一段時間後信箱容量超過2G,就不能再收信,覺得很悶,找了一下郵件的放置位置,發現evolution中同一個資料夾的郵件都放在同一個檔案裡頭,難怪資料夾的容量到了一定上限就不能再存了.

上網找了一下資料,解決方式不難,只要再建一個資料夾,建立新的郵件規則,將收到的信都存到這個資料夾中,就解決了.

測試了一下,果然可以繼續收信,只不過不知道有沒有漏掉重要的信件.

workbench的使用



http://dev.mysql.com/downloads/workbench/5.0.html

MySQL Workbench可以很方便的畫出EER(enhance entity relationship)圖,圖形介面的操作方式非常容易上手,可以很快的畫出資料表跟資料表之間的關係,要修改資料表或資料庫的設定也非常容易,用滑鼠點一點、按一按,就修改好了,這種針對資料庫的專用工具,比起自己用freemind或dia畫EER圖,更有效率,由於Workbench將資料庫層面的物件關係加在設計裡頭,也更容易看出資料表之間的關連,不用自己發揮想像力,克難地使用不適當地工具,進行資料庫的設計。

用workbench畫好EER圖後,這張EER圖可以依使用者的需求輸出成pdf、圖檔或是sql命令,最方便地部份就是輸出成sql命令了,將資料庫設計好後,直接將sql命令匯到mysql server後,整個資料庫就在server上全部建立完成了。只不過根據我自己的測試,workbench有一項小缺點,他不會檢查foreign key的型態與referenced key的型態是否一致,這二個如果不一致是沒辦法在mysql裡頭建立資料表的,但workbench不會檢查這項錯誤,如果將這類的sql命令匯入mysql中,資料表會無法建立,致於還有沒有其他的缺點,就不清楚了。

workbench目前只有window版,沒有linux版,有點不方便,我家裡的電腦已經換成ubuntu了,如果沒有linux版,在家裡就沒辦法用了,不過網站上的roadmap,指出今年會推出linux版,看來需要耐心等待linux版的推出,不然就要再想想別的辦法了。

2008年6月6日 星期五

Zend Controller設計概念

Zend Controller是Zend Framework的核心元件,Zend Controller 實做了MVC及Front Controller的設計模式,使用MVC可以將網站程式的商業邏輯及呈現外貌的程式碼分離,Front Controller會將使用者的送出的要求全部導向單人入口。

為了顧及程式的延展性,Zend Controller可以讓程式設計師subclass,或是使用interface及abstract的方式擴增程式的功能,或是撰寫plugin及action helper達到相同的功能。

Zend Controller的設計提供一個撰寫程式的架構,基本上,遵循這些設計模式,幾乎大部份的程式功能都可以有條不紊的實做出來,Zend Controller的架構比自己建立的架構,具有更高的流通性,由於Zend網站上就有現成的文件及程式碼解說,與別人進行交流時不用再重頭適應彼此的撰寫模式及架構,也可避免大家都浪費心力做同一件事情--建立基礎架構,而且Zend Controller的架構都是高手建構出來的,模仿他們的設計,可以吸取高手的菁華,快速提升程式碼的品質。

2008年6月5日 星期四

css概念


參考網址

http://www.hsiu28.net/style/

http://css.1keydata.com/tw/



如果以我的思考方式解釋css(cascading style sheet),css是一種表現網頁物件的方式,在css中,每個物件都是一個box,這個box中可以出現文字、圖片,也能設定這個box的邊框大小、背景的樣式,box中出現的文字、圖片也可以再進行設定,在網頁中出現的大大小小的box,組成了整個網頁的外貌。

組成網頁的box,可以在box中再裝進其他的box,或是進行box位置的排列,這會產生許多不同的變化,讓網頁產生不同的面貌,使用這種方式設計網頁,會使整個網頁的程式碼減少,因為是以box為單位,進行設定,而不是對所有的單一元素進行設定,而且不同的box,可以使用套用相同的設定,這樣不但使程式碼減少,而且網頁內容—html碼,及網頁呈現方式—css碼,可以完全分離,如果要改變網頁的外貌,只要更改css碼,所有的網頁外觀就會立刻更換,而不用繁鎖的一一更改所有網頁元素的設定。

花了點時間,用freemind把css的模式畫出來,也標記了常用的屬性,這樣要用到時就比較方便了,不用再上網查css屬性要怎麼用,也比較不容易在box model的迷宮中走失。

善用工具,不求甚解

以前從國小升上國中時,翻看數學,物理,化學,這些科目的教科書,感覺有如天書,經過一段時間後,與書本那種遙遠的距離感消失了,但那神秘感也消失殆盡,只感覺這些只是用另一種不常見的符號,解釋周遭現象的另一種方式.但心裡總覺得這些背後應該還有更神秘的原理支配著,這種感覺常讓我懷疑,學習這些東西,並沒有真的碰到核心.

剛接觸php程式語言時,也是一頭霧水,等到了解基本語法時,就會感覺好像只是運用一些語句,組合出各種狀況下要電腦做的事情,似乎又是另外一種換句話說的方式,底層的運作到底是怎麼樣的一個狀況,這個問題一直縈繞心頭.

接觸的電腦知識稍微廣些後,知道要通透了解整個的運作是不可能的,跟php相關的電腦議題含括apach,mysql,linux,c語言,設計模式等,要再搞懂底下的這些就要再花很大的功夫,如果再延伸下去,那是沒完沒了的.

很多底層的東西,只要大概了解就好,不須全盤鑽研,就像建築師,只要能善用建築材料,組合出客戶想要的建築物就可以了,甚於房子怎麼搭,鋼筋怎麼綁,那是另一種專業,不需太過清楚,如果一個建築師花大量的心力在研究怎麼砌磚頭,那他大概不會是一位出色的建築師,也許磚頭砌得很好,但建物的造型設計,人性化等方面大概很難兼顧.

接觸php必須以宏觀角度審視整個程式運作流程,致於函數的底層運作機制,不須全盤了解,就像建築師一樣,只要會善用最適當的材料,工法,就能組合出最佳設計,php也一樣,善用工具,才能以宏觀角度使整 個程式運作更符合需求,否則會陷入不斷追求最佳工具的迴圈中,跳脫不出來.

2008年5月28日 星期三

prepared statement

引用網址:
http://dev.mysql.com/tech-resources/articles/4.1/prepared-statements.html

prepared statement

prepared statement是什麼?

Prepared statement 可以讓你在第一次執行時sql時,先設定sql敘述,讓以後相同的sql,只要帶入不同的參數,就重複執行相同的sql,而不用每次執行相同的sql時都讓伺服器耗費時間設定sql敘述。

例子:底下是一個prepared statement的實際範例。

select * from news where id=?

符號? 叫做placeholder

執行sql時,必須代入參數,取代 ? 號,才能真正執行sql敘述。

為什麼要使用prepared statement
程式中使用prepared statement 可以增進安全性及效能。

Prepared statement 把sql的邏輯語句及代入的資料分離,可以增加安全性,這樣的做法,可以避免sql隱碼的攻擊,如果使用一般的sql敘述,沒有分離sql邏輯及代入資料,你必須非常小心處理使用者輸入的資料,通常會使用一些特定的函數,跳脫單引號、雙引號、倒斜線等特殊字元。但是當使用prepared statement時,根本不用使用特殊的函數來避免sql隱碼的攻擊,你可以直接使用prepared statement,不會造成任何sql隱碼的漏洞。

效能的提升來自prepared statement的一些特殊之處,首先,prepared statement中的sql敘述只要解析一次,之後執行的sql敘述就不用再做這些解析的動作,如果你要執行同一個sql敘述很多次,就可以增加執行的速度。

效能提升的第二個原因,是prepared statement使用了binary protocol,傳統的mysql會將資料轉換成字串,再進行傳送,但使用binary protocol後,不會進行資料的轉換,而是直接將資料以原來的binary的型式進行傳送,減低了cpu的負荷,也減少了網路的使用率(轉換成字串,資料量會變大)。

使用prepared statement 的時機
prepared statement只能用在DML(insert,update,delete,replace),create table,select敘述上,額外的支援會再慢慢加上去。

Prepared statement因為要解析兩次,會比一般的sql敘述慢,如果sql敘述只會執行一次,你就要考量,是否值得使用prepared statement增強安全性,而犧牲了整體的執行速度。

如何使用prepared statement
php 5的mysqli extention支援prepared statement,其他的語言我不熟。

Mysql也有使用prepared statement的interface,不過這些interface不支援binary protocol,除非程式語言的api不支援,不然,mysql的prepared statement interface通常當做測試用。

Mysql的prepared statement的interface的用法

create table people(name,sex);

insert into people(name,sex) values('Hi','f'),('a','b');

set @query='select * from people where name=? And sex=?';

set @name='Hi';

set @sex='f';

prepare stmt from @query;

execute stmt using @name,@sex;

2008年5月27日 星期二

trigger


引用網址

http://dev.mysql.com/doc/refman/5.0/en/triggers.html#nolinkhere

trigger

概論:
trigger是資料庫中的一種物件,trigger物件的運作與資料表息息相關,當對資料表執行insert、update或delete的動作時才會觸發指定的trigger物件,執行trigger內的sql敘述,底下以實例說明會比較清楚:

#建立一個資料表,名稱是employ

create table employ(hour int,net_pay int);


#建立一個與employ資料表相關的trigger,名稱是tri_cal
#這個trigger的作用是當插入資料到資料表employ中時,變數sum的值會與資料表employ
#中的新揷入的欄位net_pay的值相加。

create trigger tri_cal
before insert
on employ
for each row
set @sum=@sum+new.net_pay;



#變數sum的值會變成2

set @sum=0;
insert imploy(hour,net_pay) values(1,2);
select @sum;


#變數sum的值會變成4

insert imploy(hour,net_pay) values(1,2);
select @sum;


語法說明:
create [definer={usercurrent_user}] trigger trigger_name
trigger_time trigger_event
on table_name
for each row
trigger_stmt


trigger_time
before
after

trigger_event
insert
update
delete


table_name
指定觸發trigger的table


trigger_stmt
觸發後執行的sql敘述