2009年6月1日星期一

Zend_Paginator 空适配器 bug

Zend_Paginator_Adapter_Null 可以让你不使用 Zend_Paginator 来管理数据而仍然能够使用 分页控制。但zf 1.6-1.8.2 存在最后一页数据不对的情况。

Test code:

$paginator = Zend_Paginator::factory(2);
$paginator->setCurrentPageNumber(1);
$paginator->setItemCountPerPage(5);

$pages = $paginator->getPages();
var_dump($pages);


期望结果:
["currentItemCount"]=>2
["lastItemNumber"]=>2
实际结果:
["currentItemCount"]=>5
["lastItemNumber"]=>5

修正方法:
public function getItems($offset, $itemCountPerPage)
{
if ($this->_count <= $offset) return array();
$remainItemCount = $this->_count - $offset;
$currentItemCount = $remainItemCount > $itemCountPerPage ? $itemCountPerPage : $remainItemCount;
return array_fill(0, $currentItemCount, null);
}

2009年4月23日星期四

Zend Framework Plugin: 不同模块(Module)使用不同的错误控制器(ErrorController)

Zend Framework 的错误控制器提供了统一的错误处理方式, 参阅 http://framework.zend.com/manual/en/zend.controller.html#zend.controller.quickstart.go.errorhandler
  当我们工作于模块方式时,常常希望不同模块使用不同的错误处理,比如 default 模块使用默认ErrorController, 后台管理模块 admin 使用 Admin_ErrorController 提供不同的布局和更详细的错误显示。

Plugin 方式为我们提供了方便的设置途径,以下 plugin 设置每个 module 使用 模块里的 ErrorController, 如果模块不提供 ErrorController 则使用默认的 ErrorController:
require_once ('Zend/Controller/Plugin/Abstract.php');
class Mezi_Controller_Plugin_ErrorControllerSelector extends Zend_Controller_Plugin_Abstract
{
    public function routeShutdown(Zend_Controller_Request_Abstract $request)
    {
        $front = Zend_Controller_Front::getInstance();

        //If the ErrorHandler plugin is not registered, bail out
        if( !($front->getPlugin('Zend_Controller_Plugin_ErrorHandler') instanceof Zend_Controller_Plugin_ErrorHandler) )
            return;

        $error = $front->getPlugin('Zend_Controller_Plugin_ErrorHandler');

        //Generate a test request to use to determine if the error controller in our module exists
        $testRequest = new Zend_Controller_Request_HTTP();
        $testRequest->setModuleName($request->getModuleName())
                    ->setControllerName($error->getErrorHandlerController())
                    ->setActionName($error->getErrorHandlerAction());

        //Does the controller even exist?
        if ($front->getDispatcher()->isDispatchable($testRequest)) {
            $error->setErrorHandlerModule($request->getModuleName());
        }
    }
}


启动时安装 plugin:
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Mezi_Controller_Plugin_ErrorControllerSelector());




Zend Framework Plugin:自适应 magic_quotes_gpc 环境

推荐关闭 php.ini 的 magic_quotes_gpc 选项,一来可以提升性能,二来可以保证得到的原始数据,Zend_Db 会自动转义数据入库。并且新版本的 php 推荐配置都是默认关闭 magic_quotes_gpc 选项。
当然某些系统打开了 magic_quotes_gpc 选项,并且你也没有足够权限去修改这个配置,这些反斜杠可能给你造成困扰,我们可以用代码抵消修改。
Zend Framework 的 plugin 提供了很好的方式来做这些事情,而不需要改动我们去参数的每段代码。

/**
* A Zend Controller Plugin dedicated to undoing the damage of magic_quotes_gpc
* in systems where it is on.
*
* @author Ken
* @version $Id:$
*/
require_once ('Zend/Controller/Plugin/Abstract.php');
class Mezi_Controller_Plugin_StripMagicQuotes extends Zend_Controller_Plugin_Abstract
{
/**
* strip all slashes off $request parameters
*
* @param Zend_Controller_Request_Abstract $request
*/
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$params = $request->getParams();
array_walk_recursive($params, array($this , '_stripSlashes'));
$request->setParams($params);

if ($request instanceof Zend_Controller_Request_Http) {
$this->_stripSlashesGPC();
}
}

