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

news/2024/7/7 21:32:39

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

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

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

依赖注入容器 Dependency Injection Container

先来说下什么是依赖注入,依赖注入是一种允许我们从硬编码的依赖中解耦出来,从而在运行时或者编译时能够修改的软件设计模式(来自维基百科 Wikipedia)。

依赖注入通过构造注入,函数调用或者属性的设置来提供组件的依赖关系。

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

namespace Database;

class Database

{

protected $adapter;

public function __construct()

{

$this->adapter = new MySqlAdapter;

}

}

class MysqlAdapter {}

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

namespace Database;

class Database

{

protected $adapter;

public function __construct(MySqlAdapter $adapter)

{

$this->adapter = $adapter;

}

}

class MysqlAdapter {}

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

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

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

1.构造方法注入(Constructor Injection)

declare(strict_types=1);

namespace Examples;

use Eagle\DI\Container;

class Foo

{

/**

* @var \Examples\Bar

*/

public $bar;

/**

* Foo constructor.

* @param \Examples\Bar $bar

*/

public function __construct(Bar $bar)

{

$this->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)

declare(strict_types=1);

namespace Examples;

require 'vendor/autoload.php';

use Eagle\DI\Container;

class Controller

{

public $model;

public function __construct(Model $model)

{

$this->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)

declare(strict_types=1);

namespace Examples;

require 'vendor/autoload.php';

use Eagle\DI\Container;

class Controller

{

public $model;

public function __construct(Model $model)

{

$this->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)

declare(strict_types=1);

namespace AutoWiring;

require 'vendor/autoload.php';

use Eagle\DI\ContainerBuilder;

class Foo

{

/**

* @var \AutoWiring\Bar

*/

public $bar;

/**

* @var \AutoWiring\Baz

*/

public $baz;

/**

* Construct.

*

* @param \AutoWiring\Bar $bar

* @param \AutoWiring\Baz $baz

*/

public function __construct(Bar $bar, Baz $baz)

{

$this->bar = $bar;

$this->baz = $baz;

}

}

class Bar

{

/**

* @var \AutoWiring\Bam

*/

public $bam;

/**

* Construct.

*

* @param \AutoWiring\Bam $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)。

require 'vendor/autoload.php';

use Eagle\Route\Router;

use Symfony\Component\HttpFoundation\Request;

$router = new Router();

$router->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来由用户自由扩展。


http://lihuaxi.xjx100.cn/news/236393.html

相关文章

UESTC 1726 整数划分(母函数)

题目链接&#xff1a;http://222.197.181.5/problem.php?pid1726 题意&#xff1a;求n的划分数。一种划分方案中不能有相同的数字。 思路&#xff1a;(1x)(1x^2)(1x^3)……(1x^1000). int f[N];void init() {f[1]1;int a[N]{0};a[0]1; a[1]1;int i,j;for(i2;i<1000;i){for(…

MySQL如何判别InnoDB表是独立表空间还是共享表空间

InnoDB采用按表空间&#xff08;tablespace)的方式进行存储数据, 默认配置情况下会有一个初始大小为10MB&#xff0c; 名字为ibdata1的文件&#xff0c; 该文件就是默认的表空间文件&#xff08;tablespce file&#xff09;&#xff0c;用户可以通过参数innodb_data_file_path对…

php webuploader大文件,web uploader 上传大文件总结

由于业务需要&#xff0c;需要上传大文件&#xff0c;已有的版本无法处理IE版本&#xff0c;经过调研&#xff0c;百度的 webuploader 支持 IE 浏览器&#xff0c;而且支持计算MD5值&#xff0c;进而可以实现秒传的功能。大文件上传主要分为三部分&#xff0c;预上传&#xff0…

文件夹浏览(SHBrowseForFolder)

from http://www.cnblogs.com/Clingingboy/archive/2011/04/16/2018284.html 一.首先要为SHBrowseForFolder准备一个结构体BROWSEINFO typedef struct _browseinfoW {HWND hwndOwner;PCIDLIST_ABSOLUTE pidlRoot;LPWSTR pszDisplayName; // Return display…

php dropdownlist,为何activitieDropdownlist的值始终无法获取到

为什么activitieDropdownlist的值始终无法获取到&#xff1f;视图层view控制器层public function actionDbdplist(){$m_subjectlist new SubjectList();$m_gradelist new Grade;$this->subject_list SubjectList::model()->findAll();$this->grade_list Grade::mo…

红芯丑闻揭秘者 Touko 专访 | 关于红芯丑闻的更多内幕……

专栏 | 九章算法 网址 | www.jiuzhang.com ❤ 红芯事件 近日&#xff0c;一则《自主研发的国产浏览器内核&#xff0c;红芯宣布获2.5亿C轮融资》的讯息再次将“国产自主创新”这一话题推向高潮&#xff0c;希冀之声群起。然好景不长&#xff0c;网友Touko在将红芯浏览器的exe文…

VirtuaNES 模拟器 玩FC小游戏 热血系列集合

2019独角兽企业重金招聘Python工程师标准>>> 下载VirtuaNES http://www.emulator-zone.com/doc.php/nes/virtuanes.html 解压后 rom 设置键位 支持手柄 打开rom即可玩耍 完整模拟器游戏rom下载 链接&#xff1a;https://pan.baidu.com/s/1qydlBPI1FOi8BSMA6dlCqA 提…

学习文章

2019独角兽企业重金招聘Python工程师标准>>> 阮一峰 http://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html 转载于:https://my.oschina.net/u/2285087/blog/2999696