Zend Framework實例教程

http://www.phpeye.com/article/view/id/45

作者:張佳(譯)  來源:PHPEye.com 2006-11-07 最後更新:2006-12-19 15:42:16

最新更新:2006年3月11日

我們邀請PHP安全專家 — 最新版Zend Frame的貢獻者 — Chris Shiflett幫我們寫一篇文章介紹一下ZF的主要細節,他爽快地答應了!

這份手把手的完整教程提供了用ZF構建實例的方法,並教你如何編寫一個新聞管理系統。

我們的論壇上討論ZF和這篇教程。

Brain Bulb

Zend Framework發佈了!雖然仍處於開發初期,這個教程仍突出講解目前幾個最好的功能,並指導你完成一個簡單程序的構建。

 

Zend最早在社區裏發佈了ZF。基於同樣的想法,這個教程寫來用於展示ZF現有的功能。由於這個教程是在線發佈,我將在ZF變化時對其進行更新,以便儘可能有效。

 

要求

 

Zend Framework要求PHP5。爲了更好利用本教程的代碼,你還需要Apache網頁服務器。因爲示範程序(一個新聞管理系統)用到了mod_rewrite

 

這個教程的代碼可以自由下載,所以你可以自己試一下。你可以從Brain Buld的網站下載到代碼:http://brainbulb.com/zend-framework-tutorial.tar.gz

 

下載ZF

 

當你開始這篇教程時,你需要下載ZF的最新版本。你可以用瀏覽器手工從http://framework.zend.com/download選擇tar.gzzip文件進行下載,或者使用下列命令:

 

$ wget http://framework.zend.com/download/tgz
$ tar -xvzf ZendFramework-0.1.2.tar.gz

 

提示:Zend計劃提供自有PEAR通道簡化下載。

 

一旦你下載了預覽版,把library目錄放到方便的地方。在這個教程,我把library重命名爲lib以便有個簡潔的目錄結構:

 

app/
    views/
    controllers/
www/
    .htaccess
    index.php
lib/

 

www目錄是文檔根目錄,controllersviews目錄是以後會用到的空目錄,而lib目錄來自你下載的預覽版。

 

開始

 

我要介紹的第一個組件是Zend_Controller。從很多方面看,它爲你開發的程序提供了基礎,同時也部分決定了Zend Framework不只是個組件的集合。但是,你在用之前需要將所有的得到的請求都放到一個簡單的PHP腳本。本教程用的是mod_rewrite

 

mod_rewrite自身是一種藝術,但幸運的是,這個特殊的任務特別簡單。如果你對mod_rewrite或Apache的一般配置不熟悉,在文檔根目錄下創建一個.htaccess文件,並添加以下內容:

 

RewriteEngine on
RewriteRule !/.(js|ico|gif|jpg|png|css)$ index.php

 

提示: Zend_Controller的一個TODO項目就是取消對mod_rewrite的依賴。爲了提供一個預覽版的範例,本教程用了mod_rewrite

 

如果你直接把這些內容添加到httpd.conf,你必須重啓網頁服務器。但如果你用.htaccess文件,則什麼都不必做。你可以放一些具體的文本到index.php並訪問任意路徑如/foo/bar做一下快速測試。如你的域名爲example.org,則訪問http://example.org/foo/bar

 

你還要設置ZF庫的路徑到include_path。你可以在php.ini設置,也可以直接在你的.htaccess文件放下列內容:

 

php_value include_path "/path/to/lib"

 

Zend

 

Zend類包含了一些經常使用的靜態方法的集合。下面是唯一一個你要手工添加的類:

 

<?php

include 'Zend.php';

?>

 

一旦你包含了Zend.php,你就已經包含了Zend類的所有的類方法。用loadClass()就可以簡單地加載其它類。例如,加載Zend_Controller_Front類:

 

<?php

include 'Zend.php';

Zend::loadClass('Zend_Controller_Front');

?>

 

include_path能理解loadclass()及ZF的組織和目錄結構。我用它加載所有其它類。   

Zend_Controller

 

使用這個controller非常直觀。事實上,我寫本教程時並沒有用到它豐富的文檔。

 

提示:文檔目前已經可以在http://framework.zend.com/manual/zend.controller.html看到。

 

我一開始是用一個叫Zend_Controller_Front的front controller。爲了理解它是怎麼工作的,請把下列代碼放在你的index.php文件:

 

<?php

include 'Zend.php';

Zend::loadClass('Zend_Controller_Front');

$controller Zend_Controller_Front::getInstance();
$controller->setControllerDirectory('/path/to/controllers');
$controller->dispatch();

?>

 

如果你更喜歡對象鏈結,可以用以下代碼代替:

 

<?php

include 'Zend.php';

Zend::loadClass('Zend_Controller_Front');

$controller Zend_Controller_Front::getInstance()
              ->
setControllerDirectory('/path/to/controllers')
              ->
dispatch();

?>

 

現在如果你訪問/foo/bar,會有錯誤發生。沒錯!它讓你知道發生了什麼事。主要的問題是找不到IndexController.php文件。

 

在你創建這個文件之前,應先理解一下ZF想讓你怎樣組織這些事情。ZF把訪問請求給拆分開來。假如訪問的是/foo/bar,則foo是controller,而bar是action。它們的默認值都是index.

 

如果foo是controller,ZF就會去查找controllers目錄下的FooController.php文件。因爲這個文件不存在,ZF就退回到IndexController.php。結果都沒有找到,就報錯了。

 

接下來,在controllers目錄創建IndexController.php文件(可以用setControllerDirectory()設置):

 

<?php

Zend
::loadClass('Zend_Controller_Action');

class 
IndexController extends Zend_Controller_Action 
{
    public function 
indexAction()
    {
        echo 
'IndexController::indexAction()';
    }
}

?>

 

就如剛纔說明的,IndexController類處理來自index controller或controller不存在的請求。indexAction()方法處理action爲index的訪問。要記住的是index是controller和action的默認值。如果你訪問//index/index/indexindexAction()方法就會被執行。 (最後面的斜槓並不會改變這個行爲。) 而訪問其他任何資源只會導致出錯。

 

在繼續做之前,還要在IndexController加上另外一個有用的類方法。不管什麼時候訪問一個不存在的控制器,都要調用noRouteAction()類方法。例如,在FooController.php不存在的條件下,訪問/foo/bar就會執行noRouteAction()。但是訪問/index/foo仍會出錯,因爲foo是action,而不是controller.

 

noRouteAction()添加到IndexController.php:

 

<?php

Zend
::loadClass('Zend_Controller_Action');

class 
IndexController extends Zend_Controller_Action 
{
    public function 
indexAction()
    {
        echo 
'IndexController::indexAction()';
    }

    public function 
noRouteAction()
    {
        
$this->_redirect('/');
    }
}

?>

 

例子中使用$this->_redirect('/')來描述執行noRouteAction()時,可能發生的行爲。這會將對不存在controllers的訪問重定向到根文檔(首頁)。

 

現在創建FooController.php

 

<?php

Zend
::loadClass('Zend_Controller_Action');

class 
FooController extends Zend_Controller_Action 
{
    public function 
indexAction()
    {
        echo 
'FooController::indexAction()';
    }

    public function 
barAction()
    {
        echo 
'FooController::barAction()';
    }
}

?>

 

如果你再次訪問/foo/bar,你會發現執行了barAction(),因爲bar是action。現在你不只支持了友好的URL,還可以只用幾行代碼就做得這麼有條理。酷吧!

 

你也可以創建一個__call()類方法來處理像/foo/baz這樣未定義的action。

 

<?php

Zend
::loadClass('Zend_Controller_Action');

class 
FooController extends Zend_Controller_Action 
{
    public function 
indexAction()
    {
        echo 
'FooController::indexAction()';
    }

    public function 
barAction()
    {
        echo 
'FooController::barAction()';
    }

    public function 
__call($action$arguments)
    {
        echo 
'FooController:__call()';
    }
}

?>

 

現在你只要幾行代碼就可以很好地處理用戶的訪問了,準備好繼續。 

 

Zend_View

 

Zend_View是一個用來幫助你組織好你的view邏輯的類。這對於模板-系統是不可知的,爲了簡單起見,本教程不使用模板。如果你喜歡的話,不妨用一下。

 

記住,現在所有的訪問都是由front controller進行處理。因此應用框架已經存在了,另外也必須遵守它。爲了展示Zend_View的一個基本應用,將IndexController.php修改如下:

 

<?php

Zend
::loadClass('Zend_Controller_Action');
Zend::loadClass('Zend_View');

class 
IndexController extends Zend_Controller_Action 
{
    public function 
indexAction()
    {
        
$view = new Zend_View();
        
$view->setScriptPath('/path/to/views');
        echo 
$view->render('example.php');
    }

    public function 
noRouteAction()
    {
        
$this->_redirect('/');
    }
}