/**
* strip all slashes from GPC
*/
protected function _stripSlashesGPC()
{
array_walk_recursive($_GET, array($this , '_stripSlashes'));
array_walk_recursive($_POST, array($this , '_stripSlashes'));
array_walk_recursive($_COOKIE, array($this , '_stripSlashes'));
}

/**
* callback strip the slashes off an item in the Params array
*
* @param string $value
* @param string $key
*/
protected function _stripSlashes (&$value, $key)
{
$value = stripslashes($value);
}
}


然后启动时安装 plugin, 仅在 magic_quotes_gpc 打开时需要处理:

$front = Zend_Controller_Front::getInstance();

if (get_magic_quotes_gpc()) {
require_once 'Mezi/Controller/Plugin/StripMagicQuotes.php';
$this->_front->registerPlugin(new Mezi_Controller_Plugin_StripMagicQuotes());
}


Plugin 非常漂亮的实现了我们的需求,以前写好的代码都无须改动。

2009年3月23日星期一

PHP 中的 UTF8 和 Mysql

很多人在 Mysql 默认为非 UTF8 但要使用 UTF8 编码的时候会使用 "SET NAMES UTF8" 语句去设置正确的编码方式。通常为了方便在初始化 Db 对象或者创建连接后就去执行,虽然这种方式可以正确工作,但会导致一次连接和查询,即使初始化之后并无需要数据库。这也破坏了很多数据库对象提供的 lazy connect 特性。
PHP 手册不推荐使用 query() 去执行 SET NAMES

更好的方法是使用数据库驱动自身提供的特性去初始化编码。

mysql:
mysql_set_charset('utf8');

mysqli:
mysqli_set_charset($link, 'utf8'); //面向过程风格
$mysql->set_charset('utf8'); //或者面向对象风格
//也可以使用初始化命令
mysqli_options($link, MYSQLI_INIT_COMMAND, 'SET NAMES utf8'); //面向过程
$mysql->options(MYSQLI_INIT_COMMAND, 'SET NAMES utf8'); //面向对象


PDO:
//使用连接参数,MYSQL_ATTR_INIT_COMMAND 在连接后执行初始化命令,类似于 query 好处是仅在连接后执行一次
$pdo = new PDO("mysql:host=localhost;dbname=dbname",
'username', 'password',
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));


Zend_Db,Zend_Db 没有提供特殊的设置参数,只能使用初始化命令参数, 见 Mysqli 和 PDO 的 INIT_COMMAND:
//PDO 驱动
$params = array(
'host' => 'localhost',
'username' => 'username',
'password' => 'password',
'dbname' => 'dbname',
'driver_options' => array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8');
);
$db = Zend_Db::factory('Pdo_Mysql', $params);

//Mysqli 驱动:
$params = array(
'host' => 'localhost',
'username' => 'username',
'password' => 'password',
'dbname' => 'dbname',
'driver_options' => array(MYSQLI_INIT_COMMAND => 'SET NAMES UTF8');
);
$db = Zend_Db::factory('Mysqli', $params);


2009年2月25日星期三

Smarty and Zend_View integrantion

聚合使用 smarty

