6月, 2010

走进Zend Framework框架编程(六):视图

本部分内容包括:视图,模板,视图帮助类等。

6.0视图介绍
在Zendframework的MVC编程模型中,视图(View)是在控制器的控制和指挥下,用来对程序逻辑进行呈现(Render)的。呈现的结果,就是我们在浏览器里看到的文字、图片、表单等各种网页元素及其字体、颜色、样式等各种效果。
Zend_View Class就是负责视图工作的类,它有效地完成了视图与程序逻辑的分离。它提供了视图帮助、输出过滤和变量转义等功能。
Zend_View还是一个模板系统,我们可以用PHP作为我们的模板语言。当然ZF还可以在View脚本里使用其他第三方的模板系统,比如PHPLib 和Smarty等。
使用Zend_View时主要分两步,首先声明一个Zend_View实例,把变量等赋给它,然后使用控制脚本,根据视图脚本呈现出结果。
例如:
控制脚本(在控制器文件的action里,例如IndexController.php中的函数dispdataAction()):
……
Function dispdataAction()
{
……
$data = ‘to view’; //数据变量
Zend_Loader::loadClass(‘Zend_View’);
$view = new Zend_View(); //实例化
$view->books = $data;  //赋值
echo $view->render(‘view.php’);
}视图脚本转义输出语句(在视图脚本文件里,本例是view.php):
<?
php echo $this->escape($this->data
?>
6.1引导文件中setParam(‘noViewRenderer’, <bool>)语句的再解释
Index文件中$fc->setParam(‘noViewRenderer’, <bool>);语句和视图的某性特性有很大关系,比如视图存放的路径等一些默认属性都受该开关属性的影响。
false 是noViewRenderer的默认值。也就是说,如果没有该语句,则认为noViewRenderer参数被设置为false。
noViewRenderer参数被设置为false:意味着控制器使用ZF默认的视图特性,比如视图文件默认必须存放于views\script \<action>\文件夹下,视图文件必须存在,而且其名字必须为<action>.phtml。.phtml是ZF使用的 PHP脚本文件,和普通PHP文件没有本质区别。同时,视图对象默认被实例化为$view变量,在控制脚本中使用$view的形式为:$this->view->…。
noViewRenderer参数被设置为true:意味着控制器不使用ZF默认的视图特性,而是通过显式的实例化Zend_view对象,通过我们自己的代码来设置视图对象的属性和方法。本部分“视图介绍”中就是显式声明和使用视图对象的例子。为了程序的灵活性和可控行,我们自然建议把 noViewRenderer 设置为 true,这也是比较通常的做法。

6.2视图对象的Options
视图对象的Options选项进一步规定了视图脚本呈现过程中的一些细节。这些选项可以通过在声明视图对象时指定,在构造函数里设置,也可以通过 set……()方法来指定。
—basePath基本路径
设置方法:setBasePath(), addBasePath()
例如目录结构:

  1. base/path/
  2.       helpers/
  3.       filters/
  4.       scripts/

复制代码

用$view->setBasePath(”base/ path/”);语句设置基本路径后,在没有$view->setScriptPath(‘……’);语句直接指定脚本路径时,就会自动在base /path/scripts/下搜索视图脚本文件,如果使用了视图助手和过滤器,就会分别自动在helpers/和 filters/文件夹下搜索。
—encoding字符编码
用来在使用htmlentities()、htmlspecialchars()或其他操作时,指定字符编码。
设置方法:setEncoding()
默认编码是ISO-8859-1 (latin1)。
—escape回调函数
用于在视图呈现时调用该函数。后边有示例。
设置方法:setEscape()
—filter过滤器
用于在视图呈现后调用过滤方法。
设置方法:setFilter(), addFilter(),
—strictVars
当视图视图发送一个未初始化的变量时,用该选项指定ZF收购给出一个提示或警告信息:
Notice: Key “xxx” does not exist ……
设置方法:strictVars(true)

6.3视图对象的一些属性存取方法
—getVars() 得到所有赋予的变量
—clearVars()清除所有赋予的变量
—getScriptPath($script) 得到给定脚本的路径
—getScriptPaths()得到所有脚本的路径
—getHelperPath($helper)得到某个指定助手类的路径
—getHelperPaths()得到所有助手的路径
—getFilterPath($filter) 得到某个指定过滤器类的路径
—getFilterPaths()得到所有过滤器的路径

6.4视图的路径:

6.4.1视图脚本的搜寻路径
如果引导文件中$fc->setParam(‘noViewRenderer’, false);
则默认指定视图文件views/scripts/[controller_name]/[action].phtml
在实际的程序代码中,为了获得可定制的灵活性,都在控制器中实际指定了视图文件的路径:
指定路径例句:$view->setScriptPath(‘…/views’);或
$view->addScriptPath(‘…/views’);
这个时候,引导文件必须有$fc->setParam(‘noViewRenderer’, true);语句,即设置noViewRenderer为true。
6.4.2视图脚本的搜寻的优先顺序
$view = new Zend_View();
$view->addtScriptPath(‘…/views1’);
$view->addScriptPath(‘…/views2’);
$view->addScriptPath(‘…/views3’);
Zend的手册是这样说的:“如果没有指定任何搜素路径,则在控制器文件下搜索视图文件。”但是通过实际环境测试,发现这时会报告错误:
“no view script directory set; unable to determine location for view script”
看起来最少需要指定一个搜索路径。
如果指定了多条搜素路径,则最后的搜索路径优先。也就是说,如果所有的搜索路径下有相同的视图文件,则最后路径下的起作用,它覆盖了前边路径下的视图文件。

6.5视图控制脚本及其变量传递
ZF的控制器是实例化和设置Zend_View的地方。在这里,我们给视图赋值并告诉它用指定的视图脚本去呈现它们。
6.5.1给视图对象赋值
示例:
$view->Variable=”……”;的形式
    function assign1Action()
    {
      $view = new Zend_View();
      $view->setScriptPath(‘views’);
      $view->strictVars(true);
      $view->a = “Hay”;
      $view->b = “Bee”;
      $view->c = “Sea”;
      $view->d;
      echo $view->render(‘tp_abc.php’);
   }示例:
$view-> assign(‘ Variable ‘, “……”);的形式
    function assign2Action()
    {
      $view = new Zend_View();
      $view->setScriptPath(‘views’);
      $view->assign(‘a’, “Hay”);
      $view->assign(‘b’, “Bee”);
      $view->assign(‘c’, “Sea”);
      echo $view->render(‘tp_abc.php’);
   }示例:数组
   function assign3Action()
    {
      $view = new Zend_View();
      $view->setScriptPath(‘views’);
      $array = array(
        ‘a’ => “Hay”,
        ‘b’ => “Bee”,
        ‘c’ => “Sea”,
      );
      $view->assign($array);
      echo $view->render(‘tp_abc.php’);
   }示例:对象
    function assign4Action()
    {
      $view = new Zend_View();
      $view->setScriptPath(‘views’);
      $obj = new StdClass;
      $obj->a = “Hay”;
      $obj->b = “Bee”;
      $obj->c = “Sea”;
      $view->assign((array) $obj);
      echo $view->render(‘tp_abc.php’);
   }示例:
    使用回调函数。
    function myhtmlentityAction()
    {
      $view = new Zend_View();
      $view->setScriptPath(‘views’);
//自定义类,在/models文件夹下的myclass.php文件中定义
      $mycls = new myClass();
//调用$mycls类的myHtmlEntity方法
      $view->setEscape(array($mycls, ‘myHtmlEntity’));
      $obj = new StdClass;
      $obj->a = “the words hay bee sea “;
      $obj->b = “this is bee”;
      $obj->c = ” this is sea”;
      $view->assign((array) $obj);
      echo $view->render(‘tp_abc.php’);
   }tp_abc.php视图脚本模板文件内容:
<?php
  echo ‘a = ‘ . $this->escape($this->a).'<br>’;
  echo ‘b = ‘ . $this->escape($this->b).'<br>’;
  echo ‘c = ‘ . $this->escape($this->c).'<br>’;   
?>myclass.php文件内容:
<?php
  class myClass
  {
    public function __construct($options = null)
    {
    }
    function myHtmlEntity($val)
    {//把所有单词首字母变为大写
      return ucwords($val);
    }
  }
?>输出结果:

a = The Words Hay Bee Sea
b = This Is Bee
c = This Is Sea

6.6视图脚本的变量转义输出(escaping output)
视图脚本得到变量以后,需要通过转义进行输出,变成页面可以显示的Html代码。
输出语句的格式:
echo $this->escape($this->variable);
$variable变量是在视图脚本里用render方法传递过来的。
一般情况下,传递的变量是通过PHP的 htmlspecialchars()函数转义的。而我们也可以实现我们自己的转义函数。请参考以上“使用回调函数”示例。

6.7视图脚本的模板系统—操作PHPLib类型的模板
模板系统进一步完美的实现了视图与程序逻辑的分离。视图脚本可以完美的操作PHPLib等类型的模板。

6.7.1PHPlib的安装和调用
为了测试下面的示例,我们必须安装PHPLib模板系统到我们的环境中。从网上下载到phplib-7.4.ZIP安装压缩包,解压到安装ZEND的 library文件夹下,就完成了安装。
为了在ZF的视图脚本里调用得到模板类文件,必须在引导文件Index.php的set_include_path部分添加PHPLib模板类库文件夹 phplib-7.4/php到搜索路径中。以下示例同时包含了Smarty模板引擎的类库文件的搜索路径:
set_include_path(‘.’ .
  PATH_SEPARATOR . ‘../library/’.
  PATH_SEPARATOR . ‘../library/phplib-7.4/php/’.
  PATH_SEPARATOR . ‘../library/Smarty-2.6.19/libs/’.
  PATH_SEPARATOR . ‘models/’.
  PATH_SEPARATOR . get_include_path()
);注意,所有路径都是以引导文件所在文件夹作为参照的。尽管视图文件里所在文件夹不是引导文件所在根目录,但在视图文件里包含PHPLib类库文件的语句include_once ‘template.inc’;仍然是以引导文件所在目录作为参照的。

6.7.2在视图文件里调用PHPLib模板
首先包含PHPLib类库文件,然后声明模板类的一个实例。使用模板类,首先需要指定一些属性,比如指定模板所在路径,指定模板文件等,然后用 set_var传递模板变量,最后用parse方法调用模板文件。PHPLib模板系统的详细用法请参考其帮助文档。
示例:[php]
<?php
  include_once ‘template.inc’;
  $tpl = new Template();
  $tpl->set_root(‘views’);
  if ($this->books)
  {
    $tpl->set_file(array(
        “booklist” => “booklist.tpl”,
        “eachbook” => “eachbook.tpl”,
    ));
    foreach ($this->books as $key => $val)
    {
      $tpl->set_var(‘author’, $this->escape($val[‘author’]));
      $tpl->set_var(‘title’, $this->escape($val[‘title’]));
      $tpl->parse(“books”, “eachbook”, true);
    }
    $tpl->pparse(“output”, “booklist”);
  }
  else
  {
    $tpl->setFile(“nobooks”, “nobooks.tpl”);
    $tpl->pparse(“output”, “nobooks”);
  }
?>[/php]booklist.tpl文件内容:
[php]<?php
  if ($this->books):
?>
  <table border=1>
  <tr>
    <th>作者</th>
    <th>书名</th>
  </tr>
  <?php
    foreach ($this->books as $key => $val):
  ?>
  <tr>
    <td><?php echo $this->escape($val[‘author’]) ?></td>
    <td><?php echo $this->escape($val[‘title’]) ?></td>
  </tr>
  <?php endforeach; ?>
  </table>
<?php
  else:
?>
  <p>There are no books to display.</p>
<?php
  endif;[/php]eachbook.tpl文件内容:
<!– eachbook.tpl –>
<tr>
  <td>{author}</td>
  <td>{title}</td>
</tr>

6.8视图脚本的模板系统—使用 Zend_View_Interface调用第三方模板引擎
我们还可以通过实现Zend_View_Interface接口,来得到一个可用的模板系统。
ZF对Zend_View_Interface接口的原始定义:

  1. /** Return the actual template engine object */
  2. public function getEngine();
  3. /* Set the path to view scripts/templates */
  4. public function setScriptPath($path);
  5. /* Set a base path to all view resources */
  6. public function setBasePath($path, $prefix = ‘Zend_View’);
  7. /* Add an additional base path to view resources */
  8. public function addBasePath($path, $prefix = ‘Zend_View’);
  9. /* Retrieve the current script paths */
  10. public function getScriptPaths();
  11. /* Overloading methods for assigning template variables as object properties */
  12. public function __set($key, $value);
  13. public function __get($key);
  14. public function __isset($key);
  15. public function __unset($key);
  16. /* Manual assignment of template variables, or ability to assign multiple
  17.   variables en masse.*/
  18. public function assign($spec, $value = null);
  19. /* Unset all assigned template variables */
  20. public function clearVars();
  21. /* Render the template named $name */
  22. public function render($name);

复制代码

使用该接口,我们可以很容易的把第三方的模板引擎,比如Smarty,包装成Zend_View兼容的模板类。

6.8.1Smarty的安装
下载Smarty软件包,解压到ZEND的library文件夹下,就完成了安装。
为了在ZF的视图脚本里调用得到模板类文件,必须在引导文件Index.php的set_include_path部分添加Smarty模板类库文件夹 Smarty-2.6.19/libs到搜索路径中,参看前面PHPlib的安装说明部分。
Smarty模板引擎需要建立template_dir和compile_dir文件夹才能工作。ZF手册里的示例因为缺少这些设置而无法运行,正确的代码片段如下:
    public function setScriptPath($path)
    {
        if (is_readable($path))
{
            $this->_smarty->template_dir = $path;
            $this->_smarty->compile_dir = $path;  //必须加语句:设置编译路径
            $this->_smarty->cache_dir = $path;    //设置缓存路径
return;
         }……
我们把对Zend_View_Interface接口的实现的类,放在models文件夹下的ZendViewSmarty.php文件中,该文件的内容如下:[php]
<?php
require_once ‘Zend/View/Interface.php’;
require_once ‘Smarty.class.php’;
class ZendViewSmarty implements Zend_View_Interface
{
    /**
     * Smarty object
     * @var Smarty
     */
    protected $_smarty;
    /**
     * Constructor
     * @param string $tmplPath
     * @param array $extraParams
     * @return void
     */
    public function __construct($tmplPath = null, $extraParams = array())
    {
        $this->_smarty = new Smarty;
        if (null !== $tmplPath) {
            $this->setScriptPath($tmplPath);
        }
        foreach ($extraParams as $key => $value) {
            $this->_smarty->$key = $value;
        }
    }
    /**
     * Return the template engine object
     * @return Smarty
     */
    public function getEngine()
    {
        return $this->_smarty;
    }
    /**
     * Set the path to the templates
     * @param string $path The directory to set as the path.
     * @return void
     */
    public function setScriptPath($path)
    {
        if (is_readable($path)) {
            $this->_smarty->template_dir = $path;
            $this->_smarty->compile_dir = $path;
            $this->_smarty->cache_dir = $path;
            return;
        }
        throw new Exception(‘Invalid path provided’);
    }
    /**
     * Retrieve the current template directory
     * @return string
     */
    public function getScriptPaths()
    {
        return array($this->_smarty->template_dir);
    }
    /**
     * Alias for setScriptPath
     * @param string $path
     * @param string $prefix Unused
     * @return void
     */
    public function setBasePath($path, $prefix = ‘Zend_View’)
    {
        return $this->setScriptPath($path);
    }
    /**
     * Alias for setScriptPath
     * @param string $path
     * @param string $prefix Unused
     * @return void
     */
    public function addBasePath($path, $prefix = ‘Zend_View’)
    {
        return $this->setScriptPath($path);
    }
    /**
     * Assign a variable to the template
     * @param string $key The variable name.
     * @param mixed $val The variable value.
     * @return void
     */
    public function __set($key, $val)
    {
        $this->_smarty->assign($key, $val);
    }
    /**
     * Retrieve an assigned variable
     * @param string $key The variable name.
     * @return mixed The variable value.
     */
    public function __get($key)
    {
        return $this->_smarty->get_template_vars($key);
    }
    /**
     * Allows testing with empty() and isset() to work
     * @param string $key
     * @return boolean
     */
    public function __isset($key)
    {
        return (null !== $this->_smarty->get_template_vars($key));
    }
    /**
     * Allows unset() on object properties to work
     * @param string $key
     * @return void
     */
    public function __unset($key)
    {
        $this->_smarty->clear_assign($key);
    }
    /**
     * Assign variables to the template
     * Allows setting a specific key to the specified value, OR passing an array
     * of key => value pairs to set en masse.
     * @see __set()
     * @param string|array $spec The assignment strategy to use (key or array of key
     * => value pairs)
     * @param mixed $value (Optional) If assigning a named variable, use this
     * as the value.
     * @return void
     */
    public function assign($spec, $value = null)
    {
        if (is_array($spec)) {
            $this->_smarty->assign($spec);
            return;
        }
        $this->_smarty->assign($spec, $value);
    }
    /**
     * Clear all assigned variables
     * Clears all variables assigned to Zend_View either via [email={@link]{@link[/email] assign()} or
     * property overloading ([email={@link]{@link[/email] __get()}/{@link __set()}).
     * @return void
     */
    public function clearVars()
    {
        $this->_smarty->clear_all_assign();
    }
    /**
     * Processes a template and returns the output.
     * @param string $name The template to process.
     * @return string The output.
     */
    public function render($name)
    {
        return $this->_smarty->fetch($name);
    }
}
?>[/php]控制脚本中对我们我的模板类的调用代码:
   function smartyAction()
   {
      $view = new ZendViewSmarty(); //实例化新的模板类
      $view->setScriptPath(‘views’); //设置模板文件路径
      $view->book = ‘Enter Zend Framework Programme’; //传递变量给模板引擎
      $view->author = ‘张庆(网眼)’;
      echo $view->render(‘bookinfo.tpl’); //视图呈现
   }我们看到,由于Smarty模板引擎的良好特性,除过实现上述接口的代码比较复杂以外,我们这里的控制代码要比应用PHPLib模板简单得多。我们的数据不必像PHPLib引擎那样,要把数据传给视图脚本,再由视图脚本声明PHPLib类,再把数据发送给模板去呈现。这里是在控制脚本里把数据直接传递给Smarty模板去呈现的。这是因为ZendViewSmarty实现的是Zend_View接口,它和Zend_View的用法是一样的。

6.9视图助手(Helper)
视图脚本里经常有一些繁杂的事情,比如格式化日期、产生表单元素等等。这些可以用助手帮我们来完成。
助手类其实是一些以Zend_View_Helper_开头的类,类名的最后一段是助手的名字,助手的名字必须是首字母大写的,该类必须至少有一个以助手名字命名的方法。助手名通常是驼峰式命名,即它不会是大写字母开头的。类名是混合大小写字格式。方法名也是驼峰式命名。
默认的助手的路径通常指向Zend/View/Helper。即使用setHelperPath()方法重新指定了路径,该路径也会保持以使默认的助手能够工作。

6.9.1ZF自带的助手
示例代码:
[php]<?php
    echo $this->form(‘frm1’, array(‘action’=>’action.php’, ‘method’=>’post’), false) .”\n”;
    echo $this->formHidden(‘id’, ‘submited’);
    $content = ‘Your Name:’ . $this->formText(‘name’, ”, array(‘size’ => 20)) .'<br>’;
    $content .= ‘Password:’ . $this->formPassword(‘pass’, ”, array(‘size’ => 20));
    echo $this->fieldset(‘flst’, $content, array(‘legend’=>’Name:’, ‘style’=>’width:200pt’)) .'<br>’;

    echo $this->formLabel(’email’, ‘Your Email:’);
    echo $this->formText(’email’, [email=]’you@example.com'[/email], array(‘size’ => 32)) .'<br>’;
    echo ‘Your Country:’;
    echo $this->formSelect(‘country’, ‘us’, null, $this->countries) .'<br>’;
    echo ‘Would you like to opt in?’;
    echo $this->formCheckbox(‘opt_in’, ‘yes’, null, array(‘yes’, ‘no’)) .'<br>’;
    echo ‘Choose them:’;
    echo $this->formMultiCheckbox(‘chkbox’, ‘A’, null, array(‘A’=>’valA’,’B’=>’valB’,’C’=>’valC’,’D’=>’valD’), ‘<br>’) .'<br>’;
    echo ‘Choose one:’;
    echo $this->formRadio(‘radio’, ‘A’, null, array(‘A’=>’valA’,’B’=>’valB’,’C’=>’valC’,’D’=>’valD’), ”) .'<br>’;
    echo $this->htmlList($this->countries) .'<br>’;
    echo $this->url(array(‘k1’=>’v1′,’k2’=>’v2′,’k3’=>’v3′)) .'<br>’;
    echo $this->formTextarea(‘ta’, ”, array(‘rows’=>’5′,’cols’=>’25’)) .'<br>’;
    echo $this->formFile(‘file’, ”, array()) .'<br>’;
    echo $this->formButton(‘btn’, ‘BUTTON’, array(‘onClick’=>”));
    echo $this->formSubmit(‘OK’, ‘OK’);
    echo $this->formReset(‘reset’, ‘Reset’);
  ?>[/php]

6.9.2动作(Action)助手
允许我们在视图脚本里执行某个控制器里的动作方法。
示例:
  <div id=”sidebar right”>
    <div class=”item”>
      <?= $this->action(‘assign1’, ‘Book’); ?>
    </div>
  </div>
6.9.3区域(Partial)助手
区域助手的基本用法是在它自己的视图范围内解析另一个模板的片段,类似视图脚本嵌套调用。
区域助手的基本用法:
示例:
booklist.php文件包含脚本片段:[php]
<?php
  if ($this->books):
?>
  <table border=1>
  <tr>
    <th>作者</th>
    <th>书名</th>
  </tr>
  <?php
    foreach ($this->books as $key => $val):
  ?>
  <tr>
    <td><?php echo $this->escape($val[‘author’]) ?></td>
    <td><?php echo $this->escape($val[‘title’]) ?></td>
  </tr>
  <?php endforeach; ?>
  </table>
<?php
  else:
?>
  <p>There are no books to display.</p>
<?php
  endif;
?>[/php]在另一个视图脚本通过Partial助手调用booklist.php文件:
<?= $this->partial(‘booklist.php’, array(
  ‘books’ => array(
      array(‘author’=>’zhangqing’,’title’=>’《book for php》’),
      array(‘author’=>’zhangking’,’title’=>’《book for JSP》’),
      array(‘author’=>’zhanghing’,’title’=>’《book for ASP.NET》’),
    )));
?>
<?php echo str_repeat(‘-‘, 60). ‘<br>’; ?>
<?php
  $model = array(
    array(‘key’ => ‘Mammal’, ‘value’ => ‘Camel’),
    array(‘key’ => ‘Bird’, ‘value’ => ‘Penguin’),
    array(‘key’ => ‘Reptile’, ‘value’ => ‘Asp’),
    array(‘key’ => ‘Fish’, ‘value’ => ‘Flounder’),
  );
?>
<dl>
  <?= $this->partialLoop(‘partialLoop.phtml’, $model) ?>
</dl>使用 PartialLoop 来解析可迭代的(Iterable)的模型
示例:
<?php
  $model = array(
    array(‘key’ => ‘Mammal’, ‘value’ => ‘Camel’),
    array(‘key’ => ‘Bird’, ‘value’ => ‘Penguin’),
    array(‘key’ => ‘Reptile’, ‘value’ => ‘Asp’),
    array(‘key’ => ‘Fish’, ‘value’ => ‘Flounder’),
  );
?>
<dl>
  <?= $this->partialLoop(‘partialLoop.phtml’, $model) ?>
</dl>
以上代码中partialLoop 调用了以下partialLoop.phtml的内容:
  <dt><?= $this->key ?></dt>
  <dd><?= $this->value ?></dd>
6.9.4占位(Placeholder)助手
示例:
[php]<?= $this->doctype(‘XHTML1_STRICT’) ?>
<?php
// setting meta keywords
$this->headMeta()->appendName(‘keywords’, ‘framework php productivity’);
$this->headMeta()->appendHttpEquiv(‘expires’, ‘Wed, 26 Feb 1997 08:21:57 GMT’)
                 ->appendHttpEquiv(‘pragma’, ‘no-cache’)
                 ->appendHttpEquiv(‘Cache-Control’, ‘no-cache’);
$this->headMeta()->appendHttpEquiv(‘Content-Type’, ‘text/html; charset=UTF-8’)
                 ->appendHttpEquiv(‘Content-Language’, ‘en-US’);
$this->headMeta()->appendHttpEquiv(‘Refresh’, ‘3;URL=http://www.some.org/some.html’);
echo $this->headMeta();
$this->headScript()->appendFile(‘/js/prototype.js’)
                   ->appendScript(‘xx.js’);
// Putting scripts in order
// place at a particular offset to ensure loaded last
$this->headScript()->offsetSetScript(100, ‘/js/myfuncs.js’);
// use scriptaculous effects (append uses next index, 101)
$this->headScript()->appendScript(‘/js/scriptaculous.js’);
// but always have base prototype script load first:
$this->headScript()->prependScript(‘/js/prototype.js’);
echo $this->headScript();
// setting links in a view script:
$this->headLink()->appendStylesheet(‘/styles/basic.css’)
                 ->headLink(array(‘rel’ => ‘favicon’, ‘href’ => ‘/img/favicon.ico’), ‘PREPEND’)
                 ->prependStylesheet(‘/styles/moz.css’, ‘screen’, true);
// rendering the links:
echo $this->headLink();
?>
<?php
  $this->placeholder(‘foo’)->setPrefix(“<ul>\n<li>”)
                           ->setSeparator(“</li><li>\n”)
                           ->setIndent(4)
                           ->setPostfix(“</li></ul>\n”);
  $this->placeholder(‘foo’)->set(“Some text for later-1”);
  $this->placeholder(‘foo’)->bar = “Some text for later-2″;
  echo $this->placeholder(‘foo’);
  $foo = $this->placeholder(‘foo’);
  echo $foo[0] .'<br>’;
  echo $foo[‘bar’] .'<br>’;
?>
<!– Default capture: append –>
<?php
  $this->placeholder(‘hoo’)->captureStart();
  foreach ($this->data as $datum)
  {
?>
  <div class=”hoo”>
    <h2><?= $datum[‘title’] ?></h2>
    <p><?= $datum[‘content’] ?></p>
  </div>
<?php
  }
  $this->placeholder(‘hoo’)->captureEnd();
?>
<?php
  echo $this->placeholder(‘hoo’)
?>
<!– Capture to key –>
<?php
  $this->placeholder(‘woo’)->captureStart(‘SET’, ‘data’);
  foreach ($this->data as $datum):
?>
  <div class=”woo”>
    <h2><?= $datum[‘title’] ?></h2>
    <p><?= $datum[‘content’] ?></p>
</div>
<?php
  endforeach;
  $this->placeholder(‘woo’)->captureEnd();
?>
<?php
  echo $this->placeholder(‘woo’)->data;
?>[/php]
6.9.4自定义助手
编写及调用自定义助手的方法:
The class name must, at the very minimum, end with the helper name itself, using MixedCaps. E.g., if you were writing a helper called “specialPurpose”, the class name would minimally need to be “SpecialPurpose”. You may, and should, give the class name a prefix, and it is recommended that you use ‘View_Helper’ as part of that prefix: “My_View_Helper_SpecialPurpose”. (You will need to pass in the prefix, with or without the trailing underscore, to addHelperPath() or setHelperPath()).
The class must have a public method that matches the helper name; this is the method that will be called when your template calls “$this->specialPurpose()”. In our “specialPurpose” helper example, the required method declaration would be “public function specialPurpose()”.
In general, the class should not echo or print or otherwise generate output. Instead, it should return values to be printed or echoed. The returned values should be escaped appropriately.
The class must be in a file named after the helper class. Again using our “specialPurpose” helper example, the file has to be named “SpecialPurpose.php”.
Place the helper class file somewhere in your helper path stack, and Zend_View will automatically load, instantiate, persist, and execute it for you.
示例:
<?php
  //Custom Helper “myFieldset”
  echo $this->myFieldset(‘This my custom Helper.’).'<br>’;
  echo $this->myFieldset(‘This my custom Helper.’).'<br>’;
  echo $this->myFieldset(‘This my custom Helper.’).'<br>’;
?>调用代码:
    function customhelperAction()
    {
      $view = new Zend_View();
      $view->setScriptPath(‘views’);
      $view->setHelperPath(‘views/helpers’, ‘My_View_Helper’);
      echo $view->render(‘my_custom_helper.php’);
    }注意:本例只是使用Smarty引擎的其中一种方法。在ZF中还可以用别的形式来使用Smarty模板引擎,我们会在别的章节里介绍。

走进Zend Framework框架编程(五):Zend_Controller进阶

在前边的例子中,我们的index.php引导文件所在的文件夹与控制器、视图等所在的文件夹是不同的,这并不符合一般网站项目的文件夹的组织习惯。从本部分开始,我们把他们放在同一个文件夹中,这是因为ZF提供了灵活的文件夹组织和配置能力。
    我们新的文件夹结构如下:

  1.   …/htdocs
  2.     library
  3.       Zend
  4.     Phpchina2.com
  5.       controllers
  6.       models
  7.       views
  8.       index.php
  9.       .htaccess

复制代码

即把原来app_phpchina1.com文件夹下的子文件夹Controllers、models、views转移到文件夹phpchina1.com下,与index.php和.htaccess文件放在一起。为了不覆盖原来的代码,我们把原来的phpchina1.com文件夹拷贝一份,命名为phpchina2.com。
    为了测试,我们建立一个新的虚拟主机phpchina2.com。这可以仿照前面的phpchina1.com来完成,即在httpd.conf中添加:

  1.     <VirtualHost *:8080>
  2.         ServerAdmin any@any.com
  3.         DocumentRoot “C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/phpchina2.com”
  4.         ServerName phpchina2.com
  5.         ErrorLog “logs/phpchina2.com-error.log”
  6.         CustomLog “logs/phpchina2.com-access.log” common
  7.     </VirtualHost>

复制代码

重新启动Apache服务。
    然后再在hosts文件中加一条
    <服务器IP>      phpchina1.com
    以后即可以以http://phpchina2.com:8080/*.*的形式访问新的网站。但现在暂时还不能像前边的例子一样运行原来的示例代码,因为还需要对index.php里的路径配置进行调整。
    我们把语句PATH_SEPARATOR . ‘../App_phpchina1.com/models/’修改为:
    PATH_SEPARATOR . ‘models/’
    把语句”default”=>’../app_phpchina1.com/controllers’修改为:
    “default”=>’controllers’

    然后用浏览器打开地址http://phpchina2.com:8080,就可以看到和原来一样,显示了字符串“Hello PHPChina1.com!”。也可以打开http://phpchina2.com:8080/news显示 “Welcome to News!”字符串。

    下一步我们对Controllers进行扩展改造。比如我们有这样的需求:如果一个网站项目很大,有多个模块,Controllers文件夹下将有大量的控制器文件,我们打算通过按模块建立子文件夹对其进行分类存放,IndexController.php控制器文件存放在index文件夹下,news控制器文件存放在news文件夹下。
    首先修改index.php文件,把
      $fc->setControllerDirectory(array(
          “default”=>’controllers’,
        ));
    处的”default”=>’controllers’修改为
    “default”=>’controllers/index’
    这时http://phpchina2.com:8080地址就可以打开了,显示结果与原来相同。
    但要访问news文件夹下的NewsController.php控制器,必须把news添加到搜索路径,并为其命名,我们这里命名为other模块,这样我们就添加以下语句:
    $fc->addControllerDirectory(‘controllers/news’, ‘other’);
    然后打开地址http://phpchina2.com:8080/news,看不到原来的结果。
    再打开地址http://phpchina2.com:8080/otherhttp://phpchina2.com:8080/other/news,也看不到原来的结果。
    原来,需要修改NewsController.php中的语句
    Class NewsController extends Zend_Controller_Action为
    class other_NewsController extends Zend_Controller_Action
    即在类名前加“other_”字符串。然后访问地址
    http://phpchina2.com:8080/other/news
    即可得到正确的结果。
    这里有一个新问题,即http://phpchina2.com:8080/other/news和 index默认文件夹的
    OtherController控制器中的newsAction方法访问路径是相同的,如果index文件夹下同时还有文件 OtherController.php,内容为:
    <?php
    class OtherController extends Zend_Controller_Action
    {
      function newsAction()
      {
        echo “This is OtherController.php ==> newAction”;
      }
    }
    ?>    那么http://phpchina2.com:8080/other/news会不会显示
    This is OtherController.php ==> newAction
    字符串的结果呢?不会的,因为按模块搜索访问方式优先。有了other搜素路径名,就不能再访问默认文件夹下的other控制器中的任何方法了,即使搜素路径下没有任何对应的控制器文件,也不能访问。即,即使没有news文件夹下的NewsController.php文件,http://phpchina2.com:8080/other/news也不会获得index文件夹下 OtherController.php中newsAction方法的结果。
    这里的搜素路径和Web服务器的虚拟路径有点相似。但是因为可以用代码指定,所以更灵活一些。后面,我们根据这一点可以灵活的组织网站项目的文件夹结构。
    我们对前边ZF中请求URL的格式做一些补充,原URL格式
    http://host_name/controller_name/action_name/param1/ value1/param2/ value2…
    可以扩展为:
    http://host_name/路径/controller_name/action_name/param1/ value1/param2/ value2…
    路径名需要作为“路径名_”字符串的形式加在
    代码“Class controller_name”的实现代码的前边,成为“Class路径名_controller_name”的形式。明白了这点,在分析ZF的源代码的时候,就不会被路径问题所迷惑了。

走进Zend Framework框架编程(四):Zend_Controller和引导文件

上一节的基本示例已经运行成功,说明ZF已经开始工作了。这一部分我首先引入Zend_Controller的概念,再对引导文件index.php做一个详细的解释。
    1,理解Zend_Controller
    Zend_Controller是ZF的MVC体系的核心部份。
    Front Controller(前端控制器)设计模式具体是由Zend_Controller_Front静态类实现的,所有的请求都必须通过前端控制器,并基于请求的URL被分发(dispatch)到不同的控制器去来处理。
    Zend_Controller体系具有可扩展性,可以通过继承已有的类,或者通过实现各种接口和继承抽象类来写自己的扩展类,也可以编写插件或者助手类 (helper)来增强系统的功能。
    Zend_Controller_Front类的声明和所有初始化工作,以及执行dispatch()方法等都是在Bootstrap文件即入口程序中完成的,在ZF中,通常就是指index.php文件。因为用户的所有请求都是从index.php进入的,所以需要配置Web服务器,把所有请求导向到 index.php文件中,这些我们在前边已经完成了,而这里我们已经对其原因有了更深入的理解。
    2,理解ZF是如何处理HTTP请求的:
    例如有一个URL请求地址http://host_name/controller_name/action_name。
    其中host_name一般是一个域名,例如www.why100000.com。默认情况下,该URL的第一个部份 controller_name会映射到一个控制器,第二个部份action_name则映射到控制器类中的Action(控制器类内部的一个方法)。在本例中,其服务器路径为/controller_name/action_name,则会映射到controller_name控制器和 action_name这个Action。如果不存在该action,则会默认调用index这个action。如果控制器不存在,则会默认自动调用 index控制器(按照Apache的命名惯例,将自动映射到DirectoryIndex文件)。
    接下来,Zend_Controller的dispatcher会根据控制器的名称找到具体的控制器类。通常它会把控制器名称加上Controller。因此,上例中controller_name控制器与controller_name Controller类相对应。
    类似地,action会映射到控制器类中的一个方法。默认情况下,会被转成小写字母,然后加上“Action”字符串。因此,上例中 action_name这个action与 action_name Action相对应。于是最终我们访问URL调用的是
    controller_name Controller-> action_name Action()
    方法。
    控制器类保存为controller文件夹下的一个php文件中,文件名前缀约定与controller类的名字相同。例如 Controller_nameController.php。
    现在我们根据以上约定创建一个控制器和Action方法:
    <?php
    class Controller_nameController extends Zend_Controller_Action
    {
      function action_nameAction()
      {
    ……
    }
    }
    ?>    以上代码需要以文件名Controller_nameController.php保存,并存放到controllers文件夹下。
    ZF有一个约定,就是当url中不指定控制器名时,默认为index控制器;当不指定action名时,默认为index action。于是,当控制器名和action都不指定时,就执行index控制器类的indexAction方法,这时类文件形如:
    <?php
    class IndexController extends Zend_Controller_Action
    {
      function indexAction()
      {
    ……
    }
    }
    ?>    该代码保存为IndexController.php文件名。
    一般的控制器类都有一个indexAction函数,作为控制器的默认方法。
    注意在url中,控制器名和action可以同时省略:即形如http://host_name/
    也可以省略action名,执行indexAction方法,形如http://host_name/controller_name/
    但不能省略控制器名而指定action名,即
    http://host_name//action_name
    是不正确的。
    形如http://host_name/xxx的地址,xxx被认为是控制器名。
    对于我们上一节的示例,当用浏览器打开地址http://phpchica1.com:8080,其实执行的是 IndexController控制器类的indexAction方法,执行了语句
    echo “Hello PHPChina1.com!”;
    而我们在IndexController控制器类中再建立一个成员函数:
      function otherAction()
      {
        echo “this is other Action.”;
      }    在浏览器地址栏输入http://phpchica1.com:8080/index/other, 将会输出字符串“this is other Action.”。
    3,对引导(bootstrap)文件index.php的解释
    error_reporting(E_ALL|E_STRICT);语句打开了错误输出开关,用于代码调试,正式发布的代码应该屏蔽错误信息。
    date_default_timezone_set(‘Asia/Shanghai’);设定时区,该语句不能省略。
    set_include_path(……);很关键的语句。用于设定类库的包含路径,ZF的系统类库就是在这里指定的。注意如果php.ini文件里的 include_path包含了ZF类库的路径,这里就可以不用包含../library路径。但是在自己的代码里指定ZF类库路径更方便一些,一般推荐这么做。../App_phpchina.com/models/路径下包含我们自己开发的自定义类文件。没有自定义类文件,可以不用包含该路径。 get_include_path再取得php.ini的其他包含路径,一同指定我们的应用程序使用。
    include “Zend/Loader.php”;语句装载ZF的类加载器。Zend/Loader.php正是从../library路径下取得的。
    Zend_Loader::registerAutoload();自动加载类。该语句可以分别用以下两段代码代替,效果相同:
    第一段代码:
      function __autoload($class)
      {  Zend_Loader::loadClass($class);  }
    第二段代码:
      Zend_Loader::loadClass(‘Zend_Controller_Front’);
    $fc = Zend_Controller_Front::getInstance();取得Zend_Controller_Front类实例。
    $fc->setControllerDirectory(……);指定一组控制器文件路径,参数是数组。让前端控制器知道从哪里去找我们的控制器类。如果仅有一个控制器文件夹,也可以写成:
    $fc->setControllerDirectory(‘../App_www.mydomain.com/controllers’);
    $fc->throwExceptions(true);设置抛出错误信息。
    $fc->setParam(‘xxx’, true);格式的语句用于设置一些参数。
    其中$fc->setParam(‘noViewRenderer’, true);指明不使用视图,false 是默认值。
    $fc->dispatch();语句开始执行分发,导向到请求的控制器执行后续代码。
    这是一个功能较少的、典型的bootstrap引导文件,每个ZF应用中,该文件大同小异,只是个别参数设置不同。这个文件可以作为一个模板,拷贝到其他 ZF应用中使用。这个文件与ZF应用的文件夹结构有直接关系,配置时一定要仔细。调整一些参数后可以使ZF有一些其他的额外功能。这点我们以后还会接触到。
    4,ZF中请求URL的格式
    Zend Framework的控制器 Zend_Controller使网站支持“干净的URL”。它把对控制器、方法的请求和参数的传递变形为对文件夹的访问形式。URL的完整格式为:
    http://host_name/controller_name/action_name/param1/ value1/param2/ value2…
    除过前边的host_name外,controller_name和action_name为控制器和方法名,后边的残出和值必须一一对应。
    传递的参数,在action方法中以 $this->_getParam(“参数名”);的方法来取得其值。
    因为有严格的对应关系,一般在有参数传递的情况下,控制器和action的名字都不能省略,否则会出现歧义。
    我们现在在
    C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\app_phpchina1.com
    \controllers
    文件夹下建立一个NewsController.php文件,内容为:
    <?php
      class NewsController extends Zend_Controller_Action
      {
        function indexAction()
        {
          echo “Welcome to News!”;
        }
        function pageAction()
        {
          $id = $this->_getParam(“id”);
          echo “ID: “.$id.”<BR>”;
          $type = $this->_getParam(“type”);
          echo “TYPE: “.$type.”<BR>”;
        }
      }
    ?>    然后我们以http://phpchina1.com:8080/news/page/id/001/type/typename地址访问该文件,会得到以下显示结果,可以看到已经正确的获得了传递的参数:
    ID: 001
    TYPE: typename
    我们看到,ZF访问框架内的控制器方法,不是通过从url中访问控制器类php文件来实现的,而是在index.php中前端控制器的控制下对对应的控制器及其方法进行访问,一切过程和细节都被ZF框架屏蔽了,我们只要写出正确的url就能访问到对应的程序逻辑。
    我们可以试着访问http://phpchina1.com:8080/index.php地址,实际上执行的还是IndexController控制器的indexAction方法,输出字符串“Hello PHPChina1.com!”。而想直接访问IndexController.php和NewsController.php文件,我们甚至连它们的 url路径是什么都无法知道。

走进Zend Framework框架编程(三):运行第一个程序

框架主要是为了提高开发效率和支持多人团队开发而发明的。传统的平面式开发总是让每个软件项目从零开始,程序员不得不重复“制造轮子”的工作;而界面 (html+css+javascript)与程序逻辑的混杂使程序员与界面制作的角色很难分离开来,这有可能使得应用程序在其生命周期中变得无法维护。
    ZF的做法是,为了提高应用程序的可维护性,它将程序的代码分成如下三个不同的部分(通常也是独立的文件):
    模型(M):应用程序的模型部分。关心的是显示的数据的细节。模型通常关注的是业务逻辑,以及如何从数据库中存取数据。在具体的代码中表现为一个一个不同的类,可以看做是一个用户自定义的类库。
    视图(V):视图关心的是用户显示的内容,它通常是HTML。
    控制器(C):控制器将模型和视图结合起来,保证将正确的数据显示到页面上。
    也就是说,Zend Framework使用Model-View-Controller(MVC)架构。它将程序中不同的部分独立开来,使得应用程序的开发和维护变得更加容易。
    1,ZF框架的设计思想和策略
    ZF采用了前端控制器(Front Controller)设计模式,它把所有用户的请求发送到一个中心控制点,具体做法就是所有的请求都需要通过index.php进入。然后通过 index.php中的脚本把用户请求分发到对应的控制器中,控制器负责调用模型中的逻辑和用户界面的呈现。
    2,Apache对ZF的支持
    为了实现ZF的设计思想,需要Web服务器的支持。Apache服务器是通过其mod_rewrite功能来实现的。
    2.1配置mod_rewrite扩展需要去掉Apache的配置文件httpd.conf中以下语句前的注释符号#,使配置生效,即装载 mod_rewrite.so模块。
    LoadModule rewrite_module modules/mod_rewrite.so
    还必须保证Apache已配置成支持.htaccess文件的模式。通常这可以通过在httpd.conf中将

  1.     <Directory “C:\Program Files\Apache Software Foundation\Apache2.2\htdocs”>
  2.         Options Indexes FollowSymLinks
  3.         #AllowOverride None
  4.         AllowOverride All
  5.         Order allow,deny
  6.         Allow from all
  7.     </Directory>

复制代码

中的AllowOverride None改成AllowOverride All来实现。如果没有正确配置mod_rewrite及.htaccess,那么除了首页外将不能看到任何其它的页面。
    保存配置,重启Apache服务,使配置生效。
    2.2 .htaccess文件的内容:
    RewriteEngine on
    RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php
    意思是,让RewriteEngine引擎启动,并把对除过js、ico、gif、jpg、png、css等图片、js脚本、css的请求,都发送到 index.php文件中。
    3,第一个程序的目录结构
    在htdocs下建立以下2个文件夹:phpchina1.com和app_phpchina1.com。
    app_phpchina1.com下建立以下文件夹controllers、models、views。
    这样一来,典型的文件夹结构形如:
    …htdocs
    app_phpchina1.com
    controllers
    models
    views
    library
      Zend
    phpchina1.com
    phpchina1.com文件夹下存放index.php和.htaccess文件;
    Controllers存放控制器文件;
    models存放控制器文件;
    views存放视图文件;
    注意这只是ZF所推荐的文件夹的组织形式,但并不是必须的形式。实际项目中可以根据自己的具体情况灵活变通,这一点本教程的后面会详细的探讨。如果应用特别简单,models和views文件夹也许也用不到,我们下面的“最简单的ZF程序”即是如此。
    4,最简单的ZF程序:Hello PHPChina1.com!
    激动人心的时刻到了,我们讲运行我们的第一个ZF程序。
    Index.php文件内容如下:
    <?php
      error_reporting(E_ALL|E_STRICT);
      //设定时区
      date_default_timezone_set(‘Asia/Shanghai’);
      //指明引用文件的路径
      set_include_path(‘.’ .
      PATH_SEPARATOR . ‘../library/’.
      PATH_SEPARATOR . ‘../App_phpchina.com/models/’.
      PATH_SEPARATOR . get_include_path());
      include “Zend/Loader.php”;
      Zend_Loader::registerAutoload();
      $fc = Zend_Controller_Front::getInstance();
      $fc->setControllerDirectory(array(
          “default”=>’../app_phpchina1.com/controllers’,
        ));
      $fc->throwExceptions(true);
      $fc->setParam(‘noViewRenderer’, true);
      $fc->setParam(‘noErrorHandler’, false);
      //开始运行程序
      $fc->dispatch();
    ?>
    Controllers文件夹下IndexController.php文件的内容:
    <?php
    class IndexController extends Zend_Controller_Action
    {
      function indexAction()
      {
        echo “Hello PHPChina1.com!”;
      }
      function otherAction()
      {
        echo “this is other Action.”;
      }
    }
    ?>
    运行:
    在浏览器地址栏输入http://phpchica1.com:8080回车,浏览器将显示结果:
    Hello PHPChina1.com!
    如果看到上述信息,恭喜你,成功了!
    下一节将对这里的2个文件的代码进行详细解释。

走进Zend Framework框架编程(二):软件安装和环境配置

1,Windows Server2003
    Windows Server2003安装完毕,最好能上网通过Windows Update安装系统所有的补丁,包括其最新的SP补丁包,虽然这对于学习不见得有致命的影响,但还是建议这样做。由于Windows 2003默认是不安装IIS的,所以在安装完毕后还必须通过“添加和删除程序”来安装IIS6.0组件。
    注意,还必须注意把IIS6.0的“主目录”-“配置”-“选项”的“启用父路径”开通。还要把“Web服务扩展”中的“所有未知ISAPI扩展”设置为 “允许”-因为我们的测试环境将让PHP与IIS以“ISAPI方式”集成。“在服务器端的包含文件” 服务扩展,好像不影响PHP的require等脚本命令。
    再次注意,由于我们的ZF学习和代码调试主要是在Apache环境下进行,所以以上IIS的配置与我们的ZF学习关系不大,只是当我们作为附加知识,需要在IIS下演示ZF的时候才需要这些配置。

    2,mySQL for Windows的安装
    数据库选择和Windows2003安装在同一台机器上。mysql-5.0.41-win32是一个Setup.exe文件,双击它,一路做一些选择,再按“下一步”,安装完毕。最后选择立即进入配置过程。对于我们的学习,mySQL如何配置影响不大。数据库建议选成“myISAM”格式。注意记住用户 root的密码。
    注意在配置nySQL之前,务必关闭服务器上的防火墙和一些杀毒软件,比如360安全卫士,否则可能引起创建数据库失败。如果有问题,可以卸载mySQL 后再试。

    3,Apache for Windows
    我选择apache_2.2.8-win32-x86-openssl-0.9.8g.msi的Apache for Windows版本。安装路径为:C:\Program Files\Apache Software Foundation\Apache2.2。

    3.1 由于IIS服务本身占用了80端口,所以我选择Apache使用8080端口。这样一台机器就能跑2种Web服务,便于我们的测试。在真实的生产环境下,都是禁用或不安装IIS而让Apache在80端口下服务的。修改Apache的端口的方法是,用文本编辑器打开C:\Program Files\Apache Software Foundation\Apache2.2\conf\httpd.conf文件,把Listen 80修改为Listen 8080。
    注意httpd.conf是Apache的配置文件,以后会经常修改。该文件修改完毕后必须重新启动Apache服务,配置才能生效。这一点必须特别注意。(谁要愿意,重新启动机器也行,Apache服务也会重新初始化一次—但相信PHP程序员没这么傻的)

    3.2修改网站默认首页文件
    在httpd.conf以下代码的DirectoryIndex后加index.php,多个首页文件用逗号隔开:

  1.     <IfModule dir_module>
  2.         DirectoryIndex index.html, index.php
  3.     </IfModule>

复制代码

    3.3 Apache2.2文件夹下有许多文件夹,其中有2个重要的文件夹:
    Conf里包含着配置文件httpd.conf;
    Htdocs是默认的存放网页文件的地方。默认的存放网页文件的文件夹可以通过httpd.conf的DocumentRoot语句来修改,但我们的测试使用默认的。

    3.4 Apache虚拟主机配置
    为了测试众多的应用,我们需要建立虚拟主机,使应用程序分别在独立的网站内运行。
    屏蔽掉默认的网站,即在以下配置语句前加个注释符号#(也可以删除该语句):
    #DocumentRoot “C:/Program Files/Apache Software Foundation/Apache2.2/htdocs”
    然后在该语句下键入以下一段语句:

  1.     NameVirtualHost *:8080
  2.     <VirtualHost *:8080>
  3.         ServerAdmin webmaster@mydomain.com
  4.         DocumentRoot “C:\Program Files\Apache Software Foundation\Apache2.2\htdocs”
  5.         ServerName localhost
  6.         ErrorLog “logs/localhost-error.log”
  7.         CustomLog “logs/localhost-access.log” common
  8.     </VirtualHost>

复制代码

   就可以在本服务器上以http://localhost:8080/来访问本地的网站,例如,一个index.php文件内容如下,就可以显示服务器的基本配置信息:
    <?php
      phpinfo();
    ?>
    配置虚拟主机(以域名phpchina1.com区分):

  1.     <VirtualHost *:8080>
  2.         ServerAdmin any@any.com
  3.         DocumentRoot “C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/phpchina1.com”
  4.         ServerName phpchina1.com
  5.         ErrorLog “logs/phpchina1.com-error.log”
  6.         CustomLog “logs/phpchina1.com-access.log” common
  7.     </VirtualHost>

复制代码

   保存后重启服务,在局域网某台机器,或服务器上,在 hosts文件中加入语句
    <服务器IP>      phpchina1.com
    就可以以http://phpchina1.com:8080/*.*的形式访问网站。
    但注意:
    在Apache与PHP正确集成以前,是不能访问PHP文档的。
    在进一步正确配置ZF之前,也不能做ZF的测试。实际上,在运行ZF代码之前还有几个重要配置步骤需要做。

    4,PHP安装和配置
    我们选用PHP 5.2.5版本。得到php-5.2.5-Win32.zip文件后,解压到以下文件夹下即可:
    C:\usr\local\php-5.2.5.for_Apache2.2\php-5.2.5.bin
    你的路径可以与这里不同,我建立这么深层的路径是因为我的机器安装的PHP版本很多,才这么组织的。
    PHP需要的配置:
    PHP的配置通过编辑php.ini来完成。第一次,我们复制
    C:\usr\local\php-5.2.5.for_Apache2.2\php-5.2.5.bin
    下的php.ini-recommended文件生成一个php.ini文件,然后指定extension_dir路径:
      extension_dir = “C:\usr\local\php-5.2.5.for_Apache2.2\php-5.2.5.bin\ext”
    再打开以下语句的注释,用于让ZF支持mySQL数据库:
      extension=php_pdo.dll        打开 pdo
      extension=php_pdo_mysql.dll  打开 pdo_mysql
    为了PHP支持更多扩展,可以把它们的注释去掉,例如为了支持图形功能,可以打开
    extension=php_gd2.dll
    顺便注意一下,为了安全,register_globals = Off,默认已经是关闭的了。我们自己开发的PHP扩展也可以拷贝到ext 文件夹下,用extension=myphp_ext.dll的形式调用。

    5,Zend Framework:
    取得ZendFramework-1.5.1.zip压缩包,解压取出里面的library文件夹的内容,把library文件夹拷贝到以下文件夹下:
    C:\Program Files\Apache Software Foundation\Apache2.2\htdocs
    最终的文件夹路径形式是:
    C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\library\Zend\*.*

    6,PHP与Apache、mySQL的集成
    在 http.conf 文件最后加语句:

  1.     #集成mySQL
  2.     LoadFile “C:\usr\local\php-5.2.5.for_Apache2.2\php-5.2.5.bin\libmysql.dll”
  3.     #集成 PHP5
  4.     LoadModule php5_module “C:\usr\local\php-5.2.5.for_Apache2.2\php-5.2.5.bin\php5apache2_2.dll”
  5.     AddType application/x-httpd-php .php
  6.     AddType application/x-httpd-php-source .phps
  7.     #(指定php.ini文件的位置)
  8.     PHPIniDir “C:\usr\local\php-5.2.5.for_Apache2.2\php-5.2.5.bin”

复制代码

   如果是 PHP6,则形如:
    LoadModule php6_module “……\php6apache2_2.dll”
    注意以上装载模块语句LoadModule必须加载与当前PHP和Apache版本对应的dll文件。配置错了是不能工作的。
    再次注意:保存http.conf文件后必须重新启动Apache服务,配置才能生效。

    到此为止,应该可以在我们的虚拟主机上运行普通的非Zend Framework的PHP代码了。遗憾的是仍然不能开始我们的Zend Framework之旅。因为ZF的特别设计,我们还需要对Apache做点额外配置。这也是我前边不推荐在IIS上跑ZF应用的原因所在,因为 Apache经过几处设置就可以跑ZF应用,而IIS需要借助第三方的软件才可以。再者,在大家心里,IIS和PHP、ZF并不是黄金搭档,在程序员的心里,技术也必须门当户对才行。

    预计到下一部分的的最后,我们才能真正见到ZF的第一个示例。