当一个请求达到系统时,系统必须能够理解请求中的需求是什么,然后调用适当的业务逻辑进行处理,最后返回相应结果。对于简单的程序,整个过程可以放在视图中,但随着系统的增长,这种处理方式不能很好地满足请求、调用业务逻辑和显示适当视图。那么我们就需要在较大的系统中较好地管理这三者的关系,我们可以划分出视图层与命令和控制层。
视图层与命令和控制层之间的界线通常比较模糊,又是也把这两个层称为表现层。
前端控制器:单一入口(流行的PHP框架大多都是单一入口),定义了一个中心入口文件,每个请求都需要从这个入口进入系统,当然也可以在这里对用户的输入请求进行过滤。前端控制器处理请求后选择适当的命令(处理业务的)。在PHP中,这种模式每次都需要初始化,所以会导致性能的下降,但好处还是显而易见的。
前端控制器处理的是请求,那么我们就需要把用户的请求进行处理,这里用类赖封装用户的请求Request。
下面是Request的代码:
namespace demo\controller;
/**
* Request
* 封装用户请求
*/
class Request {
private $properties;
private $feedback = array();
public function __construct() {
$this->init();
$this->filterProperties();
// 保存入Registry
\demo\base\RequestRegistry::getInstance()->setRequest($this);
}
public function __clone() {
$this->properties = array();
}
public function init() {
if (isset($_SERVER['REQUEST_METHOD'])) {
if ($_SERVER['REQUEST_METHOD']) {
$this->properties = $_REQUEST;
return ;
}
}
// 命令行下的方式
foreach ($_SERVER['argv'] as $arg) {
if (strpos($arg, '=')) {
list($key, $val) = explode('=', $arg);
$this->setProperties($key, $val);
}
}
}
public function filterProperties() {
// 过滤用户请求...
}
public function getProperty($key) {
return $this->properties[$key];
}
public function setProperties($key, $val) {
$this->properties[$key] = $val;
}
public function getFeedback() {
return $feedback;
}
public function addFeedback($msg) {
array_push($this->feedback, $msg);
}
public function getFeedbackString($separator = '\n') {
return implode('\n', $this->feedback);
}
}
为什么Request不用使用内置的$_GET或者是$_POST等而是把封装在一起呢。这是为了能把用户的请求集中到一个地方进行统一处理,比如对请求过滤。这里还有一个好处就是可以从非HTTP请求中收集请求参数,允许应用程序在命令行或者在测试脚本中运行。
这里先来看看前端控制器的主体框架:
namespace demo\controller;
class Controller {
private $appHelper;
private function __construct() {
}
public static function run() {
$instance = new self();
// 加载配置
$instance->init();
// 处理请求
$instance->handleReuqest();
}
private function init() {
$appHelper = \demo\controller\ApplicationHelper::getInstance()->init();
}
private function handleReuqest() {
$request = new \demo\controller\Request();
$cmdReslover = new \demo\command\CommandReslover();
$cmd = $cmdReslover->getCommand($request);
$cmd->execute($request);
}
}
只要在一个php文件中包含这个类文件,并运行就可以了。
use demo\controller\Controller;
require_once 'demo/controller/Controller.php';
Controller::run();
执行流程很简单:
1)Controller::run()中调用init(),其中的ApplicationHelper是用于读取系统配置的类,它帮助加载系统配置; 2)解析Request由CommandReslover对象的getCommand()来处理,返回一个Command子类; 3)Command子类处理业务。
下面分别是Controller中出现的类。
ApplicationHelper的代码:
namespace demo\controller;
/**
* 助手类 获取系统配置
* 单例
*/
class ApplicationHelper {
private static $instance;
private $config = './data/config.xml';
private function __construct() {
}
public static function getInstance() {
if (isset(self::$instance) == false) {
self::$instance = new self();
}
return self::$instance;
}
public function init() {
// 用获取dsn例子,注意Regisrty缓存
$dsn = \demo\base\ApplicationRegistry::getInstance()->getDSN();
if (is_null($dsn) == false) {
return ;
}
$this->getOptions();
}
public function getOptions() {
$this->ensure(file_exists($this->config), "Could not find options file!");
$options = @simplexml_load_file($this->config);
$this->ensure($options instanceof \SimpleXMLElement, 'Could not resolve options file!');
$dsn = (string)$options->dsn;
$this->ensure($dsn, 'No dsn found!');
\demo\base\ApplicationRegistry::getInstance()->setDSN($dsn);
// 其它一些获取配置的代码...
}
private function ensure($expr, $msg) {
if (!$expr) {
throw new \demo\base\AppException($msg);
}
}
}
对于PHP来说每次都需要读文件是件很费时的事情,那ApplicationHelper就需要实现缓存机制。可以通过简单地判断变量是否已经设置来决定是否需要读取文件。其实最高效的方式是直接把配置内容写到PHP文件中。
CommandSlover通过Request来决定返回哪一个Command子类(业务封装在里面)。简单的办法获取url中cmd的值来决定的。比如runner.php?cmd=AddUser,那么CommandSlover就返回AddUser。
namespace demo\command;
class CommandReslover {
private static $baseCmd;
private static $defaultCmd;
public function __construct() {
self::$baseCmd = new \ReflectionClass('\demo\command\Command');
self::$defaultCmd = new DefaultCommand();
}
/**
* 解析请求返回命令
* @param \demo\controller\Request $request
*/
public function getCommand(\demo\controller\Request $request) {
// cmd为url参数名称,如 runner.php?cmd=addUser
$cmd = $request->getProperty('cmd');
$sep = DIRECTORY_SEPARATOR;
// 返回 默认的Command
if (empty($cmd) == true) {
return self::$defaultCmd;
}
//
$cmd = str_replace(array('.', $sep), '', $cmd);
$filePath = "demo{$sep}command{$sep}{$cmd}.php";
$className = '\demo\command\\' . $cmd;
if (file_exists($filePath)) {
@require_once $filePath;
if (class_exists($className)) {
// 判断cmd是否为Command的子类
$cmdClass = new \ReflectionClass($className);
if ($cmdClass->isSubclassOf(self::$baseCmd)) {
return $cmdClass->newInstance();
} else {
$request->addFeedback("Command '{$cmd}' is not a Command!");
}
}
}
$request->addFeedback("Command '{$cmd}' not found!");
return clone self::$defaultCmd;
}
}
Command子类主要封装业务。Command的类图:
Command抽象类代码:
namespace demo\command;
abstract class Command {
// 子类不能够重写
public final function __construct() {
}
public function execute(\demo\controller\Request $request) {
$this->doExecute($request);
}
// 子类实现对应的业务
protected abstract function doExecute(\demo\controller\Request $request);
}
DefaultCommand代码:
namespace demo\command;
class DefaultCommand extends Command {
protected function doExecute(\demo\controller\Request $request) {
$request->addFeedback('Welcome~');
//
include('demo/view/default.php');
echo $request->getFeedbackString();
}
}
现在前端控制已经能够在一个地方统一处理请求了。搭建好核心部分后就可以用最简短的代码来封装它,以后就可以重复使用了。虽然平时能够用更少的代码很简单的方法实现同样的效果,但这是为了能够加深对前端控制器的理解。
Command子类中的处理试图当时还不是最好的,能够把试图和命令分离开来效果会好一些。
分享到:
相关推荐
前端控制器 PHP 示例 - 音乐播放器 演示项目,展示了实现前端控制器模式和为 API 创建自己的微框架是多么容易。 当前项目模仿音乐播放器和 Spotify API 提供的音乐搜索。技术决策我决定实现前端控制器模式来封装典型...
│ │ └─indexend.php 前端控制器文件 │ ├─admin 管理模块目录 │ │ ├─controller 控制器目录 │ │ │ └─index.php 管理端入口控制器文件 │ │ └─view 视图目录 │ │ ├─layout 布局目录 │ │ │ ...
本文实例讲述了Zend Framework前端控制器用法。分享给大家供大家参考,具体如下: 常用方法 1.getInstance() 功能:用于获取前端控制器实例。 代码如下: <?php $front = Zend_Controller_Front::getInstance(); ...
index.php前端控制器 .htaccess内部重定向到public /目录 Slim 4应用程序必须执行以下步骤: 对于Apache,我们必须将Web流量“重定向”到public/index.php的前端控制器。 创建一个具有以下内容的文件: public/....
Laravel包含一个public / .htaccess文件,该文件用于提供URL,而路径中没有index.php前端控制器。 在为Apache提供Laravel之前,请确保启用mod_rewrite模块,以便服务器可以使用.htaccess文件。 如果Laravel随附的....
毕设&课设&项目&实训-以uni-app作为用户前端,基于esp8266在arduino开发的RFID识别控制器。在此项目中主要用于电动车NFC控制 【项目资源】: 包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、...
1、每个请求都被单个前端控制器(如app.php或index.php)文件处理,前端控制器负责引导框架; 2、路由查看并匹配请求信息,并将其指向一个特定的路由,该路由决定调用哪个控制器; 3、执行控制器,控制器中的代码将...
该网站采用MVC架构,通过路由控制器与模型的交互,实现了数据的增删改查等基本操作。在前端方面,使用Bootstrap框架进行页面布局,实现了响应式设计。 ### 功能实现 ### 企业信息发布 企业可以在网站上注册账号并...
刚开始一直获取不到前端传过来的Post的值 一番分析后 发现 通过php 命令新建的控制器默认少引用 就是上图圈起来的这个 引入就问题解决了 当然 前提是路由要配置正确 路由配置成Post或者any的时候才能获取到Post的值 ...
SVN版本控制器 手机微信开发 手机微网站开发 无刷新上传图片和文件 Bootstrap前端框架 CSS3样式设计 HTML5网页标签 Javascript前端开发 jQuery前端框架 jQuery实用插件 WEB前端项目实战 手机APP开发
包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。 包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python...
一套基于Laravel 5, Bootstrap 4 和AngularJS构建的...增强前端页面滑块选择器 增强访问者的参与度和邮政视图 增强学生,家长和老师的数据隐私 修正了在时间结束时提交在线考试的问题 修复了Firefox上的自动滚动问题
这是一个以uni-app作为用户前端,基于esp8266在arduino开发的RFID识别控制器。在此项目中主要用于电动车NFC控制 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一...
core/: 核心代码目录,包括控制器基类、模型基类、数据库基类、路由基类。 public/: Web 服务器的公共目录,包括前端资源和入口文件。 config/: 配置文件,包括数据库和路由配置。 vendor/: 第三方依赖库。 ....
包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。 包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python...
CakePHP是PHP的快速开发框架,它使用诸如联合数据映射,前端控制器和MVC之类的众所周知的设计模式。 我们的主要目标是提供一个结构化的框架,该框架使enabl CakePHP是一个PHP的快速开发框架,它使用诸如关联数据映射...
要将BasicPHP类库(Basic.php)集成到任何框架或应用程序,请在前端控制器脚本顶部包含/要求Basic.php。 这通常是应用程序的index.php。 功能包括类自动加载,REST和JSON-RPC路由,功能/中间件和安全性(HTTPS,...
【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、...
---- :file_folder: 应用=控制器 ---- :file_folder: 开机 -------- :page_facing_up: Config.php =常量CONF_URL_TEST :file_folder: 主题=观看次数 ---- :file_folder: 网路 -------- :page_facing_up: _theme....
Control目录下是的Admin和Home是控制器存放目录命名方式如:IndexAction.class.php Core目录下是封装的一些核心文件 Model目录下存放模块文件,基本上一个数据库表对应一个模块文件 Public目录下的Admin和Home分别...