<?php
require_once ('Zend/View/Abstract.php');
class SmartyView extends Zend_View_Abstract
{
protected $_smarty;

public function __construct($config = array())
{
$this->_smarty = new Smarty();

if(!isset($config['compileDir']))
throw new Exception('compileDir is not set for '.get_class($this));
else
$this->_smarty->compile_dir = $config['compileDir'];

if(isset($config['configDir']))
$this->_smarty->config_dir = $config['configDir'];

if(isset($config['pluginsDir']))
$this->_smarty->plugins_dir[] = $config['pluginsDir'];

parent::__construct($config);
}

public function __set($key,$val)
{
parent::__set($key, $val);
$this->_smarty->assign($key,$val);
}

public function __isset($key)
{
$var = $this->_smarty->get_template_vars($key);
if($var)
return true;

return false;
}

public function __unset($key)
{
parent::__unset($key);
$this->_smarty->clear_assign($key);
}

public function assign($spec,$value = null)
{
if($value === null)
$this->_smarty->assign($spec);
else
$this->_smarty->assign($spec,$value);
}


public function clearVars()
{
$this->_smarty->clear_all_assign();
}

protected function _run()
{
$this->strictVars(true);

//why 'this'?
//to emulate standard zend view functionality
//doesn't mess up smarty in any way
$this->_smarty->assign_by_ref('this',$this);

$fileFullname = func_get_arg(0);
$templateDirs = $this->getScriptPaths();

//TODO: find more effective way to get correct template dir
$templateDir = $templateDirs[0];
$file = $fileFullname;
foreach ($templateDirs as $dir) {
if (preg_match("|^$dir|", $fileFullname)) {
$templateDir = $dir;
$file = substr($fileFullname,strlen($templateDir));
break;
}
}
//$file = substr(func_get_arg(0),strlen($templateDir));
//var_dump($templateDir, $file);
$this->_smarty->template_dir = $templateDir;
$this->_smarty->compile_id = $templateDir;

echo $this->_smarty->fetch($file);
}
}


初始化脚本中设置 view

//Create the view and set the compile dir to template_c
$view = new SmartyView(array(
'compileDir' => './template_c'
));

//Create a new ViewRenderer helper and assign our newly
//created SmartyView object as the view instance
$viewHelper = new Zend_Controller_Action_Helper_ViewRenderer($view);
$viewHelper->setViewSuffix('tpl');

//Save the helper to the HelperBroker
Zend_Controller_Action_HelperBroker::addHelper($viewHelper);

2009年1月19日星期一

SPL DirectoryIterator

DirectoryIterator 提供目录迭代功能。可替换 opendir() 和 dir 伪类功能。

opendir() 写法:
$path = '.';
if (is_dir($path)) {
if ($dh = opendir($path)) {
while (false !== ($file = readdir($dh))) {
echo "filename: $file \tfiletype: ", filetype($path . $file), "\n";
}
closedir($dh);
}
}


dir 伪类写法:
$path = '.';
$dir = dir($path);
while (false !== ($entry = $dir->read())) {
echo $entry, "\n";
}
$dir->close();


主角出场:
$path = '.';
$dir = new DirectoryIterator($path);
foreach ($dir as $fileinfo) {
echo 'filename:', $fileinfo->getFilename(), "\tfiletype:", $fileinfo->getType(), "\n";
}


参考资料:
http://www.php.net/manual/en/class.directoryiterator.php
http://www.php.net/manual/en/class.splfileinfo.php
http://www.php.net/~helly/php/ext/spl/

2009年1月8日星期四

SPL File Object

class SplFileObject extends SplFileInfo implements RecursiveIterator, SeekableIterator
SPL 文件对象继承自 SplFileInfo, 实现了 RecursiveIterator, SeekableIterator 接口。

一般我们访问文件方式:

$filename = '/path/ to/file';
$fp = fopen($filename, 'r');
if ($fp) {
while (!feof($fp)) {
$text = fgets($fp);
echo $text;
}
fclose($fp);
}


利用 SplFileObject 我们可以这么写:

$filename = '/path/ to/file';
$file = new SplFileObject($filename);
$file->openFile();
foreach ($file as $line) {
echo $line;
}

因为 SplFileObject 实现了RecursiveIterator 迭代器接口, foreach 可以很方便的访问到它。

写文件:

$filename = '/path/to/file';
$file = new SplFileObject($filename);
$file->openFile('w');
$file->fwrite('some text');


继承自 SplFileInfo 的一些方法:

echo '修改时间:', $file->getMTime(), "\n";
echo '所在路径:', $file->getPath(), "\n";
echo '文件名:', $file->getFilename(), "\n";
echo '全路径(路径+文件名):', $file->getPathname(), "\n";



更多方法请参考 Spl 文档

2008年11月24日星期一

PHP 命令行 fork 多进程处理

