index.php.前端控制器,前端控制器 - Symfony开源 - Symfony中国
目前为止,我们的程序还过于简单,因为它只有一个页面。为了加点调料进去,我们疯狂一下,再做一个说“goodbye”的页面:
1
2
3
4
5
6
7
8
9
10// framework/bye.php
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$response = new Response('Goodbye!');
$response->send();
如你所见,上面代码中很多都与我们写的第一个页面完全相同。我们提取出这部分代码并在页面间共享。代码共享,听起来是个不错的计划,可以创建我们第一个“真正”的框架。
以PHP方式来重构(代码),差不多就是“创建一个包容文件”:
1
2
3
4
5
6
7
8// framework/init.php
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$response = new Response();
我们看看(在两个页面中的)实际运行情况:
1
2
3
4
5
6
7// framework/index.php
require_once __DIR__.'/init.php';
$input = $request->get('name', 'World');
$response->setContent(sprintf('Hello %s', htmlspecialchars($input, ENT_QUOTES, 'UTF-8')));
$response->send();
再来是“Goodbye”页面:
1
2
3
4
5// framework/bye.php
require_once __DIR__.'/init.php';
$response->setContent('Goodbye!');
$response->send();
我们的确把共享代码给转移到一个“中心地带(central place)”,但它并不像一个很好的抽象层,不是吗?对于所有页面,我们仍要保留send()方法,页面也不像个模板,我们始终不能正确地测试代码。
还有,添加一个新页,意味着我们必须创建一个新的PHP脚本,其名字通过URL(http://127.0.0.1:4321/bye.php)完全暴露给末级用户:PHP脚本名称与客户端URL之间是直接的映射关系。这是因为,派发的请求(request)直接依赖于服务器。为了提高灵活性,也许把这种派遣转移到我们的代码中是个好主意。这可以通过把所有客户端请求发送(routing)到一个独立PHP脚本来轻松实现。
把一个单一PHP脚本暴露给(exposing)末级用户是一个被称之为前端控制器的设计模式。
这样一个脚本可能像下面这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// framework/front.php
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$response = new Response();
$map = array(
'/hello' => __DIR__.'/hello.php',
'/bye' => __DIR__.'/bye.php',
);
$path = $request->getPathInfo();
if (isset($map[$path])) {
require $map[$path];
} else {
$response->setStatusCode(404);
$response->setContent('Not Found');
}
$response->send();
那么新的hello.php脚本应该是下面这样:
1
2
3// framework/hello.php
$input = $request->get('name', 'World');
$response->setContent(sprintf('Hello %s', htmlspecialchars($input, ENT_QUOTES, 'UTF-8')));
在front.php脚本中,$map用相对应的PHP脚本路径(paths)关联了URL路径。
作为一个奖励,如果客户端在请求一个“没有定义在URL映射中”的路径时,我们返回一个自定义的404页;现在你可以控制你的网站了。
为了能访问页面,从现在起你必须使用front.php:
http://127.0.0.1:4321/front.php/hello?name=Fabien
http://127.0.0.1:4321/front.php/bye
/hello和/bye就是页面的路径(paths)。
多数web server,比如Apache或nginx,都可以对请求的URLs进行重写(rewrite),这样就能去掉前端控制器的脚本(名称)了,进而你键入http://127.0.0.1:4321/hello?name=Fabien即可,看上去好多了。
其中的小魔法在于Request::getPathInfo()方法的使用,通过移除前端控制器的“脚本名称”连同其子目录(如果有必要的话,参考上面的灯炮tip)——它将返回请求的路径部分。
你毋须设置web server来测试代码。只需替换掉$request = Request::createFromGlobals();,改以$request = Request::create('/hello?name=Fabien');这种,其中的参数就是你打算模拟的“URL路径”。
现在的服务器已经可以通过相同的脚本(front.php)来访问所有页面了,我们还可以进一步从web根目录中移除所有无关的PHP文件以增加安全性:
1
2
3
4
5
6
7
8
9
10
11example.com
├── composer.json
├── composer.lock
├── src
│ └── pages
│ ├── hello.php
│ └── bye.php
├── vendor
│ └── autoload.php
└── web
└── front.php
现在,重新配置你的web server根目录指向web/,其他所有文件将不再能从客户端访问到。
要在浏览器中测试这种改变(http://127.0.0.1:4321/front.php/hello?name=Fabien),运行PHP内置的server:
1$ php -S 127.0.0.1:4321 -t web/ web/front.php
为了让这个新结构得以运行,你不得不在不同的PHP文件中调整一些路径。这些改变作为练习留给读者完成。
在每个页面中最后一个“重复性”的东西是调用setContent()。我们可以将全部页面转换成“模板”,只需打出(echoing)内容部分再直接从前端控制器脚本中调用setContent()即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// example.com/web/front.php
// ...
$path = $request->getPathInfo();
if (isset($map[$path])) {
ob_start();
include $map[$path];
$response->setContent(ob_get_clean());
} else {
$response->setStatusCode(404);
$response->setContent('Not Found');
}
// ...
同时hello.php的脚本要被转换成一个模板:
1
2
3
4
<?php $name = $request->get('name', 'World') ?>
Hello <?php echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?>
我们这个框架的第一个版本现已齐活:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// example.com/web/front.php
require_once __DIR__.'/../vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$response = new Response();
$map = array(
'/hello' => __DIR__.'/../src/pages/hello.php',
'/bye' => __DIR__.'/../src/pages/bye.php',
);
$path = $request->getPathInfo();
if (isset($map[$path])) {
ob_start();
include $map[$path];
$response->setContent(ob_get_clean());
} else {
$response->setStatusCode(404);
$response->setContent('Not Found');
}
$response->send();
添加一个新页面将是如下两步的过程:在映射中添加一个入口,然后在src/pages/中创建一个模板。在模板中,通过$request变量取得Request内容,然后通过$response变量可以调整响应头。
如果你决定止步于此,你也许还能通过“把URL映射提取到一个配置文件中”来强化你的框架。
index.php.前端控制器,前端控制器 - Symfony开源 - Symfony中国相关推荐
- php开源路由器,路由 - Symfony开源 - Symfony中国
对于任何严谨的web应用程序而言美观的URL是绝对必须的.这意味着日渐淘汰的 index.php?article_id=57 这类丑陋的URL要被 /read/intro-to-symfony 取代. ...
- 恐龙快跑小程序对接流量主源码+前端 v5.0.4 全开源微擎框架
简介: 恐龙快跑小程序对接流量主源码+前端 v5.0.4 全开源优点: 首页流量主赚取收益 用户冲分领奖品机制,冲到某个分数即可兑换相应奖品 用户游戏死亡后需要充值金币或者转发给好友获取金币复活. 盈 ...
- 大前端–Vue前端体系、前后端分离
大前端–Vue前端体系.前后端分离 前言 Soc:关注点分离原则 HTML+CSS+JS(视图):给用户看,刷新后台给的数据 网络通信:axios 页面跳转:vue-router 状态管理:vuex ...
- 【前端】前端及其技术栈
文章目录 0)前端 1.什么是前端? 2.为什么需要前端? 前端的发展简史: 3.前端开发的类型 Web前端开发: 客户端(APP)开发: 4.前端与后端的交互 Ajax: Socket: *前端技术 ...
- GMTC 大前端时代前端监控的最佳实践
摘要: 今天我分享的内容分成三个部分: 第一部分是"大前端时代前端监控新的变化", 讲述这些年来,前端监控一些新的视角以及最前沿的一些思考. 第二部分"前端监控的最佳实践 ...
- GMTC 大前端时代前端监控的最佳实践 1
摘要: 今天我分享的内容分成三个部分: 第一部分是"大前端时代前端监控新的变化", 讲述这些年来,前端监控一些新的视角以及最前沿的一些思考. 第二部分"前端监控的最佳实践 ...
- Asp.Net MVC控制器、控制器动作和动作结果
原文链接:http://www.asp.net/learn/mvc/ 这篇教程探索了ASP.NET MVC控制器(controller).控制器动作(controller action)和动作结果(a ...
- jmeter使用if控制器_Jmeter(七)_if控制器+循环控制器+计数器控制接口分支
最近查阅了一下网上关于if控制器的文章,大同小异,几乎找不到原创,于是决定自己写一篇 下午测试接口,遇到了一个审核的流程.逻辑很简单,就是审核不通过之后返回去继续修改再提交,然后再审核,直到通过为止. ...
- 零基础不建议学前端_web前端开发零基础怎样入门-哈尔滨前端学习
web前端开发零基础怎样入门-哈尔滨前端学习,俗话说,知己知彼,百战百胜.要想学好web前端,首先要了解什么是web前端,下面由小编来给大家介绍一下: 1什么是web? Web就是在Http协议基础之 ...
最新文章
- 黑盒测试方法之边界值分析法
- unity桌面设置vnc_win7系统通过VNCViewer访问Ubuntu桌面环境的操作方法
- python背景怎么自定义铃声_Python 上课铃声的定时播放(具有较强的自我管理能力.jpg)...
- Spring Boot异常处理
- C# 6 的新特性~
- C#中的函数式编程:递归与纯函数(二) 学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面...
- wince车机刷系统刷机包_2020年刷机包是不是越小越精简,越小越流畅好用
- 二项分布与负二项分布卡片
- 在div中加本地html,div加载另一个HTML页面
- 猫哥教你写爬虫 041--模拟登录-cookie
- 条码扫描枪有什么接口?应该怎么选择扫描枪的接口
- threejs使用tweenjs实现点击标签过渡到相应视角
- mysql explain不准确_mysql explain预估剖析
- 广告公司网站该怎么做和运营
- 分享一个好用的屏幕截取动图的工具
- 利用正则表达式来验证邮箱
- FileReader读取文件的三种方式
- sql server 入门篇
- 《量子信息与量子计算简明教程》第三章·量子纠缠状态及其应用 (上)
- 每日一道leetcode(python)876. 链表的中间结点