[php]应用控制器(1)
[php]应用控制器(一)
前端控制器已经能很好地在一个地方集中处理请求并选择适当的Command了,但是Command子类对象自己处理了视图的分配工作。要是能够使用一个类(根据Command处理后返回的状态值)来决定视图并返回到前端控制器,再由前端控制器来调用视图显示,这样的话前端控制器就处于视图层和业务层的中间了,而且也很好地把Command和视图分开了。应用控制器是个好的解决方案。
应用控制器负责映射请求到命令,并映射命令到视图。它允许改变应用程序的流程而不需要修改核心代码。它能把Command类解放出来,让Command类集中精力完成自己的工作,包括处理输入、调用应用程序逻辑和处理结果等。
应用控制器是一个类(或者一组类),它帮助前端控制接管处理请求的任务而且又把适当的视图返回给前端控制器调用。那么应用控制是什么方式运行的呢?它是通过一个xml配置文件来决定Command和视图工作的方式。比如下面这个xml文件(有点像struts的方式):
<?xml version="1.0" encoding="UTF-8"?> <options> <dsn>sqlite://data/demo.db</dsn> <username>root</username> <password>root</password> <controller> <view>main</view> <view status="CMD_OK">main</view> <view status="CMD_ERROR">error</view> <command name="ListStudents"> <view>list_students</view> </command> <command name="AddStudent"> <view>add_student</view> <status value="CMD_OK"> <forward>ListStudents</forward> </status> </command> <command name="SimpleAddStudent"> <classalias name="AddStudent"></classalias> <view>simple_add_student</view> </command> </controller> </options>
从xml的结构就能了解到Command类需要一些新的属性status了。
namespace demo\command; /** * 抽象父类 */ abstract class Command { // 状态值映射 private static $STATUS_STRINGS = array( 'CMD_DEFAULT' => 0, 'CMD_OK' => 1, 'CMD_ERROR' => 2, 'CMD_INSUFFICIENT_DATA' => 3 ); // 当前状态值 private $status = 0; public final function __construct() { // 子类不能重写构造函数 } /** * 按状态字符串返回状态值 * @param unknown_type $staStr */ public static function status($stauStr = 'CMD_DEFAULT') { if (empty($stauStr)) { $stauStr = 'CMD_DEFAULT'; } return self::$STATUS_STRINGS[$stauStr]; } /** * 调用子类实现的doExecute * @param \demo\controller\Request $request */ public function execute(\demo\controller\Request $request) { $this->doExecute($request); } protected abstract function doExecute(\demo\controller\Request $request); }
系统中有个专门获取配置的助手类ApplicationHelper可以实现对xml配置的读取。由于xml中的元素结构相对灵活一些,那么就需要一个ControllerMap来管理各元素中的值和Command、视图的一一映射关系。
namespace demo\controller; class ControllerMap { private $classrootMap = array(); private $forwardMap = array(); private $viewMap = array(); public function addClassroot($cmd, $classroot) { $this->classrootMap[$cmd] = $classroot; } public function getClassroot($cmd) { if (isset($this->classrootMap[$cmd])) { return $this->classrootMap[$cmd]; } return $cmd; } public function addForward($cmd = 'default', $status = 0, $newCmd) { $this->forwardMap[$cmd][$status] = $newCmd; } public function getForward($cmd, $status) { if (isset($this->forwardMap[$cmd][$status])) { return $this->forwardMap[$cmd][$status]; } return null; } public function addView($cmd = 'default', $status = 0, $view) { $this->viewMap[$cmd][$status] = $view; } public function getView($cmd, $status) { if (isset($this->viewMap[$cmd][$status])) { return $this->viewMap[$cmd][$status]; } return null; } }
首先需要获取xml中的配置ApplicationHelper类的getOptions。
namespace demo\controller; /** * 助手类:获取xml配置 * 单例模式 * */ 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 = \demo\base\ApplicationRegistry::getInstance()->getDSN(); $camp = \demo\base\ApplicationRegistry::getInstance()->getControllerMap(); if (is_null($dsn) || is_null($camp)) { $this->getOptions(); } } /** * 获取xml配置 */ public function getOptions() { // xml $this->ensure(file_exists($this->config), "Could not find options file!"); $options = @simplexml_load_file($this->config); var_dump($options); $this->ensure($options instanceof \SimpleXMLElement, 'Could not resolve options file!'); // <dsn> $dsn = (string)$options->dsn; $this->ensure($dsn, 'No dsn found!'); \demo\base\ApplicationRegistry::getInstance()->setDSN($dsn); // <controller> $map = new ControllerMap(); // <view> foreach ($options->controller->view as $default_view) { $stauStr = trim((string)$default_view['status']); $status = \demo\command\Command::status($stauStr); $map->addView('default', $status, (string)$default_view); } // <command> foreach ($options->controller->command as $cvf) { $cmd = trim((string)$cvf['name']); // <classalias> if($cvf->classalias) { $classroot = (string)$cvf->classalias['name']; $map->addClassroot($cmd, $classroot); } // <view>、<forward> if ($cvf->view) { $view = trim((string)$cvf->view); $forward = trim((string)$cvf->forward); $map->addView($cmd, 0, $view); if ($forward) { $map->addForward($cmd, 0, $forward); } } // <status> foreach ($cvf->status as $status) { $stauStr = trim((string)$status['value']); $view = trim((string)$status->view); $forward = trim((string)$status->forward); $stau = \demo\command\Command::status($stauStr); if ($view) { $map->addView($cmd, $stau, $view); } if ($forward) { $map->addForward($cmd, $stau, $forward); } } } var_dump($map); \demo\base\ApplicationRegistry::getInstance()->setControllerMap($map); } private function ensure($expr, $msg) { if (!$expr) { throw new \demo\base\AppException($msg); } } }</status></forward></view></classalias></command></view></controller></dsn>
获取xml配置的过程是一个比较费时的操作,可以先把ControllerMap对象序列化到文件中去,之后可以通过ApplicationRegistry获取并把它当做全局数据缓存起来。
/** * Application作用域 */ class ApplicationRegistry extends Registry { private static $instance; private $freezedir = "./data"; // 此处硬编码,具体根据实际情况配置 private $values = array(); private $mtimes = array(); private function __construct() { } public static function getInstance() { if (isset(self::$instance) == false) { self::$instance = new self(); } return self::$instance; } /** * 从序列化文件中获取$key数据 */ protected function get($key) { $path = $this->freezedir . DIRECTORY_SEPARATOR . $key; if (file_exists($path)) { // 清楚文件缓存 clearstatcache(); $mtime = filemtime($path); if (isset($this->mtimes[$key]) == false) { $this->mtimes[$key]=0; } // 文件最近被修改过,重新反序列化新的数据 if ($mtime > $this->mtimes[$key] ) { $data = file_get_contents($path); $this->mtimes[$key] = $mtime; return ($this->values[$key] = unserialize($data)); } } if (isset( $this->values[$key]) == true) { return $this->values[$key]; } return null; } protected function set($key, $val) { $this->values[$key] = $val; $path = $this->freezedir . DIRECTORY_SEPARATOR . $key; if (file_exists($path)) { touch($path); } file_put_contents($path, serialize($val)); $this->mtimes[$key]=time(); } public function getDSN() { if (isset($this->values['dsn'])) { return $this->values['dsn']; } return self::getInstance()->get('dsn'); } public function setDSN($dsn) { return self::getInstance()->set('dsn', $dsn); } /** * * @param \demo\controller\ControllerMap $map */ public function setControllerMap(\demo\controller\ControllerMap $map) { self::getInstance()->set('cmap', $map); } public function getControllerMap() { if (isset($this->values['cmap'])) { return $this->values['cmap']; } return self::getInstance()->get('cmap'); } /** * 获取AppController */ public function getAppController() { $obj = self::instance(); if (!isset($obj->appController)) { $cmap = $obj->getControllerMap(); $obj->appController = new \demo\controller\AppController($cmap); } return $obj->appController; } // 其它一些列getter和setter // ...... }
这次需要实现更加复杂的调用,比如forward,那么就需要简单地修改Request类的代码了,使它能够符合调用逻辑的需要。
namespace demo\controller; /** * 封装用户请求 * Request */ class Request { private $properties; private $feedback = array(); // 保存业务对象,可以供给视图使用 private $objects = array(); // 保存上一个已执行的Command对象 private $lastCommand; public function __construct() { $this->init(); $this->filterProperties(); \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); } /** * * @param \demo\command\Command $cmd */ public function setCommand(\demo\command\Command $cmd) { $this->lastCommand = $cmd; } public function getLastCommand() { return $this->lastCommand; } /** * * @param unknown_type $name * @param unknown_type $object */ public function setObject($name, $object) { $this->objects[$name] = $object; } public function getObject($name) { if (isset($this->objects[$name])) { return $this->objects[$name]; } return null; } }
现在已经添加了能够保存映射关系的类ControllerMap,修改了Request、Command、ApplicationHelper、ApplicationRegistry,主要是添加,少量修改之前的代码。
上面这些类已经能够完成系统的初始化了,包括读取xml配置(ApplicationHelper)、全局变量访问(Registry);
之后就是核心部分了:Controller和AppController两个类了。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

“你的组织要求你更改PIN消息”将显示在登录屏幕上。当在使用基于组织的帐户设置的电脑上达到PIN过期限制时,就会发生这种情况,在该电脑上,他们可以控制个人设备。但是,如果您使用个人帐户设置了Windows,则理想情况下不应显示错误消息。虽然情况并非总是如此。大多数遇到错误的用户使用个人帐户报告。为什么我的组织要求我在Windows11上更改我的PIN?可能是您的帐户与组织相关联,您的主要方法应该是验证这一点。联系域管理员会有所帮助!此外,配置错误的本地策略设置或不正确的注册表项也可能导致错误。即

Windows11将清新优雅的设计带到了最前沿;现代界面允许您个性化和更改最精细的细节,例如窗口边框。在本指南中,我们将讨论分步说明,以帮助您在Windows操作系统中创建反映您的风格的环境。如何更改窗口边框设置?按+打开“设置”应用。WindowsI转到个性化,然后单击颜色设置。颜色更改窗口边框设置窗口11“宽度=”643“高度=”500“>找到在标题栏和窗口边框上显示强调色选项,然后切换它旁边的开关。若要在“开始”菜单和任务栏上显示主题色,请打开“在开始”菜单和任务栏上显示主题

默认情况下,Windows11上的标题栏颜色取决于您选择的深色/浅色主题。但是,您可以将其更改为所需的任何颜色。在本指南中,我们将讨论三种方法的分步说明,以更改它并个性化您的桌面体验,使其具有视觉吸引力。是否可以更改活动和非活动窗口的标题栏颜色?是的,您可以使用“设置”应用更改活动窗口的标题栏颜色,也可以使用注册表编辑器更改非活动窗口的标题栏颜色。若要了解这些步骤,请转到下一部分。如何在Windows11中更改标题栏的颜色?1.使用“设置”应用按+打开设置窗口。WindowsI前往“个性化”,然

任务栏缩略图可能很有趣,但它们也可能分散注意力或烦人。考虑到您将鼠标悬停在该区域的频率,您可能无意中关闭了重要窗口几次。另一个缺点是它使用更多的系统资源,因此,如果您一直在寻找一种提高资源效率的方法,我们将向您展示如何禁用它。不过,如果您的硬件规格可以处理它并且您喜欢预览版,则可以启用它。如何在Windows11中启用任务栏缩略图预览?1.使用“设置”应用点击键并单击设置。Windows单击系统,然后选择关于。点击高级系统设置。导航到“高级”选项卡,然后选择“性能”下的“设置”。在“视觉效果”选

您是否在Windows安装程序页面上看到“出现问题”以及“OOBELANGUAGE”语句?Windows的安装有时会因此类错误而停止。OOBE表示开箱即用的体验。正如错误提示所表示的那样,这是与OOBE语言选择相关的问题。没有什么可担心的,你可以通过OOBE屏幕本身的漂亮注册表编辑来解决这个问题。快速修复–1.单击OOBE应用底部的“重试”按钮。这将继续进行该过程,而不会再打嗝。2.使用电源按钮强制关闭系统。系统重新启动后,OOBE应继续。3.断开系统与互联网的连接。在脱机模式下完成OOBE的所

在Windows11上的显示缩放方面,我们都有不同的偏好。有些人喜欢大图标,有些人喜欢小图标。但是,我们都同意拥有正确的缩放比例很重要。字体缩放不良或图像过度缩放可能是工作时真正的生产力杀手,因此您需要知道如何对其进行自定义以充分利用系统功能。自定义缩放的优点:对于难以阅读屏幕上的文本的人来说,这是一个有用的功能。它可以帮助您一次在屏幕上查看更多内容。您可以创建仅适用于某些监视器和应用程序的自定义扩展配置文件。可以帮助提高低端硬件的性能。它使您可以更好地控制屏幕上的内容。如何在Windows11

屏幕亮度是使用现代计算设备不可或缺的一部分,尤其是当您长时间注视屏幕时。它可以帮助您减轻眼睛疲劳,提高易读性,并轻松有效地查看内容。但是,根据您的设置,有时很难管理亮度,尤其是在具有新UI更改的Windows11上。如果您在调整亮度时遇到问题,以下是在Windows11上管理亮度的所有方法。如何在Windows11上更改亮度[10种方式解释]单显示器用户可以使用以下方法在Windows11上调整亮度。这包括使用单个显示器的台式机系统以及笔记本电脑。让我们开始吧。方法1:使用操作中心操作中心是访问

许多用户在选择智能手表的时候都会选择的华为的品牌,其中华为GT3pro和GT4都是非常热门的选择,不少用户都很好奇华为GT3pro和GT4有什么区别,下面就就给大家介绍一下二者。华为GT3pro和GT4有什么区别一、外观GT4:46mm和41mm,材质是玻璃表镜+不锈钢机身+高分纤维后壳。GT3pro:46.6mm和42.9mm,材质是蓝宝石玻璃表镜+钛金属机身/陶瓷机身+陶瓷后壳二、健康GT4:采用最新的华为Truseen5.5+算法,结果会更加的精准。GT3pro:多了ECG心电图和血管及安