很多人认为 PHP 没有多线程和多进程处理能力,其实 PHP 有多种运行模式。作为在作 web 应用的时候通常运行于 cgi 或者 web api 模块模式,web 服务器本身多进程或线程方式运行。而 PHP 在 cli 命令行模式下则有能力使用 fork 产生子进程(Windows 环境不可用)。本文将论述这种情况。

PHP 的进程控制支持实现了 Unix 类型的进程创建、程序执行、信号处理和进程终止。在 web 服务环境中不应该启用进程控制,如果在 web 服务环境中使用进程控制功能可能导致不可预料的结果。

int pcntl_fork(void)
fork 的意思是叉子,分岔路口。同样程序在这里分叉 ^_^。fork() 函数执行后,系统会将当前进程复制一份,子进程和父进程的差别只有 PID(进程号) 和 PPID(父进程号)不同。也就是说,这一点开始除了PID和PPID,子进程和父进程有同样的数据,也正在同样的控制流程中。所以注意一点,如果父进程在循环中fork(), 那么子进程也在循环中。
分叉点在这儿,fork()返回值:
  • 父进程从 fork() 得到的返回值是子进程的PID, 
  • 子进程从 fork() 得到的返回值是 0,
  • 如果父进程得到返回值是 -1 表示有错误发生,比如系统资源耗尽导致无法创建子进程。
因为创建的是进程,而每个进程的空间是相互独立的,所以在fork()这一刻子进程和父进程拥有相同的数据和状态,随后各自独立运行,改变的数据和状态不会相互影响。
$pid = pcntl_fork();
if ($pid == -1) {
     die('could not fork');
} else if ($pid) {
     // we are the parent
     pcntl_wait($status); //Protect against Zombie children
} else {
     // we are the child
}


[未完待续……]

2008年8月18日星期一

Type Casting in PHP

PHP 中的类型声明


允许的声明有:


  • (int), (integer) - cast to integer

  • (bool), (boolean) - cast to boolean

  • (float), (double), (real) - cast to float

  • (string) - cast to string

  • (binary) - cast to binary string (PHP 6)

  • (array) - cast to array

  • (object) - cast to object

  • (unset) - cast to NULL (PHP 5)


一些有用(有趣)的特性:
(bool),(boolean), 当然,这个通常不用去显示声明,在需要的时候自动会转换。这里要说的是值如何转换:

  • 布尔值FALSE本身

  • 整数 0 (零)

  • 浮点数 0.0 (零)

  • 空字符串 '' 和 字符串 '0' (零)

  • 空数组(没有任何元素的数组)

  • 没有任何成员的对象(仅 PHP 4)

  • 特殊类型 NULL (包括 unset 的变量)

  • 从空标记创建的 SimpleXML 对象 (也就是不会有任何节点)


除此以外的任何值都是 TRUE

(string), 用于对象时,会调用对象的 __toString() 魔术方法(PHP5.2+可以正确调用, 更早版本不会)
(object), 用于数组时,可以把数组转化为对象,键作为对象的属性

$foo = array('bar' => 'foobar');
var_dump((object)$foo);

//输出
object(stdClass)#1 (1) {
["bar"]=> string(6) "foobar"
}


(array), 作用于对象时

$foo = new stdClass();
$foo->bar = 'foobar';
var_dump((array)$foo);

//输出
array(1) {
["bar"]=> string(6) "foobar"
}

2008年7月24日星期四

Obfuscate e-mail addresses

防止e-mail地址被垃圾邮件收集的方法



原文的作者 Silvan Mühlemann 花了一年半的时间收集数据比较九种不同的方法。

以下是3中有效的方法:

1. 用 CSS 改变文字方向

<style type="text/css">
span.codedirection { unicode-bidi:bidi-override; direction: rtl; }
</style>
<p><span class="codedirection">moc.liamg@77uohcnek</span></p>


2. 用 CSS display:none

<style type="text/css">
p span.displaynone { display:none; }
</style>
<p>kenchou77@<span class="displaynone">null</span>gmail.com</p>
或者直接 inline css
<p>kenchou77@<span style="display:none">null</span>gmail.com</p>


3. ROT13 加密

ROT13 简单的位移13个字母,加密和解密方法相同。


