December 29,2009

[新知] Augmented Reality 擴增實境

今天發現了一個我以前就知道的東西(我在說什麼啊...?):Augmented Reality 擴增實境。最早看到應用類似技術的商品是在日本的遊戲 AR-FIGURE 中:利用 Web Cam 與遊戲廠商提供的實體方塊,建立一個存在電腦螢幕中真實環境裡的虛擬人偶(好繞口...)。第一次看到這玩意兒的時候真是嚇死我了,原來遊戲也可以這樣玩!擴增實境其實也是 Virtual Reality 虛擬實境的一種;但不同於 VR 中所有東西都是虛擬的,AR 是一種與現實環境結合的虛擬。隨著現代科技的進步,攝影機技術已經被廣泛運用生活中;也因為如此,以往印象中虛擬實境所需的厚重眼鏡與手套,已經可以藉由我們生活中常見的手機:攝影鏡頭與觸控螢幕來實現!

Google 前陣子提出的 Google Goggles,似乎就有結合類似的技術;透過即時的影像擷取與分析,立即透過網路取得相關資訊。當然這只是非常小部分的應用,更多的應用可以參考「iGarden – NSS ICT Education Blog 擴增實境Augmented Reality(AR)教學」的相關介紹文章,有非常令人驚艷的展示效果!

今天跟 Jace 提到這個技術,他提到 MIT 有個印度的學生已經結合此技術,實作出更接近未來世界的裝置;後來我 google 了一下,發現了 sixthsense 這個專案。這個專案利用成本大約 320 美金的材料,改裝成一個結合攝影與顯示於一身的特殊裝置,透過這個裝置就能夠即時擷取眼前的影像,並直接投影在物體上,利用手指上的顏色標籤作手勢操作,完全就像電影《關鍵報告》的操作方式!超屌!

以往覺得離我們好遠好遠的未來科技,突然有一種近在咫呎的感覺!Google Goggles 已經實現了動畫《東之伊甸》所描繪的技術,或許不久的將來就能體驗,動畫《電腦線圈》中透過「電腦眼鏡」看世界的科技未來!

P.S 這個影片整個很有《魔鬼終結者》的 fu...墨鏡才是本體啊!(噗)

Posted by taikobo0 at 樂多Roodo!17:07回應(0)引用(0)新知
標籤:Augmented Reality,AR

May 22,2009

[新知] OpenSource 與授權方式

從出社會工作以來,經常接觸到的幾乎所有的程式,都是開放原始碼。不過對於它的定義與其衍生的自由軟體所採用的授權方式,一直以來都是一知半解。前幾天在 PTT Soft_Job 版看到一篇討論【[請益] 請問要如何規避GPL?】,引起我想要弄清楚的興趣;這些不同的授權方式各自到底代表著什麼意義呢?

詳細看了 Wiki 以及網路上一些前輩們的介紹,發現這些不同的軟體授權方式其實還蠻複雜的...就我理解的部分,稍微介紹一些常用的授權許可證;不一定正確,只是簡單紀錄一下(整個想要逃避責任的口氣):

1. GPL
(1)其出發點在於開放程式碼、免費使用和引用、修改或衍生程式碼的開放與免費使用。
(2)符合 Copyleft 機制的授權條款。所以修改或衍生的程式碼,也必須強制繼承 GPL 的規範;確保所有使用 GPL 的自由軟體,能一直保持自由開放的原則。
(3)因為強迫繼承的關係,GPL 也引來一些批評,所以在 PTT 才會引起想要規避的討論。畢竟公司發售軟體,目的當然是為了賺錢;在遇到這種有「傳染性」的授權方式時,難免會感到困擾...

2. BSD
(1)在使用此授權方式的軟體或程式碼時,必須保留版權宣告、三條件表列以及免責聲明。
(2)可以自由的使用,修改原始碼,也可以將修改後的原始碼作為開放原始碼或者專有軟體再發佈。
(3)是使用時限制很少,寬鬆的授權方式。

3. Apache License
(1)在使用此授權方式的軟體或程式碼時,必須保留版權宣告、三條件表列以及免責聲明。
(2)可以自由的使用,修改原始碼,也可以將修改後的原始碼作為開放原始碼或者專有軟體再發佈。
(3)與 BSD 非常類似,不過在修改原始碼後,必須在版權宣告後加注。

4. MIT
(1)可根據程式的需要修改授權條款為適當的內容;但在軟體和軟體的所有副本中仍必須包含版權聲明和許可聲明。
(2)可以自由的使用,修改原始碼,也可以將修改後的原始碼作為開放原始碼或者專有軟體再發佈。
(3)與 BSD 同屬限制少又寬鬆的授權方式;唯一的不同點在於 MIT 的內容可依照程式著作權者的需求而更改。

jQuery 這個 JavaScript Library 為例,它使用的授權協議為 GPL 和 MIT 雙協議,故在使用上符合自由軟體的精神,而且限制也很寬鬆;在去年微軟也曾經發布要將 jQuery 加入 Visual Studio的相關新聞

參考資料:
(1)開放原始碼促進會(英文)
(2)OSSF::自由軟體鑄造場(中文)
(3)五種開源協議的比較(BSD,Apache,GPL,LGPL,MIT) - 整理(中文)

P.S 後來拜讀 OSSF::自由軟體鑄造場【軟體的授權觀念與自由軟體授權類別】投影片(by 自由軟體鑄造場 2008 林誠夏)後,又得到更明確的一些觀念:

自由軟體,擁有六大特性、四大自由
六大特性:開放程式原始碼、不特定授權對象、不限制使用地域、不收取授權金、不隨附擔保、釋放四大自由予後手。
四大自由:執行、研究、改良、重製散佈

另外之前我以為使用或繼承 GPL 條款的自由軟體,就不能收取費用;但其實只要收取費用的名目並不是「授權金」(其他名目如:軟體服務費),作為商業軟體販售是完全合法的!但是因為大部分軟體公司在開發專案時,會將原始碼也當作專案明細的一部分販售;如此一來就與 GPL 必須開放原始碼的宗旨相違背,所以一般軟體公司會想要規避 GPL 的原因就在這裡。

自由軟體的原始概念立意良善,是為了不讓程式撰寫人員閉門造車;如果已經有人發明了輪子,就沒有必要自己再重新設計!拜網際網路發達之賜,讓來自世界各地的程式設計師集思廣益,能更自由的運用前人的智慧,發展更好的功能、更穩定的程式,這不是很棒的一件事嗎!^^

Posted by taikobo0 at 樂多Roodo!10:57回應(0)引用(0)新知

April 9,2009

[AJAX] jQuery的多重下拉式選單應用:Select box manipulation

從本網誌回應數最高的文章得知,大家似乎對多重下拉式選單的功能情有獨鍾啊!Ajax 能夠在不換頁的情況下,達到資料庫連結,是許多人夢寐以求的功能;而 jQuery 易上手與輕鬆操作的特性,讓我們能更簡單的運用 Ajax 達成目的。然而隨著時間的推移,過去介紹的 cascade 已經有很長一段時間沒有更新,而且在使用上其實存在不少綁手綁腳的地方;距離 jQuery 的宗旨「Write Less, Do More」似乎是還差那麼一小段距離...

經由公司的 jQuery 教育訓練,Jace 介紹了一個超讚的 jQuery Plugin:Select box manipulation。透過這個外掛的幫助,可以更輕鬆的實現多重下拉式選單的功能唷,甚至連 cascade 不容易做到的「預設值」也完全沒問題,所需要撰寫的程式碼也少於 cascade,整個就是夢幻的 Plugin!這麼神奇的外掛要怎麼用呢?以下簡單的範例,給有需要的人參考吧:範例是三階層的關聯式多重下拉式選單,分為index.php(呈現頁)、action.php(Ajax 後端資料處理頁)、index.js(JavaScript 處理)、以及 selectboxes ...繼續閱讀

