自制 PHP 小框架 FanlyPHP 之插件选择

Posted coding01

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自制 PHP 小框架 FanlyPHP 之插件选择相关的知识,希望对你有一定的参考价值。

本文 2780字,需要 17 分钟

参考 Laravel 整个源代码,可以发现 Laravel 框架借用了很多第三方优质插件:

"require": {
"php": "^7.1.3",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"doctrine/inflector": "^1.1",
"dragonmantank/cron-expression": "^2.0",
"egulias/email-validator": "^2.0",
"erusev/parsedown": "^1.7",
"league/flysystem": "^1.0.8",
"monolog/monolog": "^1.12",
"nesbot/carbon": "^1.26.3 || ^2.0",
"opis/closure": "^3.1",
"psr/container": "^1.0",
"psr/simple-cache": "^1.0",
"ramsey/uuid": "^3.7",
"swiftmailer/swiftmailer": "^6.0",
"symfony/console": "^4.2",
"symfony/debug": "^4.2",
"symfony/finder": "^4.2",
"symfony/http-foundation": "^4.2",
"symfony/http-kernel": "^4.2",
"symfony/process": "^4.2",
"symfony/routing": "^4.2",
"symfony/var-dumper": "^4.2",
"tijsverkoyen/css-to-inline-styles": "^2.2.1",
"vlucas/phpdotenv": "^3.3"},

通过不同插件的作用,来构造满足自己框架设计的目标。今天我也沿用这个做法,寻找一些不错的 PHP 插件。


结构
 


正如之前所说,我给这个自制小框架取名:FanlyPHP。

自制 PHP 小框架 FanlyPHP 之插件选择

首先,public 文件夹用于存放入口文件 index.php,写个 demo:

<?php

echo "你好,FanlyPHP";

使用 php -S localhost:1234 命令执行看看效果:

自制 PHP 小框架 FanlyPHP 之插件选择


composer.json
 


创建 composer.json 文件用于添加我们的第三方插件。

用命令行 composer init 根据提示初始化 composer.json 内容:

{
"name": "coding01/fanlyphp",
"description": "The Coding01 Framework for PHP.",
"type": "project",
"license": "MIT",
"authors": [
{
"name": "coding01",
"email": "yemeishu@126.com"
}
]
,
"require": {}
}

composer install 就会多一个 vendor 文件夹用于存放第三方插件。

自制 PHP 小框架 FanlyPHP 之插件选择

  1. 创建 app 文件夹用于存放我们的代码,和 Laravel 的结构相似。并引入进 composer.json,按 psr-4 自动加载。记得要执行下命令:composer dump-autoload。


安装插件
 


下面我们一个个来分析这几个插件的使用。


league/container
 


A simple but powerful dependency injection container.

http://container.thephpleague.com/

这个插件主要会引入 DI Container,也是本文推荐的,而且 nikic/fast-route 也是 Lumen 使用的。

写个 demo

先创建一个service demo 类

<?php
/**
* User: yemeishu
* Date: 2019/5/15
* Time: 下午8:37
*/

namespace App;

class ServiceDemo{
public function hello()
{
return "叶梅树的 service demo";
}
}

 index.php 引入 autoload.php,这样就可以自动发现 composer 第三方包。

<?php

require_once __DIR__.'/../vendor/autoload.php';

$container = new LeagueContainerContainer;

// add a service to the container
$container->add('service', 'AppServiceDemo');

// retrieve the service from the container
$service = $container->get('service');
echo $service->hello();

$demo = new AppServiceDemo();
echo $demo->hello();

执行结果:

自制 PHP 小框架 FanlyPHP 之插件选择

可以看出利用 Container  new AppServiceDemo(),效果一致。


league/route
 


Route is a fast routing/dispatcher package enabling you to build well designed performant web apps. At its core is Nikita Popov’s FastRoute package allowing this package to concentrate on the dispatch of your controllers.

http://route.thephpleague.com/

composer require league/route

自制 PHP 小框架 FanlyPHP 之插件选择

根据官网的提示,还需要安装一个插件:

composer require zendframework/zend-diactoros

写个 demo

在上文例子的基础上,增加 route 的代码,具体看代码,比较简单:

<?php

use PsrHttpMessageResponseInterface;
use PsrHttpMessageServerRequestInterface;

require_once __DIR__.'/../vendor/autoload.php';

$container = new LeagueContainerContainer;

// add a service to the container
$container->add('service', 'AppServiceDemo');

// retrieve the service from the container
$service = $container->get('service');

$container->share('response', ZendDiactorosResponse::class);
$container->share('request', function () {
return ZendDiactorosServerRequestFactory::fromGlobals(
$_SERVER,
$_GET,
$_POST,
$_COOKIE,
$_FILES
);
});

$container->share('emitter', ZendDiactorosResponseSapiEmitter::class);

$route = new LeagueRouteRouteCollection($container);

$route->map(
'GET',
'/demo',
function (ServerRequestInterface $request, ResponseInterface $response
)
use ($service)
{
$hello = $service->hello();
$response->getBody()->write("<h1>$hello</h1>");

return $response;
}
);

$response = $route->dispatch($container->get('request'), $container->get('response'));

$container->get('emitter')->emit($response);

看效果,路由指定到 http://localhost:1234/demo

自制 PHP 小框架 FanlyPHP 之插件选择


LeaguePipeline
 


This package provides a plug and play implementation of the Pipeline Pattern. It’s an architectural pattern which encapsulates sequential processes. When used, it allows you to mix and match operation, and pipelines, to create new execution chains. The pipeline pattern is often compared to a production line, where each stage performs a certain operation on a given payload/subject. Stages can act on, manipulate, decorate, or even replace the payload.

If you find yourself passing results from one function to another to complete a series of tasks on a given subject, you might want to convert it into a pipeline.

https://pipeline.thephpleague.com/

// 安装插件
composer require league/pipeline

写个 demo

use LeaguePipelinePipeline;

// 创建两个闭包函数
$pipe1 = function ($payload) {
return $payload + 1;
};

$pipe2 = function ($payload) {
return $payload * 3;
};

$route->map(
'GET',
'/demo',
function (ServerRequestInterface $request, ResponseInterface $response
)
use ($service, $pipe1, $pipe2)
{
$params = $request->getQueryParams();

// 引入闭包函数
$pipeline = (new Pipeline)
->pipe($pipe1)
->pipe($pipe2);

// 执行
$callback = $pipeline->process($params['data']);

$hello = $service->hello();
$response->getBody()->write("<h1>$hello, $callback</h1>");

return $response;
}
);

自制 PHP 小框架 FanlyPHP 之插件选择

自制 PHP 小框架 FanlyPHP 之插件选择


ENV
 


与 Laravel 相似,我们借助 .env 来保存我们的配置信息。

安装插件

composer require vlucas/phpdotenv

写个 demo

$dotenv = new DotenvDotenv(__DIR__."/../");
$dotenv->load();

$route->map(
'GET',
'/env_demo',
function (ServerRequestInterface $request, ResponseInterface $response
)
{
$data = getenv('hello');
$response->getBody()->write("<h1>使用 env</h1>");
$response->getBody()->write("<p>$data</p>");
return $response;
}
);

看看 .env 定义的值

hello=Fanly

看执行结果:

自制 PHP 小框架 FanlyPHP 之插件选择


webonyx/graphql-php
 


GraphQL 是一种现代化的 HTTP API 接口构建方式,是 Facebook 在 2012 年开发的,2015 年开源,2016 年下半年 Facebook 宣布可以在生产环境使用,而其内部早就已经广泛应用了。GraphQL 是作为一个 REST 和 SOAP API 的替代品来设计的,Facebook 应对复杂接口查询的方案非常简单:用一个 “聪明” 的节点来进行复杂查询,将数据按照客户端的要求传回去,后端根据 GraphQL 机制提供一个具有强大功能的接口,用以满足前端数据的个性化需求,既保证了多样性,又控制了接口数量。

参考:https://laravel-china.org/docs/graphql-php

安装插件

composer require webonyx/graphql-php

写个 demo

在上面的例子,我们结合 GraphQL:

首先定义一个 QueryType

$queryType = new ObjectType([
'name' => 'HelloQuery',
'fields' => [
'hello' => [
'type' => Type::string(),
'args' => [
'message' => ['type' => Type::string()],
],
'resolve' => function ($root, $args) {
return $root['prefix'] . $args['message'];
}
],
],
]);

添加进 Schema 

$schema = new Schema([
'query' => $queryType
]);

增加 route:

$route->map(
'POST',
'/graphql_demo',
function (
ServerRequestInterface $request,
ResponseInterface $response
)
use (
$queryType,
$schema
)
{
$params = $request->getParsedBody();
$collect = collect($params);
$rootValue = ['prefix' => 'from: '];
try {
$result = GraphQL::executeQuery($schema, $collect->get('query'), $rootValue);
$output = json_encode($result->toArray());
} catch (Exception $e) {
$output = [
'error' => [
'message' => $e->getMessage()
]
];
}
$response->getBody()->write("<h1>输出结果</h1>");
$response->getBody()->write("<p>$output</p>");

return $response;
}
);

重点是该语句:$result = GraphQL::executeQuery($schema, $collect->get('query'), $rootValue);

运行结果如下:

自制 PHP 小框架 FanlyPHP 之插件选择

如果传入的参数不对,看看显示效果:


其它插件
 


如,我一直是 Laravel's Collections 的忠实粉丝,所以在框架中必然会引入 collect,所以本框架选用 tightenco/collect

A Collections-only split from Laravel's Illuminate Support

具体参考:https://github.com/tightenco/collect

还有其它插件安装

// 时间处理插件
composer require nesbot/carbon

// Log 插件
composer require monolog/monolog


总结
 


插件的使用满足于我们不用自己再去造轮子了,如何合理有效的使用这些插件,就看接下来如何规划框架结构了,推荐看看有关 MVC 相关书籍内容。