[Php] application controller (1) & nbsp; the front-end controller can process requests in one place and select the appropriate Command, however, the Command subclass object handles the assignment of views by itself. If you can use a class (status returned after processing by Command & #20540 [php] application controller (1)
The front-end controller can process requests in a centralized manner and select an appropriate Command. However, the sub-object of the Command class handles the assignment of views by itself. If you can use a class (the status value returned after Command processing) to determine the view and return it to the front-end controller, then the front-end controller calls the view display, in this way, the front-end controller is in the middle of the view layer and the business layer, and the Command and view are well separated. Application controller is a good solution.
The application controller maps requests to commands and maps commands to views. It allows you to change the application process without modifying the core code. It frees the Command class and enables the Command class to focus on its work, including processing input, calling application logic, and processing results.
An application controller is a class (or a group of classes) that helps the front-end control take over the request processing tasks and return the appropriate view to the front-end controller for calling. So how does application control run? It uses an xml configuration file to determine how Command and view work. For example, the following xml file (a bit like struts ):
sqlite://data/demo.db
root
root
main
main
error
list_students
add_student
ListStudents
simple_add_student
We can see the xml Can contain , , The three sub-elements represent the view corresponding to the Command, the status value after the Command processes the service, and the jump after the Command process (jump to another Command here ).
From the xml structure, we can see that the Command class requires some new attributes.
Namespace demo \ command;/*** abstract parent class */abstract class Command {// state value ing private static $ STATUS_STRINGS = array ('cmd _ default' => 0, 'command _ OK '=> 1, 'command _ error' => 2, 'command _ INSUFFICIENT_DATA' => 3); // The current status value is private $ status = 0; public final function _ construct () {// The subclass cannot override the constructor}/*** returns the status value by status string * @ param unknown_type $ staStr */public static function status ($ stauStr = 'cmd _ default') {if (empty ($ stauStr) {$ stauStr = 'cmd _ Default';} return self: $ STATUS_STRINGS [$ stauStr];} /*** call doExecute implemented by subclass * @ param \ demo \ controller \ Request $ request */public function execute (\ demo \ controller \ Request $ request) {$ this-> doExecute ($ request);} protected abstract function doExecute (\ demo \ controller \ Request $ request );}
In the system, ApplicationHelper, a helper class for configuration retrieval, can read the xml configuration. Because the element structure in xml is relatively flexible, a ControllerMap is required to manage the one-to-one ing between values and commands and views of each element.
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;}}
First, you must obtain the getOptions for configuring the ApplicationHelper class in xml.
Namespace demo \ controller;/*** helper class: get xml configuration * Singleton mode **/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 () {// Get the initialization configuration from the serialization file $ dsn =\demo \ base \ ApplicationRegistry: getInstance ()-> ge TDSN (); $ camp = \ demo \ base \ ApplicationRegistry: getInstance ()-> getControllerMap (); if (is_null ($ dsn) | is_null ($ camp )) {$ this-> getOptions () ;}/ *** get xml configuration */public function getOptions () {// xml $ this-> ensure (file_exists ($ this-> config), "cocould not find options file! "); $ Options = @ simplexml_load_file ($ this-> config); var_dump ($ options); $ 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 );//
$ Map = new ControllerMap ();//
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 );}//
Foreach ($ options-> controller-> command as $ cvf) {$ cmd = trim (string) $ cvf ['name']); //
If ($ cvf-> classalias) {$ classroot = (string) $ cvf-> classalias ['name']; $ map-> addClassroot ($ cmd, $ classroot );} //
,
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 );}}//
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 );}}}
Obtaining xml configuration is a time-consuming operation. you can first serialize the ControllerMap object to a file, and then obtain it through ApplicationRegistry and cache it as global data.
/*** Application scope */class ApplicationRegistry extends Registry {private static $ instance; private $ freezedir = ". /data "; // hard encoding here. configure private $ values = array (); private $ mtimes = array (); private function _ construct () according to the actual situation () {} public static function getInstance () {if (isset (self: $ instance) = false) {self: $ instance = new self ();} return self :: $ instance;}/*** get $ key data from the serialized file */protected fun Ction get ($ key) {$ path = $ this-> freezedir. DIRECTORY_SEPARATOR. $ key; if (file_exists ($ path) {// clear the file cache clearstatcache (); $ mtime = filemtime ($ path ); if (isset ($ this-> mtimes [$ key]) = false) {$ this-> mtimes [$ key] = 0;} // The file has been recently modified, re-deserialize new data 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');}/*** get AppController */public function getAppController () {$ obj = self: instance (); if (! Isset ($ obj-> appController) {$ cmap = $ obj-> getControllerMap (); $ obj-> appController = new \ demo \ controller \ AppController ($ cmap );} return $ obj-> appController;} // Other columns: getter and setter //......}
This time we need to implement more complex calls, such as forward, so we need to simply modify the Request class code so that it can meet the call logic needs.
Namespace demo \ controller;/*** encapsulate user requests * Request */class Request {private $ properties; private $ feedback = array (); // save the business object, you can supply the view with private $ objects = array (); // Save the Last executed Command object 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 ;}// method foreach ($ _ SERVER ['argv'] as $ arg) in the command line) {if (strpos ($ arg, '=') {list ($ key, $ val) = explode ('=', $ arg ); $ this-> setProperties ($ key, $ val) ;}} public function filterProperties () {// filter user requests ...} 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 ;}}
Now you have added the ControllerMap class that can save the ing relationship, modified the Request, Command, ApplicationHelper, ApplicationRegistry, mainly to add, a few changes to the previous code.
The above classes have been able to complete system initialization, including reading the xml configuration (ApplicationHelper) and accessing global variables (Registry );
Then there is the core part: Controller and AppController.