<script type="text/javascript">
document.write("xrapubh77@tznvy.pbz".replace(/[a-zA-Z]/g, function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);}));
</script>


原文地址:http://techblog.tilllate.com/2008/07/20/ten-methods-to-obfuscate-e-mail-addresses-compared/

2008年3月18日星期二

Zend Framework 1.5 is now available!

We couldn't be happier to announce that Zend Framework 1.5 is now
available
from the Zend Framework download site!
http://framework.zend.com/download
An overview of new features in 1.5:
* New Zend_Form component with support for AJAX-enabled form elements
* New action and view helpers for automating and facilitating AJAX
requests and alternate response formats
* Infocard, OpenID, and LDAP authentication adapters
* Support for complex Lucene searches, including fuzzy, date-range, and
wildcard queries
* Support for Lucene 2.1 index file format
* Partial, Placeholder, Action, and Header view helpers for advanced
view composition and rendering
* New Zend_Layout component for automating and facilitating site layouts
* UTF-8 support for PDF documents
* New Nirvanix, Technorati, and SlideShare web services
There are a lot of people to thank, since there are a lot of people who
worked very hard to make this happen. First of all, thanks to all the
Zend Framework contributors who helped in all kinds of ways to make this
a truly great, high-quality release. I would personally like to thank
the Zend Framework team here at Zend for working the long hours this
weekend to make sure everything came together on Monday morning. There
were also some very selfless souls in marketing who stayed up late over
the last few days to make sure the site was at its best in both style
and substance for the big day. Finally we'd like to thank all the ZF
users out there who inspire us to continue improving Zend Framework and
who never fail to keep us on our toes. :) We hope this release not only
lives up to your high expectations but goes beyond them.
Now we can finally say that we wholeheartedly recommend the 1.5 release
for production use.
Enjoy ZF 1.5!

,Wil

2007年8月14日星期二

PHP Unit 安装

1. 安装
方法一:通过 PEAR 安装
pear channel-discover pear.phpunit.de
pear install phpunit/PHPUnit

方法二:
1.下载压缩包,解压,设置 include_path
2. 将 pear-phpunit 脚本改名为 phpunit
3. 将脚本中 @php_bin@ 替换为 php 命令行解析器
4. 拷贝到 PATH 能搜索到的目录,或者 在 PATH 添加相应目录。
5. linux下添加执行属性 (chmod +x phpunit)

2007年7月9日星期一

Observer pattern

观察者模式 Observer Pattern


观察者模式为您提供了避免组件之间紧密耦合的另一种方法。该模式非常简单:一个对象通过添加一个方法(该方法允许另一个对象,即观察者 注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可观察的对象无关。结果是对象可以相互对话,而不必了解原因。



SPL 提供的 Observer 接口:
观察者:
SplObserver::update(SplSubject $subject)
观察者接口,由被观察对象调用。

主题(被观察者):
SplSubject::attach(SplObserver $observer)
参数:
$observer 要添加的观察者

SplSubject::detach(SplObserver $observer)
参数:
$observer 要删除的观察者

SplSubject::notify()
通知所有观察者.

2007年6月13日星期三

The Template Method Pattern in PHP 5

php5模板方法模式


节选翻译,原文: http://www.zend.com/zend/php5/php5-OOP.php

什么是模板方法模式?定义一个操作中算法的骨架,将一些步骤的执行放到其子类中.
这是一种重要的代码重用技术。模板方法模式通常用在类库中提取类的共同的行为。
模板方法设计模式可以让子类去重新定义一个操作中的某些步骤,而不用去改变这个操作的结构。
这些可重新定义的步骤可以在基类中标为强制的或者可选的。由于这些步骤都会归于单独的方法,所以子类必须为强制的步骤提供一个实现方法,可选的则可以忽略。

模板方法可以实现一个操作中不变的部分,把那些可能变化的行为留给子类实现。也可以用于仅在在指定点提供hook操作来给子类扩展行为。

例子


Listing 1. AbstractClass.php



Listing 1 显示了一个迷你的模板方法,叫做 templateMethod()。它使用了两个方法:mandatoryOperation() 和 optionalOperation(), 这两个可以在子类中重定义。
abstract class AbstractClass {
public final function templateMethod() {
print "AbstractClass::templateMethod() called.\n";

$this->mandatoryOperation();
$this->optionalOperation();
}

protected abstract function mandatoryOperation();

protected function optionalOperation() {
}
}



Listing 2: ConcreteClass.php



Listing 2 显示了一个和 AbstractClass 类对应的子类。 ConcreteClass 重定义了mandatoryOperation() 和 optionalOperation() 方法。

require_once 'AbstractClass.php';

class ConcreteClass extends AbstractClass {
protected function mandatoryOperation() {
print "ConcreteClass::mandatoryOperation() called.\n";
}

protected function optionalOperation() {
print "ConcreteClass::optionalOperation() called.\n";
}
}


Listing 3: example.php



最后, Listing 3 显示了如何调用 ConcreteClass 。

require_once 'ConcreteClass.php';

$o = new ConcreteClass;
$o->templateMethod();


输出:
AbstractClass::templateMethod() called.
ConcreteClass::mandatoryOperation() called.
ConcreteClass::optionalOperation() called.

2007年6月3日星期日

使用 Zend_Console_Getopt 处理 PHP 命令行

Linux 下写Shell只要在第一行加上
#!/path/to/php
然后将脚本加上执行属性即可 chmod +x

注意:Windows下编程再上传的朋友要注意。换行必须以linux格式,否则脚本会报错

require_once 'Zend/Console/Getopt.php';

定义规则:

使用短语
$opts = new Zend_Console_Getopt('abp:');

使用长语法
$opts = new Zend_Console_Getopt(
array(
'apple|a' => 'This option chooses apple, with no parameter',
'banana|b=i' => 'This option chooses banana, with required integer parameter',
'pear|p-s' => 'This option chooses pear, with optional string parameter'
)
);


捕获异常
try {
$opts = new Zend_Console_Getopt('abp:');
$opts->parse();
} catch (Zend_Console_Getopt_Exception $e) {
echo $e->getUsageMessage();
exit;
}


获取命令行选项和参数
$b = $opts->getOption('b');
$p = $opts->getOption('p');


另一种方法是使用 __get() and __isset() 魔术方法
if (isset($opts->b)) {
echo "I got the b option.\n";
}
$p = $opts->p; // null if not set

2007年4月21日星期六

概念: interface 和 abstract class 的区别

什么是 abstract class
PHP 5 引入抽象类和方法。抽象类不能被实例化。任何类只要包含有抽象方法(即使只有一个抽象方法)就必须也被定义为抽象。定义为抽象的方法只需要简单的声明而不用实现。
从抽象类继承的时候,所有父类中标记为抽象方法的声明都必须在子类中定义;此外,这些方法还必须用相同的(或更弱的)访问控制。比如,如果抽象方法定义为保护(protected),函数实现必须被定义为protected或者public

抽象类例子:
abstract class AbstractClass
{
// Force Extending class to define this method
abstract protected function getValue();
abstract protected function prefixValue($prefix);

// Common method
public function printOut() {
print $this->getValue() . "\n";
}
}

class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}

public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}

class ConcreteClass2 extends AbstractClass
{
public function getValue() {
return "ConcreteClass2";
}

public function prefixValue($prefix) {
return "{$prefix}ConcreteClass2";
}
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";



什么是 interface
Object Interface 对象接口允许你创建这种代码,一个类必须实现的指定方法,而又不必定义这些方法如何处理。接口使用interface关键字定义,和标准类定义一样,但是任何方法都没有内容。接口内所有的方法声明都必须为公开(public),这是接口的本质(呵呵,所谓接口嘛,就是用于交互的)。
实现
要实现一个接口,使用 implements 操作符。接口中的所有方法都必须在一个类中实现;不这么做会导致一个致命错误(fatal error)。 如果想要的话,使用逗号分隔每个接口,一个类可以实现多个接口。
注意:一个类不能实现两个有同样函数名的接口,因为这导致这个函数的含义模糊。

接口的例子:
// Declare the interface 'iTemplate'
interface iTemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}

