资讯专栏INFORMATION COLUMN

通过实现依赖注入和路由,构建一个自己的现代化PHP框架

waltr / 2841人阅读

摘要:依赖注入通过构造注入,函数调用或者属性的设置来提供组件的依赖关系。这段代码可以用依赖注入重构,从而解耦现在我们通过外界给予类的依赖,而不是让它自己产生依赖的对象。根据依赖注入的概念,我们的框架实现了这些特性。

如何提高自己编写代码的能力呢?我们首先想到的是阅读学习优秀的开源项目,然后写一个自己的web框架或类库组件。作为web开发者,我们通常都是基于面向对象OOP来开发的,所以面向对象的设计能力或者说设计模式的运用能力尤为重要,当然还有开发语言本身特性和基础的灵活运用。

我们可以去阅读一些优秀的开源项目,理解里面的代码设计,去学习和造轮子来提高自己。

在优秀成熟的web framework中,路由和http处理是web框架必不可少的,整个框架的服务对象依赖解析也是很重要的,有了依赖注入容器可以实现类很好的解耦。

依赖注入容器 Dependency Injection Container

先来说下什么是依赖注入,依赖注入是一种允许我们从硬编码的依赖中解耦出来,从而在运行时或者编译时能够修改的软件设计模式(来自维基百科 Wikipedia)。
依赖注入通过构造注入,函数调用或者属性的设置来提供组件的依赖关系。

下面的代码中有一个 Database 的类,它需要一个适配器来与数据库交互。我们在构造函数里实例化了适配器,从而产生了耦合。这会使测试变得很困难,而且 Database 类和适配器耦合的很紧密。

adapter = new MySqlAdapter;
    }
}

class MysqlAdapter {}

这段代码可以用依赖注入重构,从而解耦

adapter = $adapter;
    }
}

class MysqlAdapter {}

现在我们通过外界给予 Database 类的依赖,而不是让它自己产生依赖的对象。我们甚至能用可以接受依赖对象参数的成员函数来设置,或者如果 $adapter 属性本身是 public的,我们可以直接给它赋值。

根据依赖注入的概念,我们的框架实现了这些特性。

Dependency injection Container基于PSR-11规范实现,使用了PHP的类反射功能,去实例化类定义的对象依赖。定义类的对象依赖包括3种注入实现方式:构造方法注入(Constructor Injection)、setter方法或属性注入(Setter Injection)、匿名回调函数注入,代码示例如下:

1.构造方法注入(Constructor Injection)
bar = $bar;
    }
}

/*class Bar {

}*/

class Bar {
    public $baz;

    public function __construct(Baz $baz)
    {
        $this->baz = $baz;
    }
}

class Baz {

}

$container = new Container;
$container->set(Foo::class)->addArguments(Bar::class);
$container->set(Bar::class)->addArguments(Baz::class);

$foo = $container->get(Foo::class);

var_dump($foo, $foo->bar);
var_dump($foo instanceof Foo);  // true
var_dump($foo->bar instanceof Bar); // true
var_dump($foo->bar->baz instanceof Baz); // true
2.方法注入(Setter Injection)
model = $model;
    }
}

class Model
{
    public $pdo;

    public function setPdo(PDO $pdo)
    {
        $this->pdo = $pdo;
    }
}

$container = new Container;

$container->set(Controller::class)->addArguments(Model::class);
$container->set(Model::class)->addInvokeMethod("setPdo", [PDO::class]);

$container->set(PDO::class)
    ->addArguments(["mysql:dbname=test;host=localhost", "root", "111111"]);

$controller = $container->get(Controller::class);

var_dump($controller instanceof Controller); // true
var_dump($controller->model instanceof Model); // true
var_dump($controller->model->pdo instanceof PDO); // true
3.匿名回调函数注入(Closure callable Injection)
model = $model;
    }
}

class Model
{
    public $pdo;

    public function setPdo(PDO $pdo)
    {
        $this->pdo = $pdo;
    }
}

$container = new Container;

$container->set(Controller::class, function () {

    $pdo   = new PDO("mysql:dbname=test;host=localhost", "root", "111111");
    $model = new Model;

    $model->setPdo($pdo);

    return new Controller($model);
});

$controller = $container->get(Controller::class);

var_dump($controller instanceof Controller); // true
var_dump($controller->model instanceof Model); // true
var_dump($controller->model->pdo instanceof PDO); // true
自动装配(auto wiring)
bar = $bar;
        $this->baz = $baz;
    }
}