?>

 

views目錄創建example.php文件:

 

<html>
<head>
    <title>This Is an Example</title>
</head>
<body>
    <p>This is an example.</p>
</body>
</html>

 

現在,如果你訪問自己網站的根資源,你會看到example.php的內容。這仍沒什麼用,但你要清楚你要在以一種結構和組織非常清楚的方式在開發網絡應用。

 

爲了讓Zend_View的應用更清楚一點,,修改你的模板(example.php)包含以下內容:

 

<html>
<head>
    <title><?php echo $this->escape($this->title); ?></title>
</head>
<body>
    <?php echo $this->escape($this->body); ?>
</body>
</html>

 

現在已經添加了兩個功能。$this->escape()類方法用於所有的輸出。即使你自己創建輸出,就像這個例子一樣。避開所有輸出也是一個很好的習慣,它可以在默認情況下幫助你防止跨站腳本攻擊(XSS)。

 

$this->title$this->body屬性用來展示動態數據。這些也可以在controller中定義,所以我們修改IndexController.php以指定它們:

 

<?php

Zend
::loadClass('Zend_Controller_Action');
Zend::loadClass('Zend_View');

class 
IndexController extends Zend_Controller_Action 
{
    public function 
indexAction()
    {
        
$view = new Zend_View();
        
$view->setScriptPath('/path/to/views');
        
$view->title 'Dynamic Title';
        
$view->body 'This is a dynamic body.';
        echo 
$view->render('example.php');
    }

    public function 
noRouteAction()
    {
        
$this->_redirect('/');
    }
}

?>

 

現在你再次訪問根目錄,應該就可以看到模板所使用的這些值了。因爲你在模板中使用的$this就是在Zend_View範圍內所執行的實例。

 

要記住example.php只是一個普通的PHP腳本,所以你完全可以做你想做的。只是應努力只在要求顯示數據時才使用模板。你的controller (controller分發的模塊)應處理你全部的業務邏輯。

 

在繼續之前,我想做最後一個關於Zend_View的提示。在controller的每個類方法內初始化$view對象需要額外輸入一些內容,而我們的主要目標是讓快速開發網絡應用更簡單。如果所有模板都放在一個目錄下,是否要在每個例子中都調用setScriptPath()也存在爭議。

 

幸運的是,Zend類包含了一個寄存器來幫助減少工作量。你可以用register()方法把你的$view對象存儲在寄存器:

 

<?php

Zend
::register('view'$view);

?>

 

registry()方法進行檢索:

 

<?php

$view 
Zend::registry('view');

?>

 

基於這點,本教程使用寄存器。  

 

Zend_InputFilter

 

本教程討論的最後一個組件是Zend_InputFilter。這個類提供了一種簡單而有效的輸入過濾方法。你可以通過提供一組待過濾數據來進行初始化。

 

<?php

$filterPost 
= new Zend_InputFilter($_POST);

?>

 

這會將($_POST)設置爲NULL,所以就不能直接進入了。Zend_InputFilter提供了一個簡單、集中的根據特定規則過濾數據的類方法集。例如,你可以用getAlpha()來獲取$_POST['name']中的字母:

 

<?php

/* $_POST['name'] = 'John123Doe'; */

$filterPost = new Zend_InputFilter($_POST);

/* $_POST = NULL; */

$alphaName $filterPost->getAlpha('name');

/* $alphaName = 'JohnDoe'; */

?>

 

每一個類方法的參數都是對應要過濾的元素的關鍵詞。對象(例子中的$filterPost)可以保護數據不被篡改,並能更好地控制對數據的操作及一致性。因此,當你操縱輸入數據,應始終使用Zend_InputFilter

 

提示:Zend_Filter提供與Zend_InputFilter方法一樣的靜態方法。

 

構建新聞管理系統

 

雖然預覽版提供了許多組件(甚至許多已經被開發),我們已經討論了構建一個簡單程序所需要的全部組件。在這裏,你會對ZF的基本結構和設計有更清楚的理解。

 

每個人開發的程序都會有所不同,而Zend Framework試圖包容這些差異。同樣,這個教程是根據我的喜好寫的,請根據自己的偏好自行調整。

 

當我開發程序時,我會先做界面。這並不意味着我把時間都花在標籤、樣式表和圖片上,而是我從一個用戶的角度去考慮問題。因此我把程序看成是頁面的集合,每一頁都是一個獨立的網址。這個新聞系統就是由以下網址組成的:

 

/
/add/news
/add/comment
/admin
/admin/approve
/view/{id}

 

你可以直接把這些網址和controller聯繫起來。IndexController列出新聞,AddController添加新聞和評論,AdminController處理一些如批准新聞之類的管理,ViewController特定新聞和對應評論的顯示。

 

如果你的FooController.php還在,把它刪除。修改IndexController.php,爲業務邏輯以添加相應的action和一些註釋:

 

<?php

Zend
::loadClass('Zend_Controller_Action');

class 
IndexController extends Zend_Controller_Action 
{
    public function 
indexAction()
    {
        
/* List the news. */
    
}

    public function 
noRouteAction()
    {
        
$this->_redirect('/');
    }
}

?>

 

接下來,創建AddController.php文件:

 

<?php

Zend
::loadClass('Zend_Controller_Action');

class 
AddController extends Zend_Controller_Action
{
    function 
indexAction()
    {
        
$this->_redirect('/');
    }

    function 
commentAction()
    {
        
/* Add a comment. */
    
}

    function 
newsAction()
    {
        
/* Add news. */
    
}

    function 
__call($action$arguments)
    {
        
$this->_redirect('/');
    }
}

?>

 

記住AddControllerindexAction()方法不能調用。當訪問/add時會執行這個類方法。因爲用戶可以手工訪問這個網址,這是有可能的,所以你要把用戶重定向到主頁、顯示錯誤或你認爲合適的行爲。

 

接下來,創建AdminController.php文件:

 

<?php

Zend
::loadClass('Zend_Controller_Action');

class 
AdminController extends Zend_Controller_Action
{
    function 
indexAction()
    {
        
/* Display admin interface. */
    
}

    function 
approveAction()
    {
        
/* Approve news. */
    
}

    function 
__call($action$arguments)
    {
        
$this->_redirect('/');
    }
}

?>

 

最後,創建ViewController.php文件:

 

<?php

Zend
::loadClass('Zend_Controller_Action');

class 
ViewController extends Zend_Controller_Action
{
    function 
indexAction()
    {
        
$this->_redirect('/');
    }

    function 
__call($id$arguments)
    {
        
/* Display news and comments for $id. */
    
}
}

?>

 

AddController一樣,index()方法不能調用,所以你可以使用你認爲合適的action。ViewController和其它的有點不同,因爲你不知道什麼纔是有效的action。爲了支持像/view/23這樣的網址,你要使用__call()來支持動態action。

  

數據庫操作

 

因爲Zend Framework的數據庫組件還不穩定,而我希望這個演示可以做得簡單一點。我使用了一個簡單的類,用SQLite進行新聞條目和評論的存儲和查詢。

 

<?php

class Database
{
    private 
$_db;

    public function 
__construct($filename)
    {
        
$this->_db = new SQLiteDatabase($filename);
    }

    public function 
addComment($name$comment$newsId)
    {
        
$name sqlite_escape_string($name);
        
$comment sqlite_escape_string($comment);
        
$newsId sqlite_escape_string($newsId);

        
$sql "INSERT
                INTO   comments (name, comment, newsId)
                VALUES ('$name', '$comment', '$newsId')"
;

        return 
$this->_db->query($sql);
    }

    public function 
addNews($title$content)
    {
        
$title sqlite_escape_string($title);
        
$content sqlite_escape_string($content);

        
$sql "INSERT
                INTO   news (title, content)
                VALUES ('$title', '$content')"
;

        return 
$this->_db->query($sql);
    }

    public function 
approveNews($ids)
    {
        foreach (
$ids as $id) {
            
$id sqlite_escape_string($id);

            
$sql "UPDATE news
                    SET    approval = 'T'
                    WHERE  id = '$id'"
;

            if (!
$this->_db->query($sql)) {
                return 
FALSE;
            }
        }

        return 
TRUE;
    }

    public function 
getComments($newsId)
    {
        
$newsId sqlite_escape_string($newsId);

        
$sql "SELECT name, comment
                FROM   comments
                WHERE  newsId = '$newsId'"
;

        if (
$result $this->_db->query($sql)) {
            return 
$result->fetchAll();
        }

        return 
FALSE;
    }

    public function 
getNews($id 'ALL')
    {
        
$id sqlite_escape_string($id);

        switch (
$id) {
            case 
'ALL':
                
$sql "SELECT id,
                               title
                        FROM   news
                        WHERE  approval = 'T'"
;
                break;
            case 
'NEW':
                
$sql "SELECT *
                        FROM   news
                        WHERE  approval != 'T'"
;
                break;
            default:
                
$sql "SELECT *
                        FROM   news
                        WHERE  id = '$id'"
;
                break;
        }

        if (
$result $this->_db->query($sql)) {
            if (
$result->numRows() != 1) {
                return 
$result->fetchAll();
            } else {
                return 
$result->fetch();
            }
        }

        return 
FALSE;
    }
}

?>

 

(你可以用自己的解決方案隨意替換這個類。這裏只是爲你提供一個完整示例的介紹,並非建議要這麼實現。)

 

這個類的構造器需要SQLite數據庫的完整路徑和文件名,你必須自己進行創建。

 

<?php

$db 
= new SQLiteDatabase('/path/to/db.sqlite');

$db->query("CREATE TABLE news (
                id       INTEGER PRIMARY KEY,
                title    VARCHAR(255),
                content  TEXT,
                approval CHAR(1) DEFAULT 'F'
            )"
);

$db->query("CREATE TABLE comments (
                id       INTEGER PRIMARY KEY,
                name     VARCHAR(255),
                comment  TEXT,
                newsId   INTEGER
            )"
);

?>

 

你只需要做一次,以後直接給出Database類構造器的完整路徑和文件名即可:

 

<?php

$db 
= new Database('/path/to/db.sqlite');

?>

  

整合

 

爲了進行整合,在lib目錄下創建Database.phploadClass()就可以找到它。你的index.php文件現在就會初始化$view$db並存儲到寄存器。你也可以創建__autoload()函數自動加載你所需要的類:

 

<?php

include 'Zend.php';

function 
__autoload($class)
{
    
Zend::loadClass($class);
}

$db = new Database('/path/to/db.sqlite');
Zend::register('db'$db);

$view = new Zend_View;
$view->setScriptPath('/path/to/views');
Zend::register('view'$view);

$controller Zend_Controller_Front::getInstance()
              ->
setControllerDirectory('/path/to/controllers')
              ->
dispatch();

?>

 

接下來,在views目錄創建一些簡單的模板。index.php可以用來顯示index視圖:

 

<html>
<head>
  <title>News</title>
</head>
<body>
  <h1>News</h1>
  <?php foreach ($this->news as $entry) { ?>
  <p>
    <a href="/view/<?php echo $this->escape($entry['id']); ?>">
    <?php echo $this->escape($entry['title']); ?>
    </a>
  </p>
  <?php ?>
  <h1>Add News</h1>
  <form action="/add/news" method="POST">
  <p>Title:<br /><input type="text" name="title" /></p>
  <p>Content:<br /><textarea name="content"></textarea></p>
  <p><input type="submit" value="Add News" /></p>
  </form>
</body>
</html>

 

view.php模板可以用來顯示選定的新聞條目:

 

<html>
<head>
  <title>
    <?php echo $this->escape($this->news['title']); ?>
  </title>
</head>
<body>
  <h1>
    <?php echo $this->escape($this->news['title']); ?>
  </h1>
  <p>
    <?php echo $this->escape($this->news['content']); ?>
  </p>
  <h1>Comments</h1>
  <?php foreach ($this->comments as $comment) { ?>
  <p>
    <?php echo $this->escape($comment['name']); ?> writes:
  </p>
  <blockquote>
    <?php echo $this->escape($comment['comment']); ?>
  </blockquote>
  <?php ?>
  <h1>Add a Comment</h1>
  <form action="/add/comment" method="POST">
  <input type="hidden" name="newsId" 
    value="<?php echo $this->escape($this->id); ?>" />
  <p>Name:<br /><input type="text" name="name" /></p>
  <p>Comment:<br /><textarea name="comment"></textarea></p>
  <p><input type="submit" value="Add Comment" /></p>
  </form>
</body>
</html>

 

最後,admin.php模板可以用來批准新聞條目:

 

<html>
<head>
  <title>News Admin</title>
</head>
<body>
  <form action="/admin/approve" method="POST">
  <?php foreach ($this->news as $entry) { ?>
  <p>
    <input type="checkbox" name="ids[]"
    value="<?php echo $this->escape($entry['id']); ?>" />
    <?php echo $this->escape($entry['title']); ?>
    <?php echo $this->escape($entry['content']); ?>
  </p>
  <?php ?>
  <p>
    Password:<br /><input type="password" name="password" />
  </p>
  <p><input type="submit" value="Approve" /></p>
  </form>
</body>
</html>

 

提示:爲了保持簡單,這個表單用密碼作爲驗證機制。

 

使用到模板的地方,你只需要把註釋替換成幾行代碼。如IndexController.php就變成下面這樣:

 

<?php

class IndexController extends Zend_Controller_Action 
{
    public function 
indexAction()
    {
        
/* List the news. */
        
$db Zend::registry('db');
        
$view Zend::registry('view');
        
$view->news $db->getNews();
        echo 
$view->render('index.php');
    }

    public function 
noRouteAction()
    {
        
$this->_redirect('/');
    }
}

?>

 

因爲條理比較清楚,這個程序首頁的整個業務邏輯只有四行代碼。AddController.php更復雜一點,它需要更多的代碼:

 

<?php

class AddController extends Zend_Controller_Action
{
    function 
indexAction()
    {
        
$this->_redirect('/');
    }

    function 
commentAction()
    {
        
/* Add a comment. */
        
$filterPost = new Zend_InputFilter($_POST);
        
$db Zend::registry('db');
        
$name $filterPost->getAlpha('name');
        
$comment $filterPost->noTags('comment');
        
$newsId $filterPost->getDigits('newsId');
        
$db->addComment($name$comment$newsId);
        
$this->_redirect("/view/$newsId");
    }

    function 
newsAction()
    {
        
/* Add news. */
        
$filterPost = new Zend_InputFilter($_POST);
        
$db Zend::registry('db');
        
$title $filterPost->noTags('title');
        
$content $filterPost->noTags('content');
        
$db->addNews($title$content);
        
$this->_redirect('/');
    }

    function 
__call($action$arguments)
    {
        
$this->_redirect('/');
    }
}

?>

 

因爲用戶在提交表單後被重定向,這個controller不需要視圖。

 

AdminController.php,你要處理顯示管理界面和批准新聞兩個action:

 

<?php

class AdminController extends Zend_Controller_Action
{
    function 
indexAction()
    {
        
/* Display admin interface. */
        
$db Zend::registry('db');
        
$view Zend::registry('view');
        
$view->news $db->getNews('NEW');
        echo 
$view->render('admin.php');
    }

    function 
approveAction()
    {
        
/* Approve news. */
        
$filterPost = new Zend_InputFilter($_POST);
        
$db Zend::registry('db');
        if (
$filterPost->getRaw('password') == 'mypass') {
            
$db->approveNews($filterPost->getRaw('ids'));
            
$this->_redirect('/');
        } else {
            echo 
'The password is incorrect.';
        }
    }

    function 
__call($action$arguments)
    {
        
$this->_redirect('/');
    }
}

?>

 

最後是ViewController.php

 

<?php

class ViewController extends Zend_Controller_Action
{
    function 
indexAction()
    {
        
$this->_redirect('/');
    }

    function 
__call($id$arguments)
    {
        
/* Display news and comments for $id. */
        
$id Zend_Filter::getDigits($id);
        
$db Zend::registry('db');
        
$view Zend::registry('view');
        
$view->news $db->getNews($id);
        
$view->comments $db->getComments($id);
        
$view->id $id;
        echo 
$view->render('view.php');
    }
}

?>

 

雖然很簡單,但我們還是提供了一個功能較全的新聞和評論程序。最好的地方是由於有較好的設計,增加功能變得很簡單。而且隨着Zend Framework越來越成熟,只會變得更好。

 

更多信息

 

這個教程只是討論了ZF表面的一些功能,但現在也有一些其它的資源可供參考。在http://framework.zend.com/manual/有手冊可以查詢,Rob Allen在http://akrabat.com/zend-framework/介紹了一些他使用Zend Framework的經驗,而Richard Thomas也在http://www.cyberlot.net/zendframenotes提供了一些有用的筆記。如果你有自己的想法,可以訪問Zend Framework的新論壇:http://www.phparch.com/discuss/index.php/f/289//

 

結束語

 

要對預覽版進行評價是很容易的事,我在寫這個教程時也遇到很多困難。總的來說,我想Zend Framework顯示了承諾,加入的每個人都是想繼續完善它。

 

提示有什麼評論、想法或問題,請訪問Zend Framework的新論壇:http://www.phparch.com/discuss/index.php/f/289//

 

關於作者—Chris Shiflett是Brain Bulb的負責人,而Brain Bulb是一家專業的PHP開發和安全方面的顧問公司。Chris的blog是http://shiflett.org
 
發佈了18 篇原創文章 · 獲贊 0 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章