最近因為工作需要用到ZF,所以就來摸一下
Zend Framework是php裡頭其中一種Framework,因為它實作的是按照MVC架構做的,所以要先把MVC的概念看懂,
可以參考維基寫的MVC

建立目錄架構:
首先把網站目錄結構建成(目錄結構規劃不一定要參照這個方式):

Project/
	application/
		controllers/
		models/
		views/
			layouts/
			scripts/
	library/
	public/
		.htaccess
		index.php
		js/
		img/
		css/

另外設個DNS指到Project/public這個目錄

下載Zend Framework:
接著下載Zend Framework
他有 Full 跟 minimal 兩個版本,建議是下載Full,下載下來後解壓縮並把 /(Zend_Framework)/library/Zend整個目錄複製到你的Project/library下

建立index:
我們先在Project/public底下建立一個index.php,這支index.php的用途就是把除了靜態檔案以外,把其他的request都交給Zend Framework來處理,所以記得要把Rewrite的功能開起來。

首先在Project/public底下建一個.htaccess,填入以下內容:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
RewriteBase /

這段Rewrite的設定就是將所有目錄、檔案以外的URL都交給index.php處理,比如http://localhost/img/a.png便會直接吐內容,如果是http://localhost/foo/bar這類的就會丟給index.php處理

RewriteCond指令定義了規則生效的條件,也就是說RewriteCond要符合才會跑RewriteRule
-s:測試路徑名稱是否存在
-l:測試路徑名稱並確認是否有一個存在的符號連接
-d:測試路徑目錄是否存在

接著要設定index.php的內容:

<?php
	//設定application路徑常數
	define('APPLICATION_PATH', realpath(dirname( __FILE__ ) . '/../application/'));
	//設定環境常數(開發者)
	define('APPLICATION_ENVIRONMENT', 'development');
	
	//設定時區
	date_default_timezone_set('Asia/Taipei');
	
	//設定path,PATH_SEPARATOR是一個PHP中的常數,是用來串接路徑與路徑之間的區隔符號,因為UNIX是":"、windows是";",所以直接使用PATH_SEPARATOR才不會在移植時爛掉
	set_include_path(APPLICATION_PATH . '/../library' . PATH_SEPARATOR . get_include_path());
	
	
	require_once 'Zend/Loader/Autoloader.php';
	//呼叫Zend_Loader_Autoloader,讓Zend Framework在有load classes的能力
	$autoloader = Zend_Loader_Autoloader::getInstance();
	//這適用於函式庫中沒有自己加的namepsace,所以會使用預設讀Zend Framework中class所使用的字首Zend_跟ZendX_,讓Zend Framework內的class能夠自動被載入。
	//setFallbackAutoloader(true)是讓寫在include_path(library)中,又沒有註冊namespace的class能被載入
	$autoloader->setFallbackAutoloader(true);
	
	//呼叫前端Controller
	$frontController = Zend_Controller_Front::getInstance();
	//設定ControllerDirectory
	$frontController->setControllerDirectory(APPLICATION_PATH . '/controllers');
	//設定環境為開發者模式
	$frontController->setParam('env', APPLICATION_ENVIRONMENT);
	//執行
	$frontController->dispatch();
?>

Action Controller:
先在Project/application/controllers裡面建一個IndexController.php,在Zend Framework裡可以將request都交由front controller下的dispatcher來決定要交由哪個action controller來處理,所以controller命名的方式就是URL名稱(開頭大寫)Controller.php。例如http://DNS/foo就會去執行FooController的indexAction;http://DNS/foo/bar就是執行FooController的barAction。如果將Action命名成testOneAction,則DNS會對應成test-one

接著在IndexController.php輸入以下內容:

<?php
	class IndexController extends Zend_Controller_Action {
		public function init() {
			//當物件建立時會自動執行此函式
		}
		public function indexAction() {
			//關閉預設viewRenderer,不會去找application/views/scripts裡的template來顯示
			$this->_helper->viewRenderer->setNoRender();
			echo 'hello from Index#index';
		}
	}
?>

這時只要輸入http://DNS/就可以看到畫面顯示出hello from Index#index的字串

View Script:
剛剛上述Action的作法,只是拿來測試controller是否正常執行,事實上是不符合MVC架構的,controller是拿來運算而非拿來顯示的,顯示的部份必須由view來執行!
在Zend Framework中,action controller預設都會有一個view renderer來負責顯示的工作,他會根據controller/action的名稱去找application/views/scripts/controller_name/action.phtml來顯示,例如剛剛上述的IndexController/indexAction就會去找index/index.phtml,所以先在application/views/scripts底下建一個index目錄,index的目錄裡再建一個index.phtml
再將Project/application/controllers/IndexController.php改成以下內容:

<?php
	class IndexController extends Zend_Controller_Action {
		public function init() {
			//當物件建立時會自動執行此函式
		}
		public function indexAction() {
			//將日期儲存到Controller裡view這個物件的today變數
			$this->view->today = date('Y-m-d');
		}
	}
?>

接著輸入Project/application/views/scripts/index/index.phtml內容:

<!-- #file: Project/application/views/scripts/index/index.phtml -->
<!-- 可以使用 $this->escape() 函式針對變數做HTML escape-->
<h1>It's <?php echo $this->today; ?> today.</h1>

這樣http://DNS/就會顯示出日期了

URL Routing:
上面有提過Controller和Action的命名方式,現在來實作一下 http://DNS/foo 和 http://DNS/foo/bar

首先先在Project/application/controllers裡建一個FooController.php並輸入以下內容:

<?php
	class FooController extends Zend_Controller_Action {
		public function indexAction() {
			//關閉預設viewRenderer
			$this->_helper->viewRenderer->setNoRender();
			echo "It's Foo#index";
		}
		public function barAction(){
			//關閉預設viewRenderer
			$this->_helper->viewRenderer->setNoRender();
			echo "It's Foo#bar";
		}
	}
?>

選擇View Scripts:
上述有提到view renderer來負責顯示的工作,他會根據controller/action的名稱去找符合的phtml檔,
例如上述提到的狀況,若是改成view輸出,則應該會在Project/application/views/scripts/foo/裡多出index.phtml和bar.phtml
但Action也可以直接選擇其他的view script來顯示。

例如將Project/application/controllers裡的FooController.php改成:

<?php
	class FooController extends Zend_Controller_Action {
		public function indexAction() {
			$this->view->msg = "Foo#index";
			//把view script改成Project/application/views/scripts/foo/bar.phtml(要在所有view的變數輸入完後再做render,不然render之後才輸入的變數皆會失效)
			$this->render('bar');
		}
		public function barAction(){
			//使用原本的view scripts輸出
			$this->view->msg = "Foo#bar";
		}
	}
?>

Project/application/views/scripts/foo/bar.phtml:

<!-- #file: Project/application/views/scripts/foo/bar.phtml -->
It's <?php echo $this->msg; ?>.

使用Layout排版:
在設計網站時,都會有一些固定的版型,比如Header或sidebar大家都長一樣,只是內容不同而已,這樣的需求可以用Zend Framework中的Layout元件來完成。

首先要使用Layout元件時,需要一開始初始化時就把元件打開,並且設定layout存放的路徑,所以必須在Project/index.php中再加入一段程式碼:

<?php
	//程式碼要加在Zend_Loader_Autoloader::getInstance();之後
	
	//啟用mvc,並設定layout路徑
	Zend_Layout::startMvc(array('layoutPath' => APPLICATION_PATH . '/views/layouts'));
	
	//程式碼要加在Zend_Controller_Front::getInstance();之前
?>

接著在Project/application/views/layouts/layout.phtml

<!DOCTYPE HTML>
<html>
	<head>
		<meta charset="UTF-8">
		<title>ZF TEST</title>
	</head>
	<body>
	<div id="hd"><h1>Header</h1></div>
	<!-- $this->layout()->content就是view script的所有輸出 -->
	<div id="bd"><?php echo $this->layout()->content; ?></div>
	<div id="ft"><small>Footer</small></div>
	</body>
</html>

現在可以試試上述幾個已經完成測試的foo/bar就都有layout了

選擇不同的Layout:
跟view script一樣,Layout也是可以選擇的,當然你要在Project/application/views/layouts裡再建一個,比如建了一個test_layout.phtml,
只要在Controller.php裡的Action方法加上以下這段程式碼就可以了:

$this->_helper->layout->setLayout('test_layout');

關閉Layout:
如果開啟了Layout元件,但某些action不需要用到Layout,
一樣在Controller.php裡的Action方法加上以下這段程式碼就可以了:

$this->_helper->layout->disableLayout();

錯誤處理:
當寫程式時,發生問題是難免的事,Zend Framework吐出來的錯誤訊息有點亂,可以做個簡單的錯誤處理畫面,
先在Project/application/controllers/裡建一個ErrorController.php
在ErrorController.php:

<?php
	class ErrorController extends Zend_Controller_Action {
		public function errorAction() {
			//取得error_handler參數
			$error = $this->_getParam('error_handler');
			//取得Controller Response
			$response = $this->getResponse();
			//也可以使用view script和Layout來做好看的錯誤頁面
			//關閉預設viewRenderer
			$this->_helper->viewRenderer->setNoRender();
			//停止使用Layout
			$this->_helper->layout->disableLayout();
			switch ($error->type) {
				//找不到CONTROLLER
				case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
				//找不到ACTION
				case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
					//設定HttpResponseCode為404
					$response->setHttpResponseCode(404);
					echo "<h1>Page Not Found</h1>";
				default:
					//設定HttpResponseCode為500
					$response->setHttpResponseCode(500);
					echo "<h1>Application Error</h1>";
			}
		}
	}
?>

此時當網頁有錯誤時,便會執行ErrorController的errorAction