class Bar
{
    /**
     * @var AutoWiringBam
     */
    public $bam;

    /**
     * Construct.
     *
     * @param AutoWiringBam $bam
     */
    public function __construct(Bam $bam)
    {
        $this->bam = $bam;
    }
}

class Baz
{
    // ..
}

class Bam
{
    // ..
}

$container = new ContainerBuilder;
$container = $container->build();

$foo = $container->get(Foo::class);

var_dump($foo instanceof Foo);           // true
var_dump($foo->bar instanceof Bar);      // true
var_dump($foo->baz instanceof Baz);      // true
var_dump($foo->bar->bam instanceof Bam); // true
路由 Route

再介绍下路由的使用,route可以使用symfony的http foundation组件来处理HTTP请求(http messages)。

get("/articles", function () {
    return "This is articles list";
});

$router->get("/articles/{id:d+}", function ($id) {
    return "Article id: " . $id;
});

/* title为可选参数 */
$router->get("/articles/{id:d+}[/{title}]", function ($id, $title) {
    return "Article id: " . $id . ", title: " . $title;
});

/*匹配处理路由组*/
$router->group("/articles", function () use ($router) {
    $router->get("/list", function() {
        return "This is articles list";
    });

    $router->get("/detail", function ($id, $title) {
        return "Article detail id: " . $id . ", title: " . $title;
    });
});

$request = new Request();
$routeHandler = $router->getRouteHandler();
$response = $routeHandler->handle($request);
echo $response;

其它的ORM、cache、filesystem、session、validation等组件可以使用composer来由用户自由扩展。

项目地址 https://github.com/parvinShi/...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/30992.html

相关文章

  • 前端面试题(3)现代技术

    摘要:什么是单页面应用单页面应用是指用户在浏览器加载单一的页面,后续请求都无需再离开此页目标旨在用为用户提供了更接近本地移动或桌面应用程序的体验。流程第一次请求时,将导航页传输到客户端,其余请求通过获取数据实现数据的传输通过或远程过程调用。 什么是单页面应用(SPA)? 单页面应用(SPA)是指用户在浏览器加载单一的HTML页面,后续请求都无需再离开此页 目标:旨在用为用户提供了更接近本地...

    EasonTyler 评论0 收藏0
  • 前端面试题(3)现代技术

    摘要:什么是单页面应用单页面应用是指用户在浏览器加载单一的页面,后续请求都无需再离开此页目标旨在用为用户提供了更接近本地移动或桌面应用程序的体验。流程第一次请求时,将导航页传输到客户端,其余请求通过获取数据实现数据的传输通过或远程过程调用。 什么是单页面应用(SPA)? 单页面应用(SPA)是指用户在浏览器加载单一的HTML页面,后续请求都无需再离开此页 目标:旨在用为用户提供了更接近本地...

    AaronYuan 评论0 收藏0
  • 前端面试题(3)现代技术

    摘要:什么是单页面应用单页面应用是指用户在浏览器加载单一的页面,后续请求都无需再离开此页目标旨在用为用户提供了更接近本地移动或桌面应用程序的体验。流程第一次请求时,将导航页传输到客户端,其余请求通过获取数据实现数据的传输通过或远程过程调用。 什么是单页面应用(SPA)? 单页面应用(SPA)是指用户在浏览器加载单一的HTML页面,后续请求都无需再离开此页 目标:旨在用为用户提供了更接近本地...

    trigkit4 评论0 收藏0
  • 从0开始构建一个属于你自己PHP框架

    摘要:如何构建一个自己的框架为什么我们要去构建一个自己的框架可能绝大多数的人都会说市面上已经那么多的框架了,还造什么轮子。 showImg(https://segmentfault.com/img/bVNg9F?w=500&h=500); 如何构建一个自己的PHP框架 为什么我们要去构建一个自己的PHP框架?可能绝大多数的人都会说市面上已经那么多的框架了,还造什么轮子?。我的观点造轮子不是目...

    vpants 评论0 收藏0
  • Laravel中核心概念

    摘要:可以为服务提供者的方法设置类型提示。方法将在所有其他服务提供者均已注册之后调用。所有服务提供者都在配置文件中注册。可以选择推迟服务提供者的注册,直到真正需要注册绑定时,这样可以提供应用程序的性能。 本文最早发布于 Rootrl的Blog 导言 Laravel是一款先进的现代化框架,里面有一些概念非常重要。在上手Laravel之前,我认为先弄懂这些概念是很有必要的。你甚至需要重温下PHP...

    ddongjian0000 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<