Web框架与太阳系

古语有云:工欲善其事,必先利其器。对于Web开发亦是如此,不过现在的Web框架实在是太多了!以PHP为例,有CakePHPCodeIgniterSymfonyZendYii等等,到底谁是最合适的?事实上过多的选择往往会让人陷入「乱花渐欲迷人眼」的窘境,这些年我一直游走在各种PHP框架之间,却始终没有觅得属于自己的屠龙刀,于是我决定自己动手,就像歌里唱的那样:不是你亲手点燃的那就不能叫做火焰。

既然要自己动手,那么就需要明确一下设计目标,我个人主要关注以下几个方面:微内核、模块化、扩展性。套用陈道明说过的一句广告语:简约而不简单。

在说明设计方案之前,不妨放松一下心情,聊聊看似风马牛不相及的太阳系:

Solar System

Solar System

在我小时候,课本上教的是太阳系有九大行星,分别是:水星、金星、地球、火星、木星、土星、天王星、海王星和冥王星。其中,冥王星较小,后来,人们发现还有一些和冥王星类似的天体也在围绕太阳运转,如果冥王星被划为行星的话,那么这些天体无疑也属于行星,此时太阳系到底有几大行星的问题彻底让天文学家蒙圈了,最后他们耍了一个小聪明,剥夺冥王星的行星资格就行了,从此太阳系就只剩八大行星了。

当然,我并不是让你关注这些天文学上的八卦往事,实际上我希望你关注的是太阳系的结构模式:有的行星是丁克家庭,没有卫星,比如水星和金星;有的行星是计划生育标兵,比如地球只有一个卫星(月亮);有的行星是超生游击队,卫星多的数不胜数,以至于起名时只能以阿拉伯数字加以区分,比如木星和土星。卫星沿着自己的轨道围绕着行星旋转,行星沿着自己的轨道围绕着太阳旋转,这就是太阳系!

如果你了解设计模式的话,那么你会惊讶的发现,上帝是一个设计模式大师,他在设计宇宙的结构时,使用了装饰器模式:月亮装饰着地球,地球装饰着太阳,甚至太阳系本身也是银河系的一个装饰器,如此循环,这就是宇宙!

既然上帝只用了装饰器这一个概念便创造了整个世界,那么我们能不能利用装饰器模式创造Web框架呢?实际上类似的框架在Python社区中早就有了,可惜PHP社区却好像始终无动于衷,我在几年前做过一些粗浅的尝试,但是由于种种原因搁置了,最近借着公司一个项目的改版,我终于完成了它,并命名为「Beahoo」,它是一个迷你框架,代码极简,很好的诠释了我前面提的目标:微内核、模块化、扩展性。

我们先来看看控制器中Action的代码实现:

<?php

namespace Beahoo\Controller;

abstract class Action
{
    protected $decorators = array();

    public function build(Action $action = null)
    {
        if ($action === null) {
            $action = $this;
        }

        $decorators = array();

        for (
            $class = get_class($action);
            $vars  = get_class_vars($class);
            $class = get_parent_class($class)) {

            if (empty($vars['decorators'])) {
                continue;
            }

            foreach ($vars['decorators'] as $decorator => $name) {
                if (is_int($decorator)) {
                    list($decorator, $name) = array($name, null);
                }

                if (!isset($decorators[$decorator])) {
                    $decorators[$decorator] = $name;
                }
            }
        }

        $core = $action;

        $decorators = array_reverse($decorators);

        foreach ($decorators as $decorator => $name) {
            if ($name === false) {
                continue;
            }

            $decorator = $this->load($decorator, $action);
            $action = $this->build($decorator);

            if ($name) {
                $core->{$name} = $decorator;
            }
        }

        return $action;
    }

    protected function load($class, $args = null)
    {
        if ($args === null) {
            return new $class;
        }

        if (!is_array($args)) {
            $args = array($args);
        }

        $reflection = new \ReflectionClass($class);

        return $reflection->newInstanceArgs($args);
    }

    abstract public function execute(
        Request $request,
        Response $response
    );
}

我们再来看看控制器中Decorator的代码实现:

<?php

namespace Beahoo\Controller;

abstract class Decorator extends Action
{
    protected $action;

    public function __construct(Action $action)
    {
        $this->action = $action;
    }

    public function getNextAction()
    {
        return $this->action;
    }

    public function getLastAction()
    {
        $action = $this->action;

        while (is_subclass_of($action, __CLASS__)) {
            $action = $action->action;
        }

        return $action;
    }

    public function execute(Request $request, Response $response)
    {
        $this->action->execute($request, $response);
    }
}

本文省略了细枝末节的代码实现,只保留了最关键的Action和Decorator,几百行代码就实现了DNA双螺旋结构,只要有了它们,便可以衍生出丰富多彩的生命形式。

让我们扮演一把上帝,看看如何利用装饰器模式创建太阳系:

<?php

namespace SolarSystem\Controller\Action;

use Beahoo\Controller\Request;
use Beahoo\Controller\Response;

class SunAction extends \Beahoo\Controller\Action
{
    protected $decorators = array(
        'SolarSystem\Controller\Decorator\NeptuneDecorator',
        'SolarSystem\Controller\Decorator\UranusDecorator',
        'SolarSystem\Controller\Decorator\SaturnDecorator',
        'SolarSystem\Controller\Decorator\JupiterDecorator',
        'SolarSystem\Controller\Decorator\MarsDecorator',
        'SolarSystem\Controller\Decorator\EarthDecorator',
        'SolarSystem\Controller\Decorator\VenusDecorator',
        'SolarSystem\Controller\Decorator\MercuryDecorator',
    );

    public function execute(Request $request, Response $response)
    {
        // ...
    }
}

装饰器本身也可以被装饰器装饰,比如地球和月球的关系就是个极好的例子:

<?php

namespace SolarSystem\Controller\Decorator;

use Beahoo\Controller\Request;
use Beahoo\Controller\Response;

class EarthDecorator extends \Beahoo\Controller\Decorator
{
    protected $decorators = array(
        'SolarSystem\Controller\Decorator\Earth\MoonDecorator',
    );

    public function execute(Request $request, Response $response)
    {
        // ...
    }
}

怎么样?是不是有种造物主的成就感,太阳系就在我们手中,设想一下:如果有一颗彗星正在穿越太阳系会发生什么情景,那么就让我们运行它试试吧:

<?php

$sun = new \SolarSystem\Controller\Action\SunAction;
$sun->build()->execute($request, $response);

虽然本框架在代码实现上秉承了极简的原则,但是在功能上却毫不逊色,篇幅所限,我无法一一说明,有兴趣的读者不妨自己探索一下吧。

Web框架与太阳系》上有13条评论

  1. 我理解,框架分为两种,一种是架构框架,一种是业务框架,架构框架就如Symfony,Yii这种,也是现在市面最多的,但是感觉在这个层面上,还需要有若干的业务框架,比如CMS,比如监控系统。如果一个phper能熟练使用这些业务框架,应用会更适合PHP这种快粗壮的理念。

  2. 巧了,最近我刚好也自己实现了一套PHP框架,起名为Sail,不过犹豫之后没有使用namespace,感觉使用前其实先include了一遍,实在不喜欢。我的代码已经上传到github,地址是:https://github.com/lmyoaoa/sail

    假如博主或其他朋友有兴趣可以帮看看,要是有什么意见或者建议,球指教哈~

发表评论

电子邮件地址不会被公开。 必填项已用*标注