// Implement the interface
// This will work
class Template implements iTemplate
{
private $vars = array();

public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}

public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}

return $template;
}
}

// This will not work
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (iTemplate::getHtml)
class BadTemplate implements iTemplate
{
private $vars = array();

public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
}


实现多个接口:
class MyClass implements anInterface, anotherInterface {
//...
}


如果想要继承一个类并实现一个接口,先使用“extends”然后“implements”:
class MyChildClass extends MyParentClass implements MyInterface
{
// definition
}



interface 和 abstract class 的区别
PHP 5 中类没有多重继承,但可以实现多个接口。这应该是接口的主要用途。这点和Java类似,和C++不同。应该是学java,对多重继承的一种解决方式吧。

代码形式上来说,接口只是一个声明,其方法没有任何实现代码;抽象类的方法既可以没有实现代码,也可以拥有实现代码。

2007年4月10日星期二

使用模块设计 Zend Framework 控制器

启动文件:基本上一样,唯一的区别:
//$frontController->setControllerDirectory('./application/controllers');
//将原来的设置控制器目录改成以下方式:
$frontController->setControllerDirectory(array(
'default' => './application/controllers/',
'admin' => './application/controllers/admin/')
);


index.php
<?php
/**
* Bootstrap file
*/
error_reporting(E_ALL|E_STRICT);
date_default_timezone_set('Asia/Shanghai');

set_include_path('.' . PATH_SEPARATOR . '../library/'
. PATH_SEPARATOR . './application/models'
. get_include_path());

require_once "Zend/Loader.php";
// autoload class
spl_autoload_register(array('Zend_Loader', 'autoload'));

// load configuration
$config = new Zend_Config_Ini('./application/config.ini', 'general');
Zend_Registry::set('config', $config);

// setup database
$db = Zend_Db::factory($config->db->adapter, $config->db->config->asArray());

Zend_Db_Table::setDefaultAdapter($db);

// register the view we are going to use
$view = new Zend_View();
$view->setScriptPath('./application/views');
Zend_Registry::set('view', $view);

$auth = Zend_Auth::getInstance();

// setup controller
$baseUrl = substr($_SERVER['PHP_SELF'], 0,
strpos($_SERVER['PHP_SELF'], '/index.php'));
$frontController = Zend_Controller_Front::getInstance();
$frontController->setBaseUrl($baseUrl);
$frontController->throwExceptions(true);

$frontController->setControllerDirectory(array(
'default' => './application/controllers/',
'admin' => './application/controllers/admin/')
);

// run!
$frontController->dispatch();
?>


default模块:
在application/controllers目录下的IndexController.php原来一样设计,不需要改动:
<?php
class IndexController extends Zend_Controller_Action
{
public function init()
{
}

public function indexAction()
{
}

public function otherAction()
{
}
}
?>


admin模块:
在application/controllers/admin目录下的IndexController.php是这样的。注意类名:admin_IndexController, 这符合目录-类名转换的约定。
<?php
class admin_IndexController extends Zend_Controller_Action
{
public function init()
{
}

public function indexAction()
{
}

public function otherAction()
{
}
}
?>


好了,大功告成,就这么多。
访问的时候和原来是一样的。
http://localhost/ 访问默认模块index控制器indexAction() application/controllers/IndexController.php
http://localhost/admin 访问admin模块index控制器indexAction() application/controllers/admin/IndexController.php

2007年3月20日星期二

PHP 安装

PHP 安装
这篇文章为新手准备。
其实详细信息PHP安装文档中都已经详细说明了。

  1. Unix下安装:

    1. gzip -d httpd-2_0_NN.tar.gz
    2. tar xvf httpd-2_0_NN.tar
    3. gunzip php-NN.tar.gz
    4. tar -xvf php-NN.tar
    5. cd httpd-2_0_NN
    6. ./configure --enable-so
    7. make
    8. make install

    现在已经将 Apache 2.0.NN 安装在 /usr/local/apache。本安装支持可装载模块
    和标准的 MPM prefork。之后,可以使用如下命令启动 Apache 服务器:
    /usr/local/apache2/bin/apachectl start
    如果成功,可以停止 Apache 服务器并继续安装 PHP:
    /usr/local/apache2/bin/apachectl stop.

    9. cd ../php4-NN

    10. 现在需要配置 PHP。在这里可以用各种各样的参数来自定义PHP,例如启动哪些
    扩展功能包的支持等。用 ./configure --help 命令可以列出当前可用的所有参
    数。在此例中,将给出一个在有 MySQL 支持的 Apache 2 上进行配置的范例。
    用户本地的 apxs 的路径可能会不同,事实上,在用户的系统中,它可能被命名为
    apxs2。

    ./configure --with-apxs2=/usr/local/apache2/bin/apxs

    11. make
    12. make install

    如果决定在安装后改变配置选项,只需重复最后的三步,然后需要重新启动 Apache
    使新模块生效。无需重新编译。

    请注意,除非明确有提示,否则“make install”命令将安装 PEAR、各种 PHP 工具
    诸如 phpize,安装 PHP CLI 等等。

    13. 配置 php.ini

    cp php.ini-dist /usr/local/lib/php.ini

    可以编辑 php.ini 文件以修改 PHP 的选项。如果想要把此文件放到另外的位置,
    需要在步骤 10 添加 --with-config-file-path=/path 选项。

    如果选择 php.ini -recommended,请务必阅读其中的变更的列表,它们将影响
    PHP 的执行。

    14. 编辑 httpd.conf 文件以调用 PHP 模块。LoadMolude 表达式右边的路径必须指向
    系统中的 PHP。以上的 make install 命令可能已经完成了这些,但务必要检查。

    对于 PHP 4:
    LoadModule php4_module modules/libphp4.so

    对于 PHP 5:
    LoadModule php5_module modules/libphp5.so

    15. 告知 Apache 将特定的扩展名解析成 PHP,例如,让 Apache 将扩展名 .php
    解析成 PHP。可以将任何扩展名指定为 PHP,只需添加它们,每一个用空格分隔。
    例如,要添加 .phtml:

    AddType application/x-httpd-php .php .phtml

    通常还将 .phps 扩展名设置成显示高亮的 PHP 源文件,可以这样来完成:

    AddType application/x-httpd-php-source .phps

    16. 启动 Apache 服务器:
    /usr/local/apache2/bin/apachectl start


  2. Windows下安装:
    将以下两行加入到 Apache 的 httpd.conf 配置文件中设定 Apache 2.0 的 PHP 模块

    # 对 PHP 4 用这两行:
    LoadModule php4_module "c:/php/php4apache2.dll"
    # 别忘了从 sapi 目录中把 php4apache2.dll 拷贝出来!
    AddType application/x-httpd-php .php

    # 对 PHP 5 用这两行:
    LoadModule php5_module "c:/php/php5apache2.dll"
    AddType application/x-httpd-php .php

    # 配置 php.ini 的路径
    PHPIniDir "C:/php"




参考资料:
http://www.php.net/manual/zh/install.php

2007年3月18日星期日

概念: 构造函数和析构函数

构造函数和析构函数
构造函数
void __construct ( [mixed args [, ...]] )

PHP 5 允行开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。

注: 如果子类中定义了构造函数则不会暗中调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用 parent::__construct()。

例子 19-6. 使用新标准的构造函数
<?php
class BaseClass {
function __construct() {
print "In BaseClass constructor\n";
}
}

class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "In SubClass constructor\n";
}
}

$obj = new BaseClass();
$obj = new SubClass();
?>


为了实现向后兼容性,如果 PHP 5 在类中找不到 __construct() 函数,它就会尝试寻找旧式的构造函数,也就是和类同名的函数。因此唯一会产生兼容性问题的情况是:类中已有一个名为 __construct() 的方法,但它却又不是构造函数。
析构函数
void __destruct ( void )

PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

例子 19-7. 析构函数示例
<?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}

function __destruct() {
print "Destroying " . $this->name . "\n";
}
}

$obj = new MyDestructableClass();
?>


和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()。

注: 析构函数在脚本关闭时调用,此时所有的头信息已经发出。

注: 试图在析构函数中抛出一个异常会导致致命错误。

2007年1月29日星期一