为了学习理解依赖注入和路由,自己撸了一个PHP框架

如何提高自己编写代码的能力呢?作为web开发者,我们通常都是基于面向对象OOP来开发的,所以面向对象的设计能力或者说设计模式的运用能力尤为重要,当然还有开发语言本身特性和基础的灵活运用。
我们可以去阅读一些优秀的开源项目,理解里面的代码设计,去学习和造轮子来提高自己。
我比较关注web framework中的路由、HTTP、依赖注入容器这几部分,路由和http处理是web框架必不可少的,整个框架的服务对象依赖解析也是很重要的,有了依赖注入容器可以实现类很好的解耦。
Dependency Injection Container
先来说下什么是依赖注入,依赖注入是一种允许我们从硬编码的依赖中解耦出来,从而在运行时或者编译时能够修改的软件设计模式(来自维基百科 Wikipedia)。依赖注入通过构造注入,函数调用或者属性的设置来提供组件的依赖关系。
下面的代码中有一个 Database 的类,它需要一个适配器来与数据库交互。我们在构造函数里实例化了适配器,从而产生了耦合。这会使测试变得很困难,而且 Database 类和适配器耦合的很紧密。
<?php
namespace Database;

class Database
{
protected $adapter;

public function __construct()
{
$this->adapter = new MySqlAdapter;
}
}

class MysqlAdapter {}
这段代码可以用依赖注入重构,从而解耦
<?php
namespace Database;

class Database
{
protected $adapter;

public function __construct(MySqlAdapter $adapter)
{
$this->adapter = $adapter;
}
}

class MysqlAdapter {}
现在我们通过外界给予 Database 类的依赖,而不是让它自己产生依赖的对象。我们甚至能用可以接受依赖对象参数的成员函数来设置,或者如果 $adapter 属性本身是 public的,我们可以直接给它赋值。
根据依赖注入的概念,我们的框架实现了这些特性。
Dependency injection Container基于PSR-11规范实现,包括3种注入实现方式:构造方法注入(Constructor Injection)、setter方法或属性注入(Setter Injection)、匿名回调函数注入,代码如下:
1.构造方法注入(Constructor Injection)
<?php
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.方法注入
<?php
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.匿名回调函数注入
<?php
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)
<?php
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)。
<?php
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;
});

$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/…

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理