Posted by taikobo0 at 樂多Roodo!12:05回應(12)引用(0)AJAX

March 3,2009

[AJAX] JavaScript 的 PHP 函數寫法:nl2br()、htmlspecialchars()

PHP 真的是一種非常方便的語言,其支援的龐大函式庫,可以讓人很輕鬆自在的使用許多實用的函數。其中常用的 nl2br()htmlspecialchars() 在 HTML 顯示的時候很常使用,但是 JavaScript 就沒有提供類似方便使用的函數了;因為某些原因要在前端使用這二個函數,稍微 google 了一下馬上發現有前輩已經寫出來囉!在此作個紀錄並分享給有需要的人:(來源:Replace newlines with BR (platform safe)Javascript 的 htmlspecialchars function 與 htmlspecialchars_decode function,感謝網路上的前輩們^^)
// javascript 版本的 nl2br
var nl2br = function (string) {
    string = escape(string);
    if (string.indexOf('%0D%0A') > -1) {
        var re_nlchar = /%0D%0A/g ;
    } else if (string.indexOf('%0A') > -1) {
        var re_nlchar = /%0A/g ;
    } else if (string.indexOf('%0D') > -1) {
        var re_nlchar = /%0D/g ;
    }
    return unescape(string.replace(re_nlchar, '<br />'));
}

// javascript 版本的 htmlspecialchars
var htmlspecialchars = function (string, quote_style) {
   string = string.toString();

   string = string.replace(/&/g, '&amp;');
   string = string.replace(/</g, '&lt;');
   string = string.replace(/>/g, '&gt;');

   if (quote_style == 'ENT_QUOTES') {
       string = string.replace(/"/g, '&quot;');
       string = string.replace(/\'/g, '&#039;');
   } else if (quote_style != 'ENT_NOQUOTES') {
       string = string.replace(/"/g, '&quot;');
   }

   return string;
}

Posted by taikobo0 at 樂多Roodo!15:14回應(0)引用(0)AJAX

February 5,2009

[PHP] ZendFramework Zend_Db_Table_Rowset::getRow() 的用法

一般在使用 ZendFrameworkZend_Db_Table_Rowset fetchAll() 時,都是需要 Rowset 裡面全部的資料;不過偶爾有會有只需要單筆資料列的情況,這個時候就可以藉助 current() 或是 getRow() 取得單筆資料列。

current() 很單純,就是目前 Rowset 中指向的資料列,通常在沒有指定指標的情況下,就是該 Rowset 中的第一筆資料:
$table = new Table();
$select = $table->select();
$rowset = $table->fetchAll($select);
$row = $rowset->current();
// 這裡的 $row 就會是 $rowset 的第一筆資料列。
getRow() 就比較有彈性,它可以指定取得第幾個資料列,並決定是不是要將指標移至該資料列(預設為不指定)。
getRow($position, $seek = false) 
// $position 設定取得第幾列資料(從 0 開始)
// $seek 設定是否要將指標移至該資料列(預設為不指定)
用法如下:
$table = new Table();
$select = $table->select();
$rowset = $table->fetchAll($select);
$row = $rowset->getRow(1);
// 這裡的 $row 就會是 $rowset 的第二筆資料列。
要比較注意的是 $seek 的使用時機,是否有需要移動指標,就看程式的需求囉;使用後呈現的資料,如以下的例子:
$table = new Table();
$select = $table->select();
$rowset = $table->fetchAll($select);
$row = $rowset->current();
// 這裡的 $row 就會是 $rowset 的第一筆資料列。

$row = $rowset->getRow(1);
// 這裡的 $row 就會是 $rowset 的第二筆資料列($seek 預設為 false,故指標不移動)。

$row = $rowset->current();
// 這裡的 $row 還是 $rowset 的第一筆資料列。

$row = $rowset->getRow(1, true);
// 這裡的 $row 就會是 $rowset 的第二筆資料列($seek 設為 true,故指標移動)。

$row = $rowset->current();
// 這裡的 $row 就會變成是 $rowset 的第二筆資料列。
大概就是這樣;在這邊做個紀錄,感謝 Jace 解惑^^

Posted by taikobo0 at 樂多Roodo!15:27回應(0)引用(0)PHP

December 25,2008

[CSS] 面對非標準的 IE 瀏覽器,圖片撐破表單的解決方法

在撰寫網頁程式的時候,除了程式的撰寫以外,最花時間的部分大概就是版型的套用吧。有經驗的網頁美工設計師會利用 CSS 進行網站版面的配置,CSS 的運用對網頁程式設計師來說會比較親切;但是對大部分習慣以 Dreamwaver 為開發工具的網頁美工設計師來說,表格 table 的運用似乎比較心應手。雖然說如果一個簡單的網頁設計要從無到有,我也會比較傾向使用表格;但是對於複雜的網頁樣板來說,巢狀的表格真的是會讓人眼花撩亂啊...(噗)

其實以上都是閒聊...(汗)今天碰到的問題是在 Firefox 上看版面沒有問題,但是 IE 上卻發生表格被圖片撐開的情況。在全是表格的網頁上,被撐開的圖片斷層殘破不堪,實在非常有礙觀瞻;雖然大家都知道 IE 對網頁標準支援非常的不完全(wiki 寫的很含蓄:「只是有一些排版錯誤」),不過對於大部分的人來說,IE 幾乎就是瀏覽器的代名詞,所以既然有問題就還是得解決...

因為小弟經驗不足,找了半天還是找不出解決方法~後來經由 Abu 的幫忙,總算是用 CSS 解決問題囉!會發生圖片斷層的原因主要是因為圖片大小超過表單的大小,雖然我明明就把表格的高度與圖片的高度設定的一模一樣,但是好樣的 IE 就是會判斷錯誤,造成圖片斷層的問題。這個時候只要在 <img> 中加入 CSS:

<img style="float: left;" src="/sample.jpg" alt="測試圖片"/>

原本歪七扭八,七零八落的圖片就能完全契合於表格中囉!真是比吃滷肉飯還簡單哩~謝謝 Abu!

Posted by taikobo0 at 樂多Roodo!18:00回應(2)引用(0)CSS

September 3,2008

[AJAX] jQuery的多重下拉式選單應用,當有預設值的時候

在多重下拉式選單的應用中,常常也會遇到已經有預設值的時候,這時候該怎麼辦呢?其實前陣子我也遇過這個問題,當初還花了一點時間尋找 jQuery的cascade 是不是有提供參數讓我輕鬆預設;不過拜完 Google 大神之後似乎是沒有什麼線索;所以最後我採用了一個很笨的方法,在頁面讀取完畢時如果有預設值的話,就直接丟一個 Ajax 取得第二層的選項。以下是實作的程式碼:(目前我只實作到二階層)

index.php:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.cascade.js"></script>
<script type="text/javascript" src="jquery.cascade.ext.js"></script>
<script type="text/javascript" src="jquery.templating.js"></script>
</head>
<body>
第一項 <select id="myParentSelect">
<option value="">請選擇</option>
<?php
    // 資料庫設定
    $host_sql = "localhost";
    $username_sql = "username";
    $password_sql = "password";

    // 預設值設定
    $defaultParentId = '3';
    $defaultChildId = '13';


    $link = mysql_connect($host_sql, $username_sql, $password_sql) or die("無法連結資料庫");
    mysql_select_db('target', $link);
    mysql_query("SET NAMES UTF8");

    $query = "SELECT id, name FROM table where lv = 1";
    $result = mysql_query($query, $link);
    while ($row = mysql_fetch_assoc($result)) {
        echo '<option value="' . $row["id"] . '">' . $row["name"] . '</option>' . "\n";
    }

?>
</select>

<!-- 設定一個隱藏的欄位紀錄預設值 -->
<input type="hidden" name="defaultParentId" value="<?php echo $defaultParentId; ?>" />
<input type="hidden" name="defaultChildId" value="<?php echo $defaultChildId; ?>" />

 
第二項 <select id="myChildSelect">
<option value="">請選擇</option>
</select>

<script type="text/javascript">

// 如果已有預設值,即時送出 Ajax 呼叫
function getSelectedList(defaultParentId, defaultFirstChildId) {
    $.ajax({
        type: "get",
        url:  'action.php',
        data: { act: 'default', val: defaultParentId, child: defaultFirstChildId },
        dataType: "json",
        success: function (json) {
            $('select#myChildSelect').append(json.data);
        }
    });
};


$(function () {

    // 檢查是否有預設值
    if ($('input[name=defaultParentId]').val()) {
        getSelectedList ($('input[name=defaultParentId]').val(), $('input[name=defaultChildId]').val());
    }


    // 第一階層對應第二階層
    $('#myChildSelect').cascade('#myParentSelect', {
    ajax: {
        type: "post",
        url:  'action.php',
        data: { act: 'first', val: $('#myParentSelect').val() },
        dataType: "json",
    },
    template: function(item) { return "<option value='" + item.Value + "'>" + item.Text + "</option>"; },
    match: function(selectedValue) { return this.When == selectedValue; }
    });
});
</script>
</body>
</html>

action.php:

<?php

// 資料庫設定
$host_sql = "localhost";
$username_sql = "username";
$password_sql = "password";

$link = mysql_connect($host_sql, $username_sql, $password_sql) or die("無法連結資料庫");
mysql_select_db('target', $link);
mysql_query("SET NAMES UTF8");

if (!empty($_GET['act'])) {
    $action = $_GET['act'];
}

if (!empty($_GET['val'])) {
    $parentId = $_GET['val'];
}

if (!empty($_GET['child'])) {
    $childId = $_GET['child'];
}


switch ($action) {
    case 'first':
        $list = array();
        $query = "SELECT id, name FROM table where lv = 2 AND parentid= $parentId";
        $result = mysql_query($query, $link);
        while ($row = mysql_fetch_assoc($result)) {
            $arr = array ('When' => $parentId, 'Value' => $row["ID"], 'Text' => $row["NAME"]);
            $list[] = $arr;

        }
        break;
    case 'default':
    default :
        $list = '';
        $query = "SELECT id, name FROM table where lv = 2 AND parentid= $parentId";
        $result = mysql_query($query, $link);
        while ($row = mysql_fetch_assoc($result)) {
            $list .= '<option value="' . $row["ID"] . '"';
            if ($childId == $row["ID"]) {
                $list .= ' selected="selected"';
            }
            $list .= '>' . $row["NAME"] . '</option>';
        }
        $list = array('data' => $list);

        break;
}

echo json_encode($list);

第二階層的選項,其實是用字串硬湊出來的,所以程式碼看起來還蠻醜的,請見諒。基本上用的概念只是在頁面讀取完畢以後直接送出 Ajax 而已,是蠻笨的方法;不過也不失為解決問題的一種方法啦!如果有其他適合的解法,有希望高手們能提供一下嚕!謝謝^^

Posted by taikobo0 at 樂多Roodo!10:32回應(0)引用(0)AJAX

September 1,2008

[AJAX] jQuery 的 lightBox

一般網頁上圖片的展示,很難讓人不聯想到 Lightbox;這個 Prototype 的燈箱特效。無奈現在公司使用的 jQuery 與 Lightbox 犯沖~沒辦法讓基於 Prototype 寫成的這個外掛直接引用。雖然如此,這麼經典的覽圖介面 jQuery 怎麼會放過呢?所以,jQuery lightBox plugin 就因此誕生了!

基本上官方網站已經講的非常清楚了,使用方法也跟 Lightbox 非常類似;不過更方便,也更有彈性。只要在需引用的網頁中載入:

<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/jquery.lightbox-0.5.js"></script>
<link rel="stylesheet" type="text/css" href="css/jquery.lightbox-0.5.css" media="screen" />

之後,寫一隻小小的 JavaScript 於頁面中:

<script type="text/javascript">
$(function() {
    $('a[@rel*=lightbox]').lightBox();
});
</script>

就可以完全相容 Lightbox 的使用環境,無痛移植。此外,jQuery lightBox plugin 提供很多可以自訂的參數,自由度提高許多!

Posted by taikobo0 at 樂多Roodo!10:23回應(1)引用(0)AJAX

June 13,2008

[AJAX] jQuery的多重下拉式選單應用 PART2

昨天Jace傳授密技!原本 Ajax 回傳值我是用字串硬湊出來的,不過有密技可以用比較簡單易懂的方式呈現回傳資料,那就是PHP的json_encode!它會把陣列資料轉變成 json 的格式(其實原本 jQuery 吃的格式就是 json ),所以就不用辛苦的自己湊字串啦!提供第二種方法囉~修改過的地方用紅色標記:

index.php:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.cascade.js"></script>
<script type="text/javascript" src="jquery.cascade.ext.js"></script>
<script type="text/javascript" src="jquery.templating.js"></script>
</head>
<body>
第一項 <select id="myParentSelect">
<option value="">請選擇</option>
<?php
    // 資料庫設定
    $host_sql = "localhost";
    $username_sql = "username";
    $password_sql = "password";

    $link = mysql_connect($host_sql, $username_sql, $password_sql) or die("無法連結資料庫");
    mysql_select_db('target', $link);
    mysql_query("SET NAMES UTF8");

    $query = "SELECT id, name FROM table where lv = 1";
    $result = mysql_query($query, $link);
    while ($row = mysql_fetch_assoc($result)) {
        echo '<option value="' . $row["id"] . '">' . $row["name"] . '</option>' . "\n";
    }

?>
</select> 
第二項 <select id="myFirstChildSelect">
<option value="">請選擇</option>
</select>
第三項 <select id="mySecondChildSelect">
<option value="">請選擇</option>
</select>

<script type="text/javascript">
$(function () {

    // 第一階層對應第二階層
    $('#myFirstChildSelect').cascade('#myParentSelect', {
    ajax: {
        type: "post",
        url:  'action.php',
        data: { act: 'first', val: $('#myParentSelect').val() },
        dataType: "json"
    },
    template: function(item) { return "<option value='" + item.Value + "'>" + item.Text + "</option>"; },
    match: function(selectedValue) { return this.When == selectedValue; }
    });

    // 第二階層對應第三階層
    $('#mySecondChildSelect').cascade('#myFirstChildSelect', {
    ajax: {
        type: "post",
        url:  'action.php',
        data: { act: 'second', val: $('#myFirstChildSelect').val() },
        dataType: "json"
    },
    template: function(item) { return "<option value='" + item.Value + "'>" + item.Text + "</option>"; },
    match: function(selectedValue) { return this.When == selectedValue; }
    });
});
</script>
</body>
</html>

action.php:

<?php

// 資料庫設定
$host_sql = "localhost";
$username_sql = "username";
$password_sql = "password";

$link = mysql_connect($host_sql, $username_sql, $password_sql) or die("無法連結資料庫");
mysql_select_db('target', $link);
mysql_query("SET NAMES UTF8");

if (!empty($_GET['act'])) {
    $action = $_GET['act'];
}

if (!empty($_GET['val'])) {
    $parentId = $_GET['val'];
}

$list = array();

switch ($action) {
    case 'first':
        $query = "SELECT id, name FROM table where lv = 2 AND parentid= $parentId";
        $result = mysql_query($query, $link);
        while ($row = mysql_fetch_assoc($result)) {
            $arr = array ('When' => $parentId, 'Value' => $row["ID"], 'Text' => $row["NAME"]);
            $list[] = $arr;

        }
        break;
    case 'second':
    default :
        $query = "SELECT id, name FROM table where lv = 3 AND parentid = $parentId";
        $result = mysql_query($query, $link);
        while ($row = mysql_fetch_assoc($result)) {
            $arr = array ('When' => $parentId, 'Value' => $row["ID"], 'Text' => $row["NAME"]);
            $list[] = $arr;

        }
        break;
}

echo json_encode($list);

哈,這樣就清楚多了呢!當然昨天用字串組的方式也可以囉,其所達到的效果是一樣的啦!提供另一種思考方向^^

Posted by taikobo0 at 樂多Roodo!11:02回應(5)引用(0)AJAX

June 12,2008

[AJAX] jQuery的多重下拉式選單應用

P.S 2009-04-09 update:有更輕鬆簡單的方法,詳見[AJAX] jQuery的多重下拉式選單應用:Select box manipulation

關於Ajax,我想最棒的地方就是可以於背景呼叫資料庫傳值吧~多重下拉式選單就是一項非常棒的Ajax應用;前幾天Jace丟過來一個國外的網址:jQuery.cascade : Cascading values from forms,這篇文章主要是在說明jQuery的cascade,而它就是用來實現多重下拉式選單的功能。

花了一點時間實作了一下,發現非常簡單就能實現!以前我也作過類似的功能,可是花了我非常多的時間...jQuery把它包裝起來,讓一切變的簡單多了;以下是簡單的範例,給有需要的人參考吧:範例是三階層的關聯式多重下拉式選單,分為index.php(呈現頁)、action.php(Ajax後端資料處理頁)、以及jQuery的cascade

index.php:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.cascade.js"></script>
<script type="text/javascript" src="jquery.cascade.ext.js"></script>
<script type="text/javascript" src="jquery.templating.js"></script>
</head>
<body>
第一項 <select id="myParentSelect">
<option value="">請選擇</option>
<?php
    // 資料庫設定
    $host_sql = "localhost";
    $username_sql = "username";
    $password_sql = "password";

    $link = mysql_connect($host_sql, $username_sql, $password_sql) or die("無法連結資料庫");
    mysql_select_db('target', $link);
    mysql_query("SET NAMES UTF8");

    $query = "SELECT id, name FROM table where lv = 1";
    $result = mysql_query($query, $link);
    while ($row = mysql_fetch_assoc($result)) {
        echo '<option value="' . $row["id"] . '">' . $row["name"] . '</option>' . "\n";
    }

?>
</select> 
第二項 <select id="myFirstChildSelect">
<option value="">請選擇</option>
</select>
第三項 <select id="mySecondChildSelect">
<option value="">請選擇</option>
</select>

<script type="text/javascript">
$(function () {

    // 第一階層對應第二階層
    $('#myFirstChildSelect').cascade('#myParentSelect', {
    ajax: {
        type: "post",
        url:  'action.php',
        data: { act: 'first', val: $('#myParentSelect').val() }
    },
    template: function(item) { return "<option value='" + item.Value + "'>" + item.Text + "</option>"; },
    match: function(selectedValue) { return this.When == selectedValue; }
    });

    // 第二階層對應第三階層
    $('#mySecondChildSelect').cascade('#myFirstChildSelect', {
    ajax: {
        type: "post",
        url:  'action.php',
        data: { act: 'second', val: $('#myFirstChildSelect').val() }
    },
    template: function(item) { return "<option value='" + item.Value + "'>" + item.Text + "</option>"; },
    match: function(selectedValue) { return this.When == selectedValue; }
    });
});
</script>
</body>
</html>

action.php:

<?php

// 資料庫設定
$host_sql = "localhost";
$username_sql = "username";
$password_sql = "password";

$link = mysql_connect($host_sql, $username_sql, $password_sql) or die("無法連結資料庫");
mysql_select_db('target', $link);
mysql_query("SET NAMES UTF8");

if (!empty($_GET['act'])) {
    $action = $_GET['act'];
}

if (!empty($_GET['val'])) {
    $parentId = $_GET['val'];
}

$list = '[';

switch ($action) {
    case 'first':
        $query = "SELECT id, name FROM table where lv = 2 AND parentid= $parentId";
        $result = mysql_query($query, $link);
        while ($row = mysql_fetch_assoc($result)) {
            $list .= '{\'When\':\'' . $parentId . '\',\'Value\':\'' . $row["id"] . '\',\'Text\':\'' . $row["name"] . '\'},';
        }
        break;
    case 'second':
    default :
        $query = "SELECT id, name FROM table where lv = 3 AND parentid = $parentId";
        $result = mysql_query($query, $link);
        while ($row = mysql_fetch_assoc($result)) {
            $list .= '{\'When\':\'' . $parentId . '\',\'Value\':\'' . $row["id"] . '\',\'Text\':\'' . $row["name"] . '\'},';
        }
        break;
}

$list .= ']';
echo $list;

實作的重點是在資料的格式:

list = [{'When':'A1','Value':'W','Text':'SubchildA1a'},
          {'When':'A1','Value':'X','Text':'SubchildA1b'},
         ];

其中When代表上一階層的值,Value是此一階層的值,Test則是下拉式選單顯示的文字;要特別注意JavaScript是大小寫敏感的!實作這個範例途中,曾經被大小寫拖了一段時間...後來才發現~所以這地方要特別注意!希望大家都能輕鬆寫出關聯資料庫的多重下拉式選單囉^^

Posted by taikobo0 at 樂多Roodo!11:22回應(31)引用(0)AJAX

June 5,2008

[新知] Comet,Server Push與Ajax的應用?

這二年在Web界引起軒然大波的Ajax,雖然說穿了其內涵不過就是JavaScript的應用,以往也有前輩在許多年前就已經運用這項技術了~然而,在Ajax這個名詞出來以後,這項技術的發展,同時帶動許多著名framework的出現:PrototypeYUI以及目前我們公司使用的jQuery等;在這些好用的框架下,許多以往必須經由繁複程式碼才得以實現的功能,都可以利用函數呼叫的方式輕鬆呈現。

也就因為Ajax的大流行,這幾年有愈來愈多人想把所有本來在單機上執行的桌面程式,全部搬到Web上。桌面程式與網頁程式最大的差別就在,桌面程式與資料庫的連線是一直存在的,但是網頁程式則因為先天上http設計的關係,必須是建立在「客戶端發出請求,伺服端才得以回應」的條件下,才得以與資料庫建立連線;當然,神奇如Ajax也受此制約。這造成了一個問題,如果客戶端想要與伺服端同步更新資料,那應該怎麼辦呢?最常見的方式是Polling,也就是每隔一段時間由客戶端送出請求,看伺服端是否有新的資料回應;只要Polling的間隔時間夠短,客戶端就會有即時更新的錯覺。然而如果資料沒有更新,一來一往無謂的訊息傳送不但浪費頻寬,對伺服端也容易造成龐大負擔。

而Server Push就在需要即時更新資料的需求下出現,早期CGI的網路聊天室就是利用這種方式;如果再結合Ajax,則可以做到背景更新頁面的即時處理。Dojo的作者 Alex Russell 在 2006-03-03 發表的 Comet: Low Latency Data for the Browser,首次提出「Comet」這個新名詞!關於Comet詳細的介紹,可以參考Lighty RoR的文章:Comet For Ruby on Rails and Mongrel淺談 Comet PUSH Server 架構

我覺得,此理論的實現,確實可以讓桌面程式網頁化更輕鬆愉快!但是,目前似乎並沒有特別受到Web界的注意...所以,一切還是等網路前輩們的努力推廣吧~小輩的我就先暫時觀望吧^^|||

Posted by taikobo0 at 樂多Roodo!10:06回應(0)引用(0)新知

May 15,2008

[PHP] Zend_Search_Lucene中文分詞實做

最近在練習中有用到搜尋的功能。一般對MySQL資料庫作搜尋,常用的做法是針對資料表中的特定欄位,用「%」LIKE的方式去尋找。然而這樣的做法常伴隨著許多限制,使用者必須先選定所要輸入的資料欄位,再對其進行搜尋;習慣了Google搜尋所帶來的便利,最理想的方式是只有一個輸入格,且可以在此輸入格中任意輸入,即可對整個資料庫進行搜尋。在MySQL中稱為Full-Text(全文檢索);然而拜完Google大神以後,網路上前輩們幾乎是一面倒的否定全文檢索。最主要的原因是因為它不支持中文!

全文檢索的做法,即是對資料庫裡的資料進行「分詞」的索引處理,有了索引,搜尋起來自然有效率的多;然而中文字不同於英文,一個句子中單獨一個中文字就可能有它的意思,另一個最大的分別在於中文句子可不像英文句子由單字與「空格」組成;建立索引時的「分詞」的動作,就是以空格進行判斷!

全文檢索的問題在網路上一直存在著,但是前輩們似乎都沒有非常完美的解答;甚至有人直接勸退提問者:「全文檢索的功能,是可以讓你寫好幾篇博士論文的研究!」如此可見,Google雲端運算的強大。既然資料庫端無解,我就從PHP的方向著手吧~Google的確是大神,讓我找到了Zend Framework就有分詞的函式Zend Search Lucene;而且原本這個功能其實也不支持中文,萬能的Google大神還幫我找到了支持中文的解決方法!

PHP製作中文全文搜尋不求人中,作者巨細靡遺的說明了資料夾配置、原始程式碼,還非常貼心的提供範例程式的下載。在如何讓Zend_Search_Lucene支持中文分詞中,作者改良了分詞用的類別,讓中文分詞的動作更加準確!既然有如此完整的範例,我當然是馬上適用在練習中啦!

目前練習所利用的開發框架,是Jace一手打造的Wacow Framework。結合Zend FrameworkSmarty,以及許多在專案製作時常會用到的工具,是公司目前專案開發的主力,也是我目前需要熟練的工具。我把建立索引的功能放在首頁,這樣只要有人進入首頁就會觸發建立索引(當然這樣做的代價是每次進首頁的速度都會被拖慢)。因為自動載入類別的關係,在建立索引時不需再額外include或require,程式碼如下:

IndexController.php(部分節錄)

// 建立分詞索引
// 關閉 Notice 錯誤提醒
error_reporting(E_ALL ^ E_NOTICE);
// 資料是 utf8 為編碼的這句為重點。如果你是 utf8 的話必需加入,否則資料會錯誤;另Phpbean是需要另外建立的中文分詞的類別
Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Phpbean());
if (function_exists("set_time_limit") && ! get_cfg_var('safe_mode')) {
set_time_limit(0);
}
$index = new Zend_Search_Lucene('index', true);
$itemTable = new Items();
$itemRowset = $itemTable->fetchAll();
foreach ($itemRowset as $itemRow) {
$url = '/gime/item/detail/id/' . $itemRow->id; // 建立連結
$itemName = $itemRow->name; // 抓出物品名稱
$description = $itemRow->description; // 抓出物品敘述
//儲存網頁的位置以在搜尋結果中連結.
$doc = new Zend_Search_Lucene_Document(); // 建立新的索引文件
$doc->addField(Zend_Search_Lucene_Field::UnIndexed('url', strtolower($url)));
$doc->addField(Zend_Search_Lucene_Field::Text('name', strtolower($itemName), 'utf-8'));
$doc->addField(Zend_Search_Lucene_Field::Text('contents', strtolower($description), 'utf-8'));
$index->addDocument($doc); //把索引文件加到索引中
}
$index->commit(); //提交,及保存索引

Phpbean.php


class Phpbean extends Zend_Search_Lucene_Analysis_Analyzer_Common
{
private $_position;

private $_cnStopWords = array();

public function setCnStopWords($cnStopWords){
$this->_cnStopWords = $cnStopWords;
}

/**
* Reset token stream
*/
public function reset()
{
$this->_position = 0;
$search = array(",", "/", "\", ".", ";", ":", """, "!", "~", "`", "^", "(", ")", "?", "-", "'", "<", ">", "$", "&", "%", "#", "@", "+", "=", "{", "}", "[", "]", ":", ")", "(", ".", "。", ",", "!", ";", "“", "”", "‘", "’", "[", "]", "、", "—", " ", "《", "》", "-", "…", "【", "】","的");
$this->_input = str_replace($search,' ',$this->_input);
$this->_input = str_replace($this->_cnStopWords,' ',$this->_input);
}

/**
* Tokenization stream API
* Get next token
* Returns null at the end of stream
*
* @return Zend_Search_Lucene_Analysis_Token|null
*/
public function nextToken()
{
if ($this->_input === null) {
return null;
}
$len = strlen($this->_input);
while ($this->_position < $len) {
while ($this->_position < $len && $this->_input[$this->_position] == ' ') {
$this->_position++;
}
$termStartPosition = $this->_position;
$temp_char = $this->_input[$this->_position];
$isCnWord = false;
if (ord($temp_char) > 127) {
$i = 0;
while ($this->_position < $len && ord($this->_input[$this->_position]) >127) {
$this->_position = $this->_position + 3;
$i ++;
if ($i == 2) {
$isCnWord = true;
break;
}
}
if ($i == 1) continue;
} else {
while ($this->_position < $len && ctype_alnum($this->_input[$this->_position])) {
$this->_position++;
}
// echo $this->_position.":".$this->_input[$this->_position]."\n";
}
if ($this->_position == $termStartPosition) {
$this->_position++;
continue;
}

$token = new Zend_Search_Lucene_Analysis_Token(
substr($this->_input,
$termStartPosition,
$this->_position - $termStartPosition),
$termStartPosition,
$this->_position);
$token = $this->normalize($token);
if ($isCnWord) $this->_position = $this->_position - 3;
if ($token !== null) {
return $token;
}
}
return null;
}
}

SearchController.php(部分節錄)

Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Phpbean());
$index = new Zend_Search_Lucene('index');
$query = $this->_request->getParam('query'); // 擷取來自表單的關鍵字
$query = trim($query);
if (strlen($query) > 0) {
try {
$query2 = Zend_Search_Lucene_Search_QueryParser::parse(strtolower($query), "utf-8");
$hits = $index->find($query2); // 根據關鍵字找到資料,並將資料回存為物件
}
catch (Zend_Search_Lucene_Exception $ex) {
$hits = array();
}
$numHits = count($hits); // 根據關鍵字找到資料的數量
}

非常簡單的實作;然而就全文檢索來說還是有缺點的!首先,建立分詞索引時必定會耗費系統資源,故比較好的做法是批次定時處理建立索引的動作。第二點是中文的問題,因為中文字詞與連貫的句子的關係,在分詞時是以二個字為一個詞的最基本單位,所以單一個中文字是不會有任何搜尋結果的。最後因為建立分詞索引為觸發事件,如果沒有去觸發它就無法更新分詞至目前資料庫的最新狀態。我覺得分詞索引的方式很像是MySQL的View資料表,也是將資料表中的欄位作一個資料上的更新,只是它沒有欄位的限制,可以針對建立的「詞」索引進行搜尋。就某方面來說是很好用的功能,也不失為中文在全文索引時的一種解決方案。^^

Posted by taikobo0 at 樂多Roodo!10:22回應(2)引用(0)PHP

April 18,2008

[新知] 初探物件導向(二)

這幾天承蒙Jace的諄諄教誨,當初一知半解的物件導向現在總算是有些概念了!

承接上篇的繼承,多型是敘述繼承的狀態,解釋繼承的概念:由於子類別繼承於相同的父類別,因此由子類別建立的物件當然也繼承父類別的方法。而這種「不同物件卻有相同方法」的狀態,即是多型。

抽象類別,依照我的理解可以解釋為一個不能被New(建立)的父類別;當多個類別擁有許多共通的實體變數與方法時,就可以被獨立抽離出來成為一個抽象類別的概念。另外還有抽象方法,代表該方法不能被實作,但繼承該類別的子類別,此方法一定要被實作執行。

介面(interface)則類似抽象,但介面沒有實體變數,只有方法,而且該方法不能被實作(抽象方法),而是經由繼承(implements)自該介面的類別實作,且一定要被執行。

關於抽象與介面,Jace有非常簡潔的說明...不過這太簡潔了吧,不懂的人應該還是不懂?(還有另外一個關於「介面」的說明...)

Posted by taikobo0 at 樂多Roodo!18:32回應(0)引用(0)新知

[PHP] 表單輸入時對於HTML的過濾

在PHP中最常見的應用就是互動表單,因此接收用戶端資訊是非常常見的事!然而俗話說:「世風日下,人心不古」,這年頭誰知道用戶端的人在想些什麼?用戶端送出的資訊很有可能會包含一些惡意的語法,對於24小時暴露在網路環境的網站來說,時時刻刻都得小心防範駭客的攻擊,提升自我的資訊安全觀念非常重要。所以在接收用戶端資訊後,存入資料庫之前,都會先對其進行基本的過濾;第一個要判斷的就是HTML語法的攻擊!

許多惡意語法都是建立在HTML上,PHP本身有提供轉換HTML碼的函數:htmlspecialchars()htmlentities();其中htmlspecialchars()只會轉換HTML相關碼:
  • '&' => '&amp;'
  • '"' => '&quot;'
  • ''' => '&#039;' 
  • '<' => '&lt;'
  • '>' => '&gt;'
htmlentities()則是把字串中所有字元做轉換,另外還可以設定轉換字串的編碼方式。還有一個函數:strip_tags()是直接把HTML的標籤整個過濾掉。

之前在題目試作/哇寶基本能力測試一文中,也有提到相關的觀念。另外附上那個時候google到的一個國外防止XSS的網頁:PHP XSS (cross site scripting) filter function,他有對HTML作是否可能被當作惡意的語法作判斷,不過測試後發現對中文的處理好像有點問題...就當作是英文的過濾函式吧~

Posted by taikobo0 at 樂多Roodo!18:07回應(0)引用(0)PHP

April 16,2008

[新知] 初探物件導向

說來慚愧,之前完全沒有注意物件導向的定義與使用;但是因為現在工作所使用的撰寫方式為全物件導向,這意味著我要重頭學起~從《深入淺出JAVA 第二版》開始!

事實上維基百科對於物件導向程式設計的介紹也非常完整,但是頂多也就這麼一個頁面的介紹,要把物件導向的全貌解釋清楚,還略嫌不足;當初看完整篇介紹,其實對物件導向仍是一知半解的狀態...

之前Jace就在如何學習ZendFramework的介紹文中推薦過《深入淺出JAVA 第二版》這本書。我目前先挑選書中對物件導向介紹的章節研讀:2、拜訪物件村;4、物件的行為;7、物件村的優質生活(繼承與多型);8、深入多形;9、物件的前世今生。目前已經讀完類別、物件、封裝與繼承的觀念;現在可以說是稍微有一點點概念了~

類別(class)他就像是一個規則、一個模子,物件(Object)則是利用這個規則、模子做出來的東西。類別只是規範,如果要在程式中使用的話,就一定得先New一個物件出來!類別包含實體變數(instance variables或稱屬性)與方法(method),也被稱作是狀態與行為;實體變數被用來記載此類別已知的事物,而方法則是用來說明類別執行的動作。類別所描述的是物件「知道什麼」與「執行什麼」!

狀態影響行為,行為影響狀態。方法可以引用實體變數的值作判斷,實體變數可以用參數的方式影響方法;而方法則可以利用的set或是get的方式,對實體變數作存取的動作。封裝的意義在於避免洩漏資料,為了不讓實體變數就這樣直接暴露在隨時隨地可以任意設定的狀況下,在習慣上會將實體變數標示為private,另外利用標示為public的方法setter與getter,對實體變數進行存取,也可在此同時進行限制及過濾。

繼承也是物件導向的特色之一。子類別可以用繼承的方式沿用父類別的實體變數及方法;唯父類別實體變數與方法的標示需為public或protect。A extends B意味著B的種類中含有A,B擁有的所有特性A也會有,然而A擁有的特性B並不一定會有;如白馬是馬,白馬跟馬一樣有長長的臉而且跑的很快,但是並不代表所有的馬都是白色的。子類別繼承父類別所有的方法,但是也可以依照子類別自己的需求,建立新方法;或是完全改寫繼承自父類別的方法(override),但實體變數不行。

Posted by taikobo0 at 樂多Roodo!09:45回應(0)引用(0)新知

April 15,2008

[新知] MVC的觀念

經由透視WebMVC的作者Jace親自講解,讓我稍微了解到什麼是MVC架構。透過一個簡單的留言版程式的範例解構,重新建立一個符合MVC架構的留言版程式。

依照過去一般留言版程式的撰寫模式,先將其分為二個部分:一個是業務邏輯、表現邏輯。業務邏輯(business)是指PHP控制與執行的部分;表現邏輯(present)則是指單純HTML或XML輸出呈現的部分。一但程式分為此二部分,表現邏輯就相當於View的部分;之後再將業務邏輯分成Model與Controller。

Model在留言版程式中所代表的角色,就有點像是「取得資料」這一部分;不論取得資料的方式是經由文字檔或是資料庫的連結讀出寫入,這一塊在MVC的架構中就相當於Model的部分。所以在範例的留言版程式中,Guestbook.php扮演的角色是用來與資料來源溝通的管道。

Controller在留言版程式中所代表的角色,就比較像是「操縱動作」這一部分;從留言版程式的功能面來說,分為選擇頁面(首頁/新增頁/RSS的XML頁面)、新增資料時取得的變數與重新導向。如果說Guestbook.php是與資料來源溝通的管道,Actions.php就像是觸發事件,以及運用Guestbook.php去產生動作的角色;當然最後所呈現的結果是View。所以Controller可以說是串聯Model與View的重要橋樑。

其後又提到一些物件導向的觀念,因為我這方面實在是很弱;所以整個聽下來有些吃力。在我粗淺的理解後導出的一個見解是:利用抽象化後繼承父類別的子類別,可以減少使用判斷式,以及更動到原有程式碼的機會。大概是這樣的意思吧?(抓頭)關於物件導向,Jace建議我看《深入淺出 Java 程式設計 第二版》這本書,我想我得好好花時間研究一下囉!

Posted by taikobo0 at 樂多Roodo!11:40回應(0)引用(0)新知

April 11,2008

[AJAX] 初探jQuery

因為哇寶在Ajax的應用上是採用jQuery,所以利用時間稍為了解一下。

我以往在處理Ajax時大部分是借用網上的範例JS檔,所以它通常都有一套制式的規範,哪裡要new,哪裡要丟參數,範例上都寫得清清楚楚,只要按部就班,通常都能很快達到我想要的功能。另外一種是土法煉鋼,自己造輪子;先從JS丟參數到php,再由php切我要的參數去跟伺服端Query我要的資料,最後呈現。這二種方式其實好像都不是正統的方式說...在看完jQuery的介紹後更是讓我有這樣的感覺~

網路上其實有很多淺顯易懂的介紹,我是看:國二學生認真打雜jQuery 學習心得筆記一系列文章稍微接觸jQuery這個Framework的。詳盡且生動有趣的介紹,讓我對jQuery有粗淺的認識!有興趣可以去逛逛~^^

Posted by taikobo0 at 樂多Roodo!11:29回應(0)引用(0)AJAX

April 6,2008

[PHP] ZendFramework 1.5.1安裝

ZendFramework是PHP的一個開發用框架,主要以PHP5作為撰寫時的基礎。要使用之前當然得先建立環境,以下是安裝的順序:參考「在Zend Framework上開發一個HelloWorld

1、首先先從ZendFramework的官方網站下在最新版本的ZendFramework 1.5.1;解壓縮後放在任意資料夾。我是放在「C:\ZendFramework」這個目錄下。

2、修改Apache httpd.conf設定:
(1)啟動 Apache的 .htaccess功能,搜尋「AllowOverride」 並將其設定為 All
(2)
開啟 LoadModule rewrite_module modules/mod_rewrite.so。

3、
修改PHP php.ini設定:
(1)設定include_path = ".;C:\ZendFramework"
(2)開啟extension=php_pdo.dll、extension=php_pdo_mysql.dll

重新啟動
Apache,如此一來基本的環境算是建置完成了。

Posted by taikobo0 at 樂多Roodo!22:22回應(0)引用(0)PHP

April 3,2008

[新知] Windows的伺服器環境架設

Windows環境下伺服器的安裝:apache_2.2.8-win32-x86-no_ssl.msiphp-5.2.5-Win32.zipmysql-essential-5.0.51a-win32.msiphpMyAdmin-2.11.5-all-languages-utf-8-only.zip

事實上在Windows環境下,我一直是用AppServ;簡單、快速、方便。但事實上各軟體之間的結合,是需要一些設定的。AppServ雖然提供一個很快就能上手的管道,然而他卻不是一個純粹乾淨的環境。那種感覺有點像是重灌時是用Ghost而不是一步步重新建立。

第一次一個一個軟體裝,完全架站手冊幫了我許多;或許裡面的版本有點舊了,不過基本的大方向是相同的。

1、首先先來看Apache的安裝與設定吧:

執行apache_2.2.8-win32-x86-no_ssl.msi->Next->Next->Next->localhost/localhost/E-mail->Custom Next->Next->Install->Finish

猴子也能學會的「軟體安裝Next」!安裝完成以後右下角的工具列會常佇一個Apache Monitor,表示安裝成功啦,可以從這裡控制Apache的服務。重點是設定啊,httpd.conf設定:

DirectoryIndex index.html index.htm index.php  //索引頁的設定

LoadModule php5_module "C:/PHP/php5apache2_2.dll"  //設定PHP5與Apache2.2的連接檔

<IfModule mod_php5.c>
  AddType application/x-httpd-php .php
  AddType application/x-httpd-php .php3
  AddType application/x-httpd-php-source .phps
</IfModule>  //設定Apache使用正確的方式處理 php 副檔名的檔案

2、PHP5的安裝與設定:

解壓縮php-5.2.5-Win32.zip->"C:\PHP\"->複製 php.ini-recommended 到 C:\WINDOWS 資料夾下並改名為 php.ini->複製 C:\PHP\libeay32.dll、C:\PHP\ssleay32.dll及C:\PHP\libmcrypt.dll至 C:\WINDOWS\System32 資料夾

這個有點複雜,因為要複製很多檔案到Windows的系統所在資料夾。php.ini設定:

extension_dir = "C:/PHP/ext"  //設定PHP延伸函式庫存放路徑

extension=php_curl.dll  //不知道
extension=php_gd2.dll  //圖片處理函式庫
extension=php_mbstring.dll  //字串編碼處理函式庫
extension=php_mcrypt.dll  //不清楚
extension=php_mysql.dll  //MySQL資料庫連接函式庫

3、MySQL5的安裝與設定:

執行mysql-essential-5.0.51a-win32.msi->Next->Complete Next->Install->Next->Finish->Configuration Wizard->Next->root 密碼->Execute->Finish

最後手動複製C:\Program Files\MySQL\MySQL Server 5.0\bin\libmySQL.dll 至 C:\WINDOWS\System32 資料夾。重新啟動Apache,建立一個phpinfo.php看看PHP5是否已在Apache環境中,MySQL是否運作正常。一切OK以後,再加上phpMyAdmin就完美啦!

4、phpMyAdmin的安裝與設定:

解壓縮phpMyAdmin-2.11.5-all-languages-utf-8-only.zip->"..\htdocs\phpmyadmin\"->複製 config.sample.inc.php 並改名為 config.inc.php

$cfg['blowfish_secret'] = '0123456789';  //設定cookie登入時的編碼參數

經由之前MySQL設定的root密碼,即可利用phpMyAdmin管理MySQl資料庫囉!


Posted by taikobo0 at 樂多Roodo!14:20回應(0)引用(0)新知

March 26,2008

[新知] 題目試作/哇寶基本能力測試

這是哇寶國際資訊的一份基本能力測試;很多東西雖然不能說完全沒有概念,不過要詳細回答不免還是要google一下,由此可知我的能力還遠遠不足。當作是自我測試與吸取新知吧,開始解題囉...

(X)HTML篇:

1、請寫出有序清單和無序清單的的 HTML Tag 並簡述其差異。

<ol>為有序清單標記;<ul>為無序清單標記。<li>則用來標示項目。<ol>可以追加type/start參數,分別代表排序類型/開始數字;<ul>則是可以追加type,決定標示項目呈現的方式。<li>可以追加type/value參數,決定標式呈現方式/呈現數字。

以下是呈現結果與HTML語法:

==========<呈現結果>==========
  1. 有序第一項
  2. 有序第二項
  3. 有序第三項
  • 無序第一項
  • 無序第二項
  • 無序第三項
==========<HTML語法>==========

<ol type="1">
    <li>有序第一項
    <li value="5">有序第二項
    <li>有序第三項
</ol>
<ul type="square">
    <li>無序第一項
    <li type="circle">無序第二項
    <li type="disc">無序第三項
</ul>

2、請簡述 HTML 空標籤的特性,並試著列出所有的空標籤。

HTML是一種標式語法,通常是成對出現,如:<b>這是粗體字</b>,有起始標籤<b>與結束標籤</b>。空標籤則是指不需要結束標籤的特別標籤。如:<br>斷行與<hr>格線。

3、請簡述 DOCTYPE 對瀏覽器的影響,並說明目前常用的 DOCTYPE 有哪些。

參考網頁:DOCTYPE使用方法。簡單說就是告訴瀏覽器該以怎樣的模式來解讀HTML;文末作者建議採用 XHTML (Transitional)的方式。老實說我以前不是很在意他...看來以後要多注意一點,符合標準。

4、如何在瀏覽器中顯示小於 (<) 或大於 (>) ?如果連結網址中有 & 符號時,應該在 HTML 原始碼中用什麼代替?

「>」:&lt;
「<」:&gt;
「&」:&amp;

5、請找出以下範例中不符合 XHTML 規範的地方:
<!DOCTYPE html PUBLIC "-/W3C//DTD XHTML 1.0 Transtitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>
<head>
<title>測試範例</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="check.js"></script>
</head>
<body>
<form action="">
<table border="1">
<tr>
<td>
<label for="account">帳號</label>
</td>
<td>
<input type="text" name="account" id="account" />
</td>
</tr>
<tr>
<td>
<label for="passwd">密碼</label>
</td>
<td>
<input type="password" name="passwd" id="passwd" />
</td>
</tr>
<tr>
<td>
<label><input type="checkbox" name="keepLogin" value="1" />保持登入</label>
</td>
<td>
<input type="submit" value="登入" onclick="checkForm();" />
</td>
</tr>
</table>
  </form>
</body>
</html>

其實我有作弊...(自首)只能說有W3C真好。

6、同上題,如果在 IE 上瀏覽該頁面時發生亂碼,你會如何解決?假設伺服端送出的標頭及頁面編碼都是正確的。

如果伺服端送出的標頭與頁面編碼都是正確的,那就有可能是HTML撰寫時的原始碼沒有存成對應的編碼。例如現在網頁編輯幾乎都使用UTF8編碼,但如果在HTML撰寫時仍使用ANCI碼儲存,就會發生亂碼。所以在撰寫HTML時,最好是將文件存成與伺服端標頭相同的編碼型態。

7、何謂 HTML 的語意?如果要在頁面上用一個標籤來表示文章標題,你認為用 <h1></h1> 比較好還是 <p class="articleTitle"></p> 比較好?

HTML:HyperText Markup Language,超文件標示語言。有許多前輩們似乎都比較喜歡用CSS,畢竟比起HTML,CSS的規範比較嚴謹,可以表現的變化也比較多;最重要的是它能夠獨立於HTML。網頁設計起來也就能程式歸程式,設計歸設計囉。

JavaScript篇:

1、請找出這段 JavaScript 的錯誤,並寫出正確可執行的版本。

function checkForm() {
    if (document.forms[0].account.value == '') {
        alert('請輸入帳號');
        return false;
    }
    if (document.forms[0].passwd.value == '') {
        alert('請輸入密碼');
        return false;
    }
    return true;
}

不知道為什麼,this.form抓不到表單內容,所以改用document.forms[0]來處理;另外if條件判斷是採用比較的方式,所以「=」要改為「==」。

2、請找出 HTML 範例中的 JavaScript 邏輯錯誤。

HTML範例中,JS的表單判斷是寫在「登入」按鈕上,如此一來即使JS判斷為false,表單資料卻還是傳送出去了。所以應該刪除onclick="checkForm();",改寫HTML的<form>,利用onSubmit去判斷JS的表單驗證。:

<form action="" onsubmit="return checkForm();">

3、請試著改寫 check.js ,在帳號或密碼未填寫任何資料並出現警告時,同時將輸入焦點置於錯誤的欄位上。

function checkForm() {
    if (document.forms[0].account.value == '') {
        alert('請輸入帳號');
        document.forms[0].account.focus();      
        return false;
    }
    if (document.forms[0].passwd.value == '') {
        alert('請輸入密碼');
        document.forms[0].passwd.focus();   
        return false;
    }
    return true;
}

4、上面的例子中利用 JavaScript 驗證後的資料,是否還需要用 PHP 或其他伺服端技術驗證?為什麼?

最安全的方法,當然是客戶端與伺服端都作驗證。曾經在網路上看過一句話:「永遠不要相信使用者輸入的資料!」單作JS的表單驗證其實是不夠的,拿表單傳值的GET來說,直接載網址列輸入資料就可以避開JS的驗證了。JS的驗證是為了減輕伺服的負擔,同時減少錯誤反應的時間。如果每筆資料的輸入都單靠伺服端的驗證,伺服器的負擔與使用者等待的時間就會成為問題。

5、請試著不要使用 onxxxx 事件,改用 window.onload 來繫結表單驗證的功能。

能力不足。

6、請說明 window.onload 可能潛藏的問題,並說明有什麼可以解決的方式 (不可使用第三方套件)。

能力不足。

PHP篇:

1、請說明 PHP 字串採用單引號和雙引號的差別。

單引號內如果有變數$var,不會被解讀;雙引號內如果有變數$var,則可以被解讀。

<?php
    $var = 'World!';
    echo 'Hello'.$var;  //印出「HelloWorld!」
    echo "Hello $var";  //印出「Hello World!」
    echo 'Hello $var';  //印出「'Hello $var」
?>

2、請說明如何接收網址參數。例如 http://localhost/index.php?abc=123 中的 abc 。

此網址使用GET傳遞參數,所以使用$_GET['abc']就可以接收。

3、請說明如何接收上面 HTML 範例的表單內容。

一般<form>如果沒有指定傳遞參數的方式,預設為GET;所以帳號與密碼分別是以$_GET['account']與$_GET['passwd']接收。

4、請說明 magic_quotes_gpc 對 PHP 程式的影響。

這個設定主要是解決SQL injection的問題。當magic_quotes_gpc=On時,使用者輸入的資料中若包含敏感的字元,如「'」時,它會自動將提交的表單內容加上「\」。這是一種防範使用者輸入惡意代碼設定,如果完全不處理使用這輸入的資料,使用者就有可能利用SQL語法看到本來它不應該看到的資料,甚至刪除table內的資料。如果magic_quotes_gpc=Off,我自己在處理SQL injection問題時是使用mysql_real_escape_string()函數,去防範敏感的字元。

5、如何避免使用者直接輸入 HTML 或是 JavaScript ?假設客戶要求一定要讓使用者輸入 HTML ,你該如何處理?

PHP有htmlspecialchars()htmlentities()函數,能將「&」、「"」、「<」、「>」轉成HTML字串格式。如:

& =  &amp;
" = &quot;
< = &lt;
> = &gt;

經過函數的轉換,當使用者的輸入中有<IFRAME>、<SCRIPT>這些HTML標籤時,就能避免被瀏覽器當成網頁內容而執行。
然而如果客戶要求,當然就必須另寫函數去處理,擋掉可能會被用來當作惡意攻擊的HTML標籤;google剛好找到一篇國外的文章:PHP XSS (cross site scripting) filter function。非常實用,利用此函數就能防止XSS的發生。

6、請說明 Session 的基本運作方式以及相關注意事項,並利用 PHP 的 Session 功能來完成上面的登入功能。帳號及密碼列表如下:
帳號 密碼
abc 123!@#
def 456L:'

能力不足。

7、請說明 Cookie 的基本運作方式以及相關注意事項,並利用 PHP 的 Cookie 功能來完成上面的保持登入功能。

能力不足。

8、請簡單設計一個 PHP 類別,讓以下程式能夠運作: (提示:要使用 SPL)

能力不足。

9、請修改你剛剛所建立的類別,讓 addValue 函式在參數不為數字時,能丟出一個異常。然後在上面程式中加入 try...catch 敘述,使得 Test 物件在加入非數字資料時,還能繼續運作並顯示錯誤。

能力不足。

10、為剛剛的類別加入註解 ,以便能夠以 PHPDoc 來產生 API 文件。

能力不足。

MySQL篇:

1、何謂 Schema ?

應該就是指資料庫的意思...

2、請建立一個名為 guestbook 的資料庫,並建立一個名為 messages 的資料表。其中 messages 資料表要包含自動編號 (id) 、標題 (title) 、內容 (content) 、作者 (author) 及建立日期 (createDateTime) 等欄位,欄位型態請以符合效率及易維護性為考量來設計。

建立資料庫:
CREATE DATABASE guestbook;

建立資料表:
CREATE TABLE messages (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
tite VARCHAR( 50 ) NULL ,
content TEXT NULL ,
author VARCHAR( 30 ) NULL ,
createDateTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE = MYISAM ;

3、請試著用 MySQL 的命令列模式,來操作對 messages 資料表的新增、列表、修改及刪除等功能。

登入MySQL:
mysql -u*** -p***

使用資料庫:
use guestbook;

新增:
INSERT INTO messages (tite, content, author)
VALUES ('test', 'test_content', 'test_author');

列表:
select * from messages;

修改:
UPDATE messages SET tite = 'update', content = 'update_content', author = 'update_author'
WHERE id = 1 ;

刪除:
DELETE FROM messages WHERE id = 1;

4、請試著用 MySQL 的命令列模式匯出 guestbook 資料庫的結構與資料,並刪除 guestbook 資料庫。然後再利用匯出的備份檔案,來重建 guestbook 資料庫。

匯出資料:(剛好前一陣子有試著利用排程,定期備份資料庫
C:\>mysqldump --opt -u*** -p*** guestbook >C:\%date:~0,4%-%date:~5,2%-%date:~8,2%.sql

如此一來就會存成一個yyyy-mm-dd.sql的文字檔

刪除資料庫:
DROP DATABASE guestbook;

重建資料庫:
C:\>mysqladmin -u*** -p*** create guestbook
C:\>mysql -u*** -p*** guestbook < yyyy-mm-dd.sql

5、在 MySQL 4.1 以後,如果已存的資料其編碼為 UTF-8 ,那麼 Client 端在取出資料前,要先下何種指令來指定正確的編碼?

如果網頁是以UTF8的方式儲存,在進入資料庫以後要下:mysql_query("SET NAMES UTF8");

如果網頁是以big5的方式儲存,在進入資料庫以後要下:mysql_query("SET NAMES big5");

Posted by taikobo0 at 樂多Roodo!23:15回應(0)引用(0)新知