[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两个类了。

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



The message "Your organization has asked you to change your PIN" will appear on the login screen. This happens when the PIN expiration limit is reached on a computer using organization-based account settings, where they have control over personal devices. However, if you set up Windows using a personal account, the error message should ideally not appear. Although this is not always the case. Most users who encounter errors report using their personal accounts. Why does my organization ask me to change my PIN on Windows 11? It's possible that your account is associated with an organization, and your primary approach should be to verify this. Contacting your domain administrator can help! Additionally, misconfigured local policy settings or incorrect registry keys can cause errors. Right now

Windows 11 brings fresh and elegant design to the forefront; the modern interface allows you to personalize and change the finest details, such as window borders. In this guide, we'll discuss step-by-step instructions to help you create an environment that reflects your style in the Windows operating system. How to change window border settings? Press + to open the Settings app. WindowsI go to Personalization and click Color Settings. Color Change Window Borders Settings Window 11" Width="643" Height="500" > Find the Show accent color on title bar and window borders option, and toggle the switch next to it. To display accent colors on the Start menu and taskbar To display the theme color on the Start menu and taskbar, turn on Show theme on the Start menu and taskbar

By default, the title bar color on Windows 11 depends on the dark/light theme you choose. However, you can change it to any color you want. In this guide, we'll discuss step-by-step instructions for three ways to change it and personalize your desktop experience to make it visually appealing. Is it possible to change the title bar color of active and inactive windows? Yes, you can change the title bar color of active windows using the Settings app, or you can change the title bar color of inactive windows using Registry Editor. To learn these steps, go to the next section. How to change title bar color in Windows 11? 1. Using the Settings app press + to open the settings window. WindowsI go to "Personalization" and then

Do you see "A problem occurred" along with the "OOBELANGUAGE" statement on the Windows Installer page? The installation of Windows sometimes stops due to such errors. OOBE means out-of-the-box experience. As the error message indicates, this is an issue related to OOBE language selection. There is nothing to worry about, you can solve this problem with nifty registry editing from the OOBE screen itself. Quick Fix – 1. Click the “Retry” button at the bottom of the OOBE app. This will continue the process without further hiccups. 2. Use the power button to force shut down the system. After the system restarts, OOBE should continue. 3. Disconnect the system from the Internet. Complete all aspects of OOBE in offline mode

Taskbar thumbnails can be fun, but they can also be distracting or annoying. Considering how often you hover over this area, you may have inadvertently closed important windows a few times. Another disadvantage is that it uses more system resources, so if you've been looking for a way to be more resource efficient, we'll show you how to disable it. However, if your hardware specs can handle it and you like the preview, you can enable it. How to enable taskbar thumbnail preview in Windows 11? 1. Using the Settings app tap the key and click Settings. Windows click System and select About. Click Advanced system settings. Navigate to the Advanced tab and select Settings under Performance. Select "Visual Effects"

We all have different preferences when it comes to display scaling on Windows 11. Some people like big icons, some like small icons. However, we all agree that having the right scaling is important. Poor font scaling or over-scaling of images can be a real productivity killer when working, so you need to know how to customize it to get the most out of your system's capabilities. Advantages of Custom Zoom: This is a useful feature for people who have difficulty reading text on the screen. It helps you see more on the screen at one time. You can create custom extension profiles that apply only to certain monitors and applications. Can help improve the performance of low-end hardware. It gives you more control over what's on your screen. How to use Windows 11

Many users will choose the Huawei brand when choosing smart watches. Among them, Huawei GT3pro and GT4 are very popular choices. Many users are curious about the difference between Huawei GT3pro and GT4. Let’s introduce the two to you. . What are the differences between Huawei GT3pro and GT4? 1. Appearance GT4: 46mm and 41mm, the material is glass mirror + stainless steel body + high-resolution fiber back shell. GT3pro: 46.6mm and 42.9mm, the material is sapphire glass + titanium body/ceramic body + ceramic back shell 2. Healthy GT4: Using the latest Huawei Truseen5.5+ algorithm, the results will be more accurate. GT3pro: Added ECG electrocardiogram and blood vessel and safety

Screen brightness is an integral part of using modern computing devices, especially when you look at the screen for long periods of time. It helps you reduce eye strain, improve legibility, and view content easily and efficiently. However, depending on your settings, it can sometimes be difficult to manage brightness, especially on Windows 11 with the new UI changes. If you're having trouble adjusting brightness, here are all the ways to manage brightness on Windows 11. How to Change Brightness on Windows 11 [10 Ways Explained] Single monitor users can use the following methods to adjust brightness on Windows 11. This includes desktop systems using a single monitor as well as laptops. let's start. Method 1: Use the Action Center The Action Center is accessible
