`
923723914
  • 浏览: 635162 次
文章分类
社区版块
存档分类
最新评论

[php]前端控制器

 
阅读更多

当一个请求达到系统时,系统必须能够理解请求中的需求是什么,然后调用适当的业务逻辑进行处理,最后返回相应结果。对于简单的程序,整个过程可以放在视图中,但随着系统的增长,这种处理方式不能很好地满足请求、调用业务逻辑和显示适当视图。那么我们就需要在较大的系统中较好地管理这三者的关系,我们可以划分出视图层与命令和控制层。

视图层与命令和控制层之间的界线通常比较模糊,又是也把这两个层称为表现层。

前端控制器单一入口(流行的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子类中的处理试图当时还不是最好的,能够把试图和命令分离开来效果会好一些。

分享到:
评论

相关推荐

    front-controller-php-example:前端控制器 PHP 示例 - 音乐播放器

    前端控制器 PHP 示例 - 音乐播放器 演示项目,展示了实现前端控制器模式和为 API 创建自己的微框架是多么容易。 当前项目模仿音乐播放器和 Spotify API 提供的音乐搜索。技术决策我决定实现前端控制器模式来封装典型...

    Inspinia for ThinkPHP

    │ │ └─indexend.php 前端控制器文件 │ ├─admin 管理模块目录 │ │ ├─controller 控制器目录 │ │ │ └─index.php 管理端入口控制器文件 │ │ └─view 视图目录 │ │ ├─layout 布局目录 │ │ │ ...

    Zend Framework前端控制器用法示例

    本文实例讲述了Zend Framework前端控制器用法。分享给大家供大家参考,具体如下: 常用方法 1.getInstance() 功能:用于获取前端控制器实例。 代码如下: <?php $front = Zend_Controller_Front::getInstance(); ...

    basepath:Slim 4的基本路径检测器

    index.php前端控制器 .htaccess内部重定向到public /目录 Slim 4应用程序必须执行以下步骤: 对于Apache,我们必须将Web流量“重定向”到public/index.php的前端控制器。 创建一个具有以下内容的文件: public/....

    laravel-skeleton

    Laravel包含一个public / .htaccess文件,该文件用于提供URL,而路径中没有index.php前端控制器。 在为Apache提供Laravel之前,请确保启用mod_rewrite模块,以便服务器可以使用.htaccess文件。 如果Laravel随附的....

    毕设&课设&项目&实训-以uni-app作为用户前端,基于esp8266在arduino开发的RFID识别控制器.zip

    毕设&课设&项目&实训-以uni-app作为用户前端,基于esp8266在arduino开发的RFID识别控制器。在此项目中主要用于电动车NFC控制 【项目资源】: 包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、...

    Symfony2开发之控制器用法实例分析

    1、每个请求都被单个前端控制器(如app.php或index.php)文件处理,前端控制器负责引导框架; 2、路由查看并匹配请求信息,并将其指向一个特定的路由,该路由决定调用哪个控制器; 3、执行控制器,控制器中的代码将...

    基于PHP的华旭商务网的毕业设计,MySQL作为数据库,HTML、CSS、JavaScript作为前端技术,使用了Laravel

    该网站采用MVC架构,通过路由控制器与模型的交互,实现了数据的增删改查等基本操作。在前端方面,使用Bootstrap框架进行页面布局,实现了响应式设计。 ### 功能实现 ### 企业信息发布 企业可以在网站上注册账号并...

    laravel 解决后端无法获取到前端Post过来的值问题

    刚开始一直获取不到前端传过来的Post的值 一番分析后 发现 通过php 命令新建的控制器默认少引用 就是上图圈起来的这个 引入就问题解决了 当然 前提是路由要配置正确 路由配置成Post或者any的时候才能获取到Post的值 ...

    最新2016云知梦WEB开发教程合集

    SVN版本控制器 手机微信开发 手机微网站开发 无刷新上传图片和文件 Bootstrap前端框架 CSS3样式设计 HTML5网页标签 Javascript前端开发 jQuery前端框架 jQuery实用插件 WEB前端项目实战 手机APP开发

    基于STM32F1系列微控制器的四旋翼飞行控制器,用于挑战无人机自动控制。.zip

    包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。 包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python...

    学校学院/教育培训机构管理系统PHP源码

    一套基于Laravel 5, Bootstrap 4 和AngularJS构建的...增强前端页面滑块选择器 增强访问者的参与度和邮政视图 增强学生,家长和老师的数据隐私 修正了在时间结束时提交在线考试的问题 修复了Firefox上的自动滚动问题

    毕设&课设&项目&实训-基于esp8266在arduino开发的RFID识别控制器。.zip

    这是一个以uni-app作为用户前端,基于esp8266在arduino开发的RFID识别控制器。在此项目中主要用于电动车NFC控制 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一...

    基于PHP的团队周报系统

    core/: 核心代码目录,包括控制器基类、模型基类、数据库基类、路由基类。 public/: Web 服务器的公共目录,包括前端资源和入口文件。 config/: 配置文件,包括数据库和路由配置。 vendor/: 第三方依赖库。 ....

    毕设&课设&项目&实训-基于Apache开源协议和ESP32主控芯片的低成本四足机器人控制器,控制器集成.zip

    包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。 包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python...

    CakePHP:PHP快速开发框架-官方资料库-PHP开发

    CakePHP是PHP的快速开发框架,它使用诸如联合数据映射,前端控制器和MVC之类的众所周知的设计模式。 我们的主要目标是提供一个结构化的框架,该框架使enabl CakePHP是一个PHP的快速开发框架,它使用诸如关联数据映射...

    basicphp:BasicPHP-一种基于PHP的无框架库的方法

    要将BasicPHP类库(Basic.php)集成到任何框架或应用程序,请在前端控制器脚本顶部包含/要求Basic.php。 这通常是应用程序的index.php。 功能包括类自动加载,REST和JSON-RPC路由,功能/中间件和安全性(HTTPS,...

    三相直流无刷电机控制器解决方案.pdf

    【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、...

    列表问题:前端响应的简单用法和示例json

    ---- :file_folder: 应用=控制器 ---- :file_folder: 开机 -------- :page_facing_up: Config.php =常量CONF_URL_TEST :file_folder: 主题=观看次数 ---- :file_folder: 网路 -------- :page_facing_up: _theme....

    php的mvc模式开发框架

    Control目录下是的Admin和Home是控制器存放目录命名方式如:IndexAction.class.php Core目录下是封装的一些核心文件 Model目录下存放模块文件,基本上一个数据库表对应一个模块文件 Public目录下的Admin和Home分别...

Global site tag (gtag.js) - Google Analytics