4

用php5.3的namespace实现类的无痛继承

 2 years ago
source link: https://limboy.me/posts/php-namespace-class-extend/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

用php5.3的namespace实现类的无痛继承

2011-03-24

标题有点怪异,先来说说正常的继承会有什么问题。假设你一个应用的 Controller 多次用到了 View 类,就像这样

<?php

class Controller_Hello
{
	public function action_index()
	{
		//...
		$view = new View('index.tpl');
		$view->render();
	}

	public function action_edit($id)
	{
		//...
		$view = new View('edit.tpl');
		$view->render();
	}

	//...
}

这个 View 是框架提供的,假如某一天发现 View 类需要新添加一个方法,最常用的就是新建一个自定义的 View 类继承框架的 View 类

<?php

class My_View extends View
{
	public function newMethod()
	{
		//...
	}
}

这时以前使用 View 类的地方就要全部变成 My_View,这是比较恐怖的。很多框架也都提供了解决方法,大体有三种

这是 Kohana2 采用的方法,就是系统类命名为 XXX_Core,然后调用的时候在 autoload 处,动态 eval 出一个 XXX class,就像这样

<?php
// system class
class View_Core
{
	//...
}

// autoload
public function autoload($class)
{
	// first look into app dir
	// then look into modules dir
	// last look into system dir
	require '/path/to/'.$class.'.php';
	eval('class '.$class.' extends '.$class.'_Core');
}

如果用户需要对该类添加新的方法,可以在 app/classes 里定义新的 View 类,同时继承 View_Core 类,这样使用时,因为优先级的原因,View 类名可以保持不变

<?php

class View extends View_Core
{
	public function newMethod()
	{
		//...
	}
}

因为使用了 eval,所以不够优雅,而且有安全隐患

这是 Kohana3 的做法,具体如下

<?php

// system/classes/view.php
// 是的,就这么一句话
class View extends Kohana_View {}

// system/classes/kohana/view.php
class Kohana_View
{
	//...
}

根据优先级,最后会找到 system/classes/view.php 定义的 View 类。如果需要自己扩展 Kohana_View 类,可以在 app/classes 目录里新建一个 view.php

<?php

class View extends Kohana_View
{
	// add your method
}

这样框架就会先找到 app/classes/view.php 而不是 system/classes/view.php,自定义 View 类生效,同时原先使用的 View 类也不需要做调整

这么做的缺点就是 system/classes 目录下会有大量的空壳类,有点累赘

attach behavior

这是 yii 采用的方法,简单说来就是通过 attachBehavior 方法,动态地给某个类添加新的功能

<?php

class SomeComponent extends Component
{
	//...
}

class SomeBehavior extends CBehavior
{
	public function addWidth($width)
	{
		$this->Owner->width += $width;
	}
}

$sc = new SomeComponent();
$sc->attachBehavior('sb', 'SomeBehavior');
$sc->addWidth(100);

需要实例化后动态调用 attachBehavior 方法,有点麻烦。而且不能使用父类的 protected 属性和方法。

用 namespace 实现无痛继承

所谓的无痛继承就是不用修改原先的类名,没有多余的空壳类,没有 eval,不用 attachBehavior,只要修改'use'就行了。代码如下

<?php

// app/lib1.php
namespace App\Lib1;

class Controller
{
	public function before()
	{
		echo 'lib1\'s before';
	}
}

定义了一个 Controller 类,使用时:

<?php

require 'lib/lib1.php';

use App\Lib1\Controller;

$c = new Controller();
$c->before();

// output: lib1's before

现在要有一个新的 controller 继承 lib1.php 的 Controller,如下

<?php

namespace App\Lib2;
use App\Lib1;

class Controller extends Lib1\Controller
{
	public function before()
	{
		echo 'lib2\'s before';
	}
}

使用时,只要将 use App\Lib1\Controller 改为 use App\Lib2\Controller 就行了

<?php
// 可以通过设置autoload来解决require的问题
require 'lib/lib1.php';
require 'lib/lib2.php';

use App\Lib2\Controller;

$c = new Controller();
$c->before();

是不是很方便


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK