This article describes the knowledge points related to getting started with Zend Framework development. Share it with everyone for your reference, the details are as follows:
Zend Framework has been released! Although still in the early stages of development, this tutorial highlights some of the best features available and guides you through building a simple program.
Zend was the first to release ZF in the community. Based on the same idea, this tutorial was written to demonstrate the existing capabilities of ZF. Since this tutorial is posted online, I will update it as ZF changes so that it is as efficient as possible.
Requirements
Zend Framework requires PHP5. In order to make best use of the code in this tutorial, you will also need the Apache web server. Because the demonstration program (a news management system) uses mod_rewrite.
The code for this tutorial is freely downloadable, so you can try it yourself. You can download the code from Brain Buld's website: http://brainbulb.com/zend-framework-tutorial.tar.gz.
Download ZF
When you start this tutorial, you need to download the latest version of ZF. You can use a browser to manually select the tar.gz or zip file to download from http://framework.zend.com/download, or use the following command:
$ wget http://framework.zend.com/download/tgz
$ tar -xvzf ZendFramework-0.1.2.tar.gz
Tip: Zend plans to provide its own PEAR channel to simplify download.
Once you download the preview version, put the library directory somewhere convenient. In this tutorial, I renamed the library to lib to have a concise directory structure:
app/
views/
controllers/
www/
.htaccess
The index.php
lib/
www directory is the document root directory, the controllers and views directories are empty directories that will be used later, and the lib directory comes from the preview version you downloaded.
Start
The first component I want to introduce is Zend_Controller. In many ways, it provides the foundation for the programs you develop, and it also partially determines that Zend Framework is more than just a collection of components. However, you need to put all the obtained requests into a simple PHP script before using it. This tutorial uses mod_rewrite.
Using mod_rewrite is an art in itself, but fortunately, this particular task is surprisingly easy. If you are unfamiliar with mod_rewrite or Apache configuration in general, create a .htaccess file in the document root and add the following content:
RewriteEngine on
RewriteRule !/.(js|ico|gif|jpg|png|css)$ index.php
Tip: Zend_Controller One TODO item is to remove the dependency on mod_rewrite. To provide a preview of the example, this tutorial uses mod_rewrite.
If you add these contents directly to httpd.conf, you must restart the web server. But if you use a .htaccess file, you don't have to do anything. You can do a quick test by putting some specific text into index.php and accessing any path such as /foo/bar. If your domain name is example.org, visit http://example.org/foo/bar.
You also need to set the path of the ZF library to include_path. You can set it in php.ini, or you can put the following content directly in your .htaccess file:
php_value include_path "/path/to/lib"
##Zend
The Zend class contains A collection of frequently used static methods. Here is the only class you have to add manually:
<?php
include 'Zend.php';
?>
Once you include Zend.php, you have included all the class methods of the Zend class . You can simply load other classes using loadClass(). For example, load the Zend_Controller_Front class:
<?php
include 'Zend.php';
Zend::loadClass('Zend_Controller_Front');
?>
include_path can understand the organization and directory structure of loadclass() and ZF. I use it to load all other classes.
Zend_Controller
Using this controller is very intuitive. In fact, I didn't use its extensive documentation when writing this tutorial.
Tips: The documentation can now be seen at http://framework.zend.com/manual/zend.controller.html.
I initially used a front controller called Zend_Controller_Front. To understand how it works, place the following code in your index.php file:
<?php
include 'Zend.php';
Zend::loadClass('Zend_Controller_Front');
$controller = Zend_Controller_Front::getInstance();
$controller->setControllerDirectory('/path/to/controllers');
$controller->dispatch();
?>
If you prefer object links, you can use Replace the following code:
<?php
include 'Zend.php';
Zend::loadClass('Zend_Controller_Front');
$controller = Zend_Controller_Front::getInstance()
->setControllerDirectory('/path/to/controllers')
->dispatch();
?>
Now if you access /foo/bar, an error will occur. That's right! It lets you know what's going on. The main problem is that the IndexController.php file cannot be found.
Before you create this file, you should first understand how the government wants you to organize these things. ZF splits the access requests. If you are accessing /foo/bar, foo is the controller and bar is the action. Their default values ??are all index.
If foo is a controller, ZF will search for the FooController.php file in the controllers directory. Because this file does not exist, ZF falls back to IndexController.php. No results were found, so an error was reported.
Next, create the IndexController.php file in the controllers directory (can be set with 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/index,indexAction()方法就會被執(zhí)行。 (最后面的斜杠并不會改變這個行為。) 而訪問其他任何資源只會導致出錯。
在繼續(xù)做之前,還要在IndexController加上另外一個有用的類方法。不管什么時候訪問一個不存在的控制器,都要調(diào)用noRouteAction()類方法。例如,在FooController.php不存在的條件下,訪問/foo/bar就會執(zhí)行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('/')來描述執(zhí)行noRouteAction()時,可能發(fā)生的行為。這會將對不存在controllers的訪問重定向到根文檔(首頁)。
現(xiàn)在創(chuàng)建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,你會發(fā)現(xiàn)執(zhí)行了barAction(),因為bar是action?,F(xiàn)在你不只支持了友好的URL,還可以只用幾行代碼就做得這么有條理。酷吧!
你也可以創(chuàng)建一個__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()';
}
}
?>
現(xiàn)在你只要幾行代碼就可以很好地處理用戶的訪問了,準備好繼續(xù)。
Zend_View
Zend_View是一個用來幫助你組織好你的view邏輯的類。這對于模板-系統(tǒng)是不可知的,為了簡單起見,本教程不使用模板。如果你喜歡的話,不妨用一下。
記住,現(xiàn)在所有的訪問都是由front controller進行處理。因此應(yīng)用框架已經(jīng)存在了,另外也必須遵守它。為了展示Zend_View的一個基本應(yīng)用,將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目錄創(chuàng)建example.php文件:
<html>
<head>
<title>This Is an Example</title>
</head>
<body>
<p>This is an example.</p>
</body>
</html>
現(xiàn)在,如果你訪問自己網(wǎng)站的根資源,你會看到example.php的內(nèi)容。這仍沒什么用,但你要清楚你要在以一種結(jié)構(gòu)和組織非常清楚的方式在開發(fā)網(wǎng)絡(luò)應(yīng)用。
為了讓Zend_View的應(yīng)用更清楚一點,,修改你的模板(example.php)包含以下內(nèi)容:
<html>
<head>
<title><?php echo $this->escape($this->title); ?></title>
</head>
<body>
<?php echo $this->escape($this->body); ?>
</body>
</html>
現(xiàn)在已經(jīng)添加了兩個功能。$this->escape()類方法用于所有的輸出。即使你自己創(chuàng)建輸出,就像這個例子一樣。避開所有輸出也是一個很好的習慣,它可以在默認情況下幫助你防止跨站腳本攻擊(XSS)。
$this->title和$this->body屬性用來展示動態(tài)數(shù)據(jù)。這些也可以在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('/');
}
}
?>
現(xiàn)在你再次訪問根目錄,應(yīng)該就可以看到模板所使用的這些值了。因為你在模板中使用的$this就是在Zend_View范圍內(nèi)所執(zhí)行的實例。
要記住example.php只是一個普通的PHP腳本,所以你完全可以做你想做的。只是應(yīng)努力只在要求顯示數(shù)據(jù)時才使用模板。你的controller (controller分發(fā)的模塊)應(yīng)處理你全部的業(yè)務(wù)邏輯。
在繼續(xù)之前,我想做最后一個關(guān)于Zend_View的提示。在controller的每個類方法內(nèi)初始化$view對象需要額外輸入一些內(nèi)容,而我們的主要目標是讓快速開發(fā)網(wǎng)絡(luò)應(yīng)用更簡單。如果所有模板都放在一個目錄下,是否要在每個例子中都調(diào)用setScriptPath()也存在爭議。
幸運的是,Zend類包含了一個寄存器來幫助減少工作量。你可以用register()方法把你的$view對象存儲在寄存器:
<?php
Zend::register('view', $view);
?>
用registry()方法進行檢索:
<?php
$view = Zend::registry('view');
?>
基于這點,本教程使用寄存器。
Zend_InputFilter
本教程討論的最后一個組件是Zend_InputFilter。這個類提供了一種簡單而有效的輸入過濾方法。你可以通過提供一組待過濾數(shù)據(jù)來進行初始化。
<?php
$filterPost = new Zend_InputFilter($_POST);
?>
這會將($_POST)設(shè)置為NULL,所以就不能直接進入了。Zend_InputFilter提供了一個簡單、集中的根據(jù)特定規(guī)則過濾數(shù)據(jù)的類方法集。例如,你可以用getAlpha()來獲取$_POST['name']中的字母:
<?php
/* $_POST['name'] = 'John123Doe'; */
$filterPost = new Zend_InputFilter($_POST);
/* $_POST = NULL; */
$alphaName = $filterPost->getAlpha('name');
/* $alphaName = 'JohnDoe'; */
?>
每一個類方法的參數(shù)都是對應(yīng)要過濾的元素的關(guān)鍵詞。對象(例子中的$filterPost)可以保護數(shù)據(jù)不被篡改,并能更好地控制對數(shù)據(jù)的操作及一致性。因此,當你操縱輸入數(shù)據(jù),應(yīng)始終使用Zend_InputFilter。
提示:Zend_Filter提供與Zend_InputFilter方法一樣的靜態(tài)方法。
構(gòu)建新聞管理系統(tǒng)
雖然預覽版提供了許多組件(甚至許多已經(jīng)被開發(fā)),我們已經(jīng)討論了構(gòu)建一個簡單程序所需要的全部組件。在這里,你會對ZF的基本結(jié)構(gòu)和設(shè)計有更清楚的理解。
每個人開發(fā)的程序都會有所不同,而Zend Framework試圖包容這些差異。同樣,這個教程是根據(jù)我的喜好寫的,請根據(jù)自己的偏好自行調(diào)整。
當我開發(fā)程序時,我會先做界面。這并不意味著我把時間都花在標簽、樣式表和圖片上,而是我從一個用戶的角度去考慮問題。因此我把程序看成是頁面的集合,每一頁都是一個獨立的網(wǎng)址。這個新聞系統(tǒng)就是由以下網(wǎng)址組成的:
/
/add/news
/add/comment
/admin
/admin/approve
/view/{id}
你可以直接把這些網(wǎng)址和controller聯(lián)系起來。IndexController列出新聞,AddController添加新聞和評論,AdminController處理一些如批準新聞之類的管理,ViewController特定新聞和對應(yīng)評論的顯示。
如果你的FooController.php還在,把它刪除。修改IndexController.php,為業(yè)務(wù)邏輯以添加相應(yīng)的action和一些注釋:
<?php
Zend::loadClass('Zend_Controller_Action');
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
/* List the news. */
}
public function noRouteAction()
{
$this->_redirect('/');
}
}
?>
接下來,創(chuàng)建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('/');
}
}
?>
記住AddController的indexAction()方法不能調(diào)用。當訪問/add時會執(zhí)行這個類方法。因為用戶可以手工訪問這個網(wǎng)址,這是有可能的,所以你要把用戶重定向到主頁、顯示錯誤或你認為合適的行為。
接下來,創(chuàng)建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('/');
}
}
?>
最后,創(chuàng)建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()方法不能調(diào)用,所以你可以使用你認為合適的action。ViewController和其它的有點不同,因為你不知道什么才是有效的action。為了支持像/view/23這樣的網(wǎng)址,你要使用__call()來支持動態(tài)action。
數(shù)據(jù)庫操作
因為Zend Framework的數(shù)據(jù)庫組件還不穩(wěn)定,而我希望這個演示可以做得簡單一點。我使用了一個簡單的類,用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;
}
}
?>
(你可以用自己的解決方案隨意替換這個類。這里只是為你提供一個完整示例的介紹,并非建議要這么實現(xiàn)。)
這個類的構(gòu)造器需要SQLite數(shù)據(jù)庫的完整路徑和文件名,你必須自己進行創(chuàng)建。
<?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類構(gòu)造器的完整路徑和文件名即可:
<?php
$db = new Database('/path/to/db.sqlite');
?>
整合
為了進行整合,在lib目錄下創(chuàng)建Database.php,loadClass()就可以找到它。你的index.php文件現(xiàn)在就會初始化$view和$db并存儲到寄存器。你也可以創(chuàng)建__autoload()函數(shù)自動加載你所需要的類:
<?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目錄創(chuàng)建一些簡單的模板。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('/');
}
}
?>
因為條理比較清楚,這個程序首頁的整個業(yè)務(wù)邏輯只有四行代碼。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');
}
}
?>
? ?
雖然很簡單,但我們還是提供了一個功能較全的新聞和評論程序。最好的地方是由于有較好的設(shè)計,增加功能變得很簡單。而且隨著Zend Framework越來越成熟,只會變得更好。
更多信息
這個教程只是討論了ZF表面的一些功能,但現(xiàn)在也有一些其它的資源可供參考。在http://framework.zend.com/manual/有手冊可以查詢,Rob Allen在http://akrabat.com/zend-framework/介紹了一些他使用Zend Framework的經(jīng)驗,而Richard Thomas也在http://www.cyberlot.net/zendframenotes提供了一些有用的筆記。如果你有自己的想法,可以訪問Zend Framework的新論壇:http://www.phparch.com/discuss/index.php/f/289//。
結(jié)束語
要對預覽版進行評價是很容易的事,我在寫這個教程時也遇到很多困難??偟膩碚f,我想Zend Framework顯示了承諾,加入的每個人都是想繼續(xù)完善它。
I hope this article will be helpful to everyone’s PHP programming based on the Zend Framework framework.
For more articles related to Zend Framework development introductory classic tutorials, please pay attention to the PHP Chinese website!