首页 > PHP > 重新出发,全新改版的slim3.0

重新出发,全新改版的slim3.0

早期的PHP路由写法大多类似,无论是slim2.x的echo还是Silex的return都停留在php系统层上,而抽象层的代码可以让我们对http通信的认识更上一层。例如nodejs中的处理方式:

server.on('request', function(req, res) { 
    res.writeHead(200, {'Content-Type': 'text/html'}); 
    res.write('<h1>Node.js</h1>'); 
    res.end('<p>Hello World</p>'); 
}); 
server.listen(3000);

没有$_GET,$_POST等超全局变量,依然能很好地实现http通信,也许是时候让php返璞归真了(虽然php任何一个版本都不抛弃不放弃)。读了wordpres和typecho的源码才知道他们已经走的太远,不知道什么时候能够追上。


如果版本<php5.5或使用了任何一款不兼容slim3.x的slim插件,请不要升级!


好了,新版slim3.0已经发布,抛弃了所有低版本插件,整理好自己重新上路。


  1. 依然是composer安装,如果有不会的参考

composer require slim/slim

or

{
   "require": {
        "slim/slim": "3.*",
   },
   "repositories": {
      "packagist": {
         "type": "composer",
         "url": "http://packagist.phpcomposer.com"
      }
   }
}


如果是手动安装,那太恐怖了,slim3.0需要安装以下第三方依赖:

Pimple 3.x

PSR HTTP Message 0.x

FastRoute 4.x

2. Apache或Nginx等配置不变,或许你对PHP内置的WebServer更感兴趣:

$ php -S localhost:8000

重大升级之后的slim3.0让PHP绕过了Apache和Nginx容器层,甚至可以认为Slim实现了Nodejs的Webserver。

如果项目使用了Nginx或Apache的某些特殊功能(如禁IP,限速等功能),可以自行加一层反向代理。

Slim3.0是如何实现的呢?抽象与解耦

环境对象

环境对象是对当前请求 PHP 全局环境的封装,包括了 HTTP 请求的方法、URI、头部以及超全局数组 $_SERVER 中的服务器变量。环境对象实现了 Slim 应用和 PHP 全局环境之间的有效解耦。


请求对象

请求对象是根据环境对象提供的数据对当前 HTTP 请求的封装,提供了 HTTP 请求的方法、头部、参数以及主体。


响应对象

响应对象是对需要返回给客户端的 HTTP 响应的封装,它管理着 HTTP 响应的状态、头部和主体。


路由器对象

路由器对象的职责是管理应用的所有路由。一个 路由 包含三个部分:请求方法、URI 以及回调函数。当你调用 Slim 应用的 run() 方法时,路由器对象会遍历其管理的路由,然后调用第一个与当前 HTTP 请求方法和 URI 相匹配的路由。Slim 应用提供了一系列快捷简便的代理方法来操作路由器对象,因此不推荐直接访问路由器对象。


依赖注入(解耦)

关于依赖注入,可以看这篇文章转《PHP程序员如何理解依赖注入容器(dependency injection container)》

在Slim3.0中使用Pimple实现自定义依赖注入。Slim 应用的依赖都是通过具有延迟初始化特性的 Pimple 服务来实现的,只有在被调用时才会初始化并返回相应的对象。(这句好难理解啊)

对于注入还有很多规范,先看一个例子:

<?php
require 'vendor/autoload.php';

// 创建Slim App
$app = new \Slim\App();

// 获取容器对象
$container = $app->getContainer();

// 注入Twig View依赖对象
$container['view'] = function ($c) {
	// 设模板目录和缓存目录
    $view = new \Slim\Views\Twig('path/to/templates', [
        'cache' => 'path/to/cache'
    ]);

    // 添加Twig View扩展
    $view->addExtension(new Slim\Views\TwigExtension(
        $c['router'],
        $c['request']->getUri()
    ));

    return $view;
};

// 设置路由规则
$app->get('/hello/{name}', function ($request, $response, $args) {
    return $this->view->render($response, 'profile.html', [
        'name' => $args['name']
    ]);
})->setName('profile');

// 运行
$app->run();

由于Slim3.0不支持2.x版的大部分插件,于是作者自己整了一个模板插件——Twig-View,和smarty类似,甚至连缓存文件都面向对象了,虽然有种被牵着鼻子走的感觉,不过暂时只能这样。


下面,正式介绍Slim3.0中的http部分:

请求对象

请求对象从当前环境对象封装了 HTTP 请求数据。每个Slim应用都有一个请求对象,你可以使用这个请求对象来获取 HTTP 请求的方法名(method)、头部消息(headers)、消息主体部分(body)。这个消息对象同样通过Pimple方式注入到Slim应用上的,所以你可以通过以下方式获取

<?php
//获取请求对象,在路由回调中可以使用第一个参数,也可以使用$this['request']
$request = $container['request'];
//获取请求方法
$method = $request->getMethod();
//判断请求方法是GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
if ($request->isGet() === true) {
    // Do something
}
if ($request->isPost() === true) {
    // Do something
}
if ($request->isPut() === true) {
    // Do something
}
if ($request->isDelete() === true) {
    // Do something
}
if ($request->isHead() === true) {
    // Do something
}
//表示没听说过
if ($request->isOptions() === true) {
    // Do something
}
//表示没听说过
if ($request->isPatch() === true) {
    // Do something
}

据RFC2616标准(现行的HTTP/1.1)得知,通常有以下8种方法:OPTIONS、GETHEADPOSTPUTDELETE、TRACE和CONNECT。

RESTFUL服务常用的五种请求方式很容易理解,但是Slim3.0支持识别Options请求,据说是为了获取服务器功能性(返回服务器针对特定资源所支持的HTTP请求方法);而PATCH用于资源部分内容的更新,在某些情况下比PUTT省网络带宽,但是PATCH操作并不安全。

我们知道在某些浏览器中是不支持PUT操作的,这时可以将请求方式设置为POST,然后设置X-HTTP-Method-Override header参数覆盖之前的请求方式,从而达到模拟PUT请求的目的。同时,在后端可以通过$request->getOriginalMethod来获取未被覆盖的请求方式名。

Slim2.x版本中获取request参数方式

从$app['request']对象中还可以获取协议(Scheme),主机(Host),端口(Port),脚本物理路径(Physical path),不带域名的脚本Web路径(Virtual path),完整带域名的脚本Web路径(physical + virtual paths),请求参数(Query String)

//2.x版获取request对象
$request = $app->request;
//对应$_SERVER['REQUEST_SCHEME']
$server['scheme'] = $request->getScheme();
//对应$_SERVER['HTTP_HOST']
$server['host'] = $request->getHost();
//对应$_SERVER['SERVER_PORT']
$server['port'] = $request->getPort();
//对应$_SERVER['DOCUMENT_ROOT']
$server['realPath'] = $request->getScriptName();
//对应$_SERVER['SCRIPT_NAME']
$server['virtualPath'] = $request->getPathInfo();
//暂无对应
$server['fullPath'] = $request->getPath();
//暂无对应,获取参数将会保存到一个数组中
$server['params'] = $request->params();
//对应$_GET,获取参数将会保存到一个数组中
$_GET = $request->get();
//对应$_POST,获取参数将会保存到一个数组中
$_POST = $request->post();

Slim3.x版本中获取request参数方式

//对应$_SERVER['REQUEST_SCHEME']
$server['scheme'] = $request->getServerParams()['REQUEST_SCHEME'];
//对应$_SERVER['HTTP_HOST']
$server['host'] = $request->getServerParams()['HTTP_HOST'];
//对应$_SERVER['SERVER_PORT']
$server['port'] = $request->getServerParams()['SERVER_PORT'];
//对应$_SERVER['SCRIPT_FILENAME']
$server['scriptFilename'] = $request->getServerParams()['SCRIPT_FILENAME'];
//对应$_SERVER['SCRIPT_NAME']
$server['scriptName'] = $request->getServerParams()['SCRIPT_NAME'];
//对应$_SERVER['REDIRECT_URL']
$server['redirectUrl'] = $request->getServerParams()['REDIRECT_URL'];
//对应$_SERVER['QUERY_STRING']
$server['queryString'] = $request->getServerParams()['QUERY_STRING'];
//对应$_REQUEST
$_REQUEST = $request->getParams();
//对应$_GET
$_GET = $request->getQueryParams();
//如果在$app->get中无法求出post参数,如果$app->post可以得到post参数但是会受到$request->getQueryParams()的干扰

也就是说Slim3.x版本舍弃了2.x版本所有的方法名,而是将模拟取出$_SERVER中的值放到了$response中的某个属性中;而对于get路由方式下拒绝任何post参数,而post路由下却引入了get参数到$request->getParams()中,坑!

其他请求参数的获取:

//获取所有头部消息
$headers = $request->getHeaders();
if ($request->hasHeader('Accept') === true) {
	$headerValue = $request->getHeader('Accept');
}
//获取所有cookie到$cookies数组中
$cookies = $request->getCookieParams();
//检测cookie是否存在不能使用hasCookie了, 也不能通过getCookie($key)直接获取某个cookie
$cookieValue = isset($cookies['test']) ? $cookies['test'] : '';
//获取请求主体, 注意:Slim2.x版getBody()返回内容字符串, 3.x版返回body对象, 使用$body->getContents()获取内容字符串
$body = $request->getBody();
//获取请求对象的资源类型, 注意不是每一个请求都会有资源对象, 得到null, 除非有upload文件
$contentType = $request->getContentType();
//获取请求对象所支持的媒体类型, 不解
$mediaType = $request->getMediaType();
//获取请求对象所支持的媒体类型参数, 返回数组
$mediaParams = $request->getMediaTypeParams();
//获取请求对象的字符集, 得到null
$charset = $request->getContentCharset();
//获取请求内容的长度, 得到null
$length = $request->getContentLength();
//对应$_SERVER['REMOTE_ADDR']
$server['REMOTE_ADDR'] = $request->getServerParams()['REMOTE_ADDR'];
//对应$_SERVER['HTTP_REFERER'], 如果没有传递referer将不会出现
$ref = isset($request->getServerParams()['HTTP_REFERER']) ? $request->getServerParams()['HTTP_REFERER'] : '';
//对应$_SERVER['HTTP_USER_AGENT']
$agent = $request->getServerParams()['HTTP_USER_AGENT'];

响应对象

Response是slim封装了HTTP响应的对象, 你可以通过response对象设置HTTP响应状态, 响应头部, 和响应值, 然后return给客户端(浏览器), Response对象是不可改变的, 但是你可以使用with*()方法设置Response拷贝对象上, 无论在Slim的任何地方你都能获取最新的Response对象。

//旧的response对象设置了状态码后并不会改变旧的响应状态码, 如果需要生效请使用return $newResponse
$newResponse = $response->withStatus(404);
//获取旧的响应状态码
$status = $response->getStatusCode();
//获取响应header头部
$headers = $response->getHeaders();
//探测是否含有指定的header头部
if($response->hasHeader('Allow')) {
	//将会返回类似GET,HEAD,DELETE
	$isAllow = $response->getHeader('Allow');
}

//以key-value覆写模式设置header头部,但是withHeader将会覆盖之前设置的同名key
$newResponse = $response->withHeader(
	'Allow',
	'GET'
);
//以key-value追加模式设置header头部
$newResponse = $response->withAddedHeader(
	'Content-type',
	'application/json'
);
//将同名key的header头部使用逗号连接作为一行返回
$line = $newResponse->getHeaderLine('Content-type');
//移除指定key的header头部
$newResponse = $response->withoutHeader('Content-type');
//获取响应的body部分, 注意:Slim2.x版getBody()返回内容字符串, 3.x版返回body对象, 使用$body->getContents()获取内容字符串
$body = $response->getBody();
//向响应对象中写入数据, 这个竟然可以被修改!!!
$response->write('111');
//withBody太复杂,使用$newResponse = $oldResponse->withBody(参数必须符合\Psr\Http\Message\StreamableInterface规范)
$body = $response->getBody();

响应 Cookies部分被移除了,也不知道以后会不会再加上,不过使用Set-Cookies设置response的header头部可以达到设置cookie的效果。cookie参数列表类似:

[
'value' => 'Bob',
'expires' => '2 days',
'path' => '/',
'domain' => 'example.com',
'secure' => true,
'httponly' => true
]

路由对象

Slim3.x由nikic/fastroute提供路由功能,其路由规则和Slim2.x版大不相同,升级时请注意。

常见的路由方式有,$app->get();$app->post();$app->put();$app->delete();$app->options();$app->patch();$app->map(['GET','POST']);

和Slim2.x不同,路由回调的参数依次为请求对象($request),响应对象($response),路由参数对象($args)。

其中路由参数对应路由规则上的{}部分。

最后一个->setName('hello');是给当前路由规则取别名,然后在路由回调或模板中你就可以通过urlFor获取当前路由规则所对应的网址。例如:

echo $this['router']->urlFor('hello', [
    'name' => 'Josh'
]);
// Outputs "/hello/Josh"

我并不喜欢smarty之类的模板引擎,因为我使用Slim仅仅是为了制作RESTFUL API,而非搭建网站。所以模板功能可以自行查阅slim/twig-view


OK,翻译完成,Slim3.x改动太大,建议不要从Slim2.x升级。

本文地址:http://blog.zhengshuiguang.com/php/slim-3.html

转载随意,但请附上文章地址:-)

标签:none

评论已关闭