This article mainly introduces the way to build your own PHP MVC framework, detailed analysis of PHP to build the MVC framework of the specific steps, related operational skills and considerations, the need for friends can refer to the next
This article describes in detail the way to build your own PHP MVC framework. Share to everyone for your reference, as follows:
Objective
When it comes to the MVC framework that writes PHP, the first word you think about-"build wheels", yes, a programmer who doesn't have a lot of skill, writes a PHP framework that's not as good as the ones that come from the hands of the great gods, through time and projects. But I was prepared and did it, mainly because:
Think about all aspects of PHP understand, but their own time to learn PHP is short, the foundation is not solid, a lot of common functions of the parameters are occasionally to check the manual, and for some of the newer PHP features such as namespaces, reflection and so on is just simple to see, and has not been able to actually apply.
PHP is a lot of knowledge and miscellaneous, a common project to live is the business logic code-based, and the framework is a can integrate these points of knowledge together projects.
When I write a framework, will also refer to some of the frameworks I used such as tp/ci/yii, such as the source code, in their own view of the source can also help to understand the framework, more easily accept the future to use the framework.
So the purpose of this wheel is not to build wheels, but to familiarize themselves with the craft in the process of making wheels, to summarize wheel features, and to use wheels better.
If you write a full PHP framework, you need to master a lot of PHP knowledge, such as design patterns, iterators, events and hooks and so on, there are many basic knowledge of the flexible application. I think that these are not fully control, so my step is to build a skeleton, and then refer to the characteristics of different PHP framework, and gradually improve it. Because of the work, but also in the evening to complement the algorithm, network and other programming basis, the PHP framework may only have time to update the weekend, I will be in the framework of functional updates, summarize the use of knowledge points, update the blog post.
First put on the framework of the current source:Github/zhenbianshu
Framework Overall
First summarize the workflow of the PHP MVC framework:
Simply put, it takes a portal file to accept the request, select the route, process the request, and return the result.
Of course, a few words summed up the things actually to do a lot of work, the PHP framework will every time the request, the definition of constants, load configuration files, base classes, according to the URL of the access to logical judgment, select the corresponding (module) controller and method, and automatically load the corresponding class, after processing the request, The framework selects and renders the corresponding template file, returning the response as an HTML page. When dealing with logic, you also need to consider the handling of errors and exceptions.
1, as the MVC framework, must have a unique portal file to command the global, all access requests will first enter the portal file, such as the index.php of my framework root, in which I define the basic folder path, the current environment, and according to the current environment to define the level of error reporting.
2, loading additional files in PHP, using require and include, they are to load the contents of the target file into the current file, replace the require or include statements, require is loaded to execute, and include is loaded to execute when needed , and their _once structures represent only one execution at a time when they are written multiple times.
3, the framework of configuration variables, such as using a dedicated configuration file to save, here I modeled the TP in the array return method, using a compileConf()
function to parse the array, the key of the array is defined as a constant, the value is the value of the group.
if (!function_exists (' compile_conf ')) { function compileconf ($conf) { foreach ($conf as $key = = $val) { if (Is_array ($val)) { compileconf ($val); } else{ define ($key, $val);}}} Compileconf (require_once conf_path. ' config.php ');
Namespaces and automatic loading
Why put namespaces and auto-loading in one piece?
In a PHP project, when classes are particularly high, it can be confusing if the class name repeats, and a file with the same name cannot exist in the same folder, so the namespace and folder will be paired. A folder is a box, a namespace that I understand as a label, a box corresponding to a label. When we define a class, we put all kinds of classes in different boxes and affix the corresponding labels to each other. When loading classes automatically, we can easily find the corresponding box (folder) based on the tag (namespace) and find the corresponding class file.
And the automatic loading of classes, we know the __autoload () Magic function, which will be automatically called when you instantiate an object not found in the current path, according to the class name passed in, loading the corresponding class file in the function body.
Now we use the Spl_autoload_register () function, which can register multiple functions instead of the function of the __autoload function, we pass in a function named parameter, Spl_autoload_register will push this function into the stack, When instantiating a class that is not found within the current path, the system will call the function out of the stack in turn until the instantiation succeeds.
Spl_autoload_register (' Sqier\loader::autoload '); class Loader {public static function AutoLoad ($class) { //if any, Remove the leftmost \ $class of the class = LTrim ($class, ' \ \ '); Gets the path full name of the class $class _path = str_replace (' \ \ ', '/', $class). EXT; if (File_exists (Sys_path. $class _path)) { include sys_path. $class _path; return; } if (File_exists (App_path. $class _path)) { include app_path. $class _path; return;} }
Now the loader class is still a simple class, to be gradually perfected later.
Route selection
The next step is routing, which essentially chooses the appropriate method to parse the incoming URI according to the current defined global URL pattern, loads the corresponding class, and implements the corresponding method.
Class Router {public static $uri;p ublic static function bootstrap () {self :: $uri = $_server[' Request_uri ']; Switch (url_mode) {case 1: { self::rboot (); break; } Default: { self::rboot ();}} } public static function RBoot () { $router = isset ($_get[' R '))? Explode ('/', $_get[' R ']): [ ' index ', ' Inde X ' ]; $cName = ' controller\\ '. Ucfirst ($router [0]); $aName = Isset ($router [1])? Strtolower ($router [1]). ' Action ': ' Indexaction '; $controller = new $cName (); $controller-$aName (); }}
This way, after I enter zbs.com/index.php?r=index/login in the Address bar, the system will automatically call the login method under/app/controller/index.php. Complete such a simple route.
Phase Summary:
Next I will optimize the existing tool class, add the display layer, add the database class, and will be some other framework of very cool features to be ported in ~
Next (the code is updated), continue to refine the framework (ii):
For this update, I would like to say:
① This framework by I pick time to improve, and I am not a PHP big God-level characters, so the framework is unavoidable, ask the great gods point out.
② This framework of Knowledge point application will be written in the blog, we have any objection can be discussed together, but also hope to see the blog can learn them.
③ This update, updated the function specification of some problems, such as the function as far as possible, each function as far as possible to do one thing alone, to minimize the function of dependence. It also optimizes the overall framework, adding the SQ global class to handle global functions, variables.
callback function
Replace the very low class name assembly instantiation, and then assemble the use of the method name, using the PHP callback function method:
Original code:
$controller _name = ' controller\\ '. Self:: $c _name, $action _name = self:: $a _name. ' Action '; $controller = new $controller _name (); $controller, $action _name ();
Post-Modification Code
$controller _name = ' controller\\ '. Self:: $c _name; $controller = new $controller _name (); Call_user_func ([ $controller, Self :: $a _name. ' Action ']);
Here is a description of the PHP function callback application:Call_user_func and Call_user_func_array:
Call_user_func (Callback $function [, Mixed $parameter [, mixed $ ...])
Invokes the user-defined function provided by the first parameter.
Return value: Returns the result of the calling function, or false.
call_user_func_array()
is similar to Call_user_func, except that the passed parameter params is a whole array.
In addition, the Call_user_func series function can also pass in the first parameter to pass in the anonymous parameter, can be very convenient to callback some events, these features in the complex framework of the application is also very extensive, such as the Yii2 event mechanism of the use of callback function is based on this.
View Layer and OB functions
The framework defines the Render method in the controller's base class to render the page, which invokes the static function of the Class View to parse the template that loads the corresponding page.
public static function display ($data, $view _file) { if (Is_array ($data)) { extract ($data);//extract function Parsing $ Variables in the data array }else { //throws variable type exception } Ob_start (); Ob_implicit_flush (0); Include Self::checktemplate ($view _file);//Custom checktemplate function, analysis check corresponding function template, normal return path $content = Ob_get_clean (); echo $content;}
Here is the emphasis on the OB (output buffering) series functions, which refer to the function of the brief generation of magic OB:
① prevents errors caused by using Setcookie or Header,session_start functions after the browser has output. In fact, this usage is less good, develop good code habits.
② captures the output of some unreachable functions, such as phpinfo will output a whole bunch of HTML, but we can't use a variable such as $info=phpinfo () to capture, and the OB works.
③ processes the output content, such as gzip compression, for example, for simple conversion, such as some string substitution.
④ generates a static file, which is actually capturing the output of the entire page, and then saving it as a file, often in HTML generation or full page caching.
After the Ob_start () function executes, the buffer is opened, the following output is loaded into the buffer of the system, the Ob_implicit_flush (0) function closes the absolute swipe (ECHO, etc.), and the contents of the buffer are finally taken out using the Ob_get_clean () function.
Class __url__ constants and global classes
TP in the __url__ and other global constants with very convenient, can be very simple to achieve jump and other operations, and define its function CreateURL function I want to reuse, and then draw on Yii's global class definition method:
Define the base class and the detailed method (the future global method will be written here)
The class basesqier{ //method returns a URL string based on the incoming $info information, and the current Url_mode parsing the public static function CreateURL ($info = ") { $ Url_info = explode ('/', Strtolower ($info)); $controller = Isset ($url _info[1])? $url _info[0]: Strtolower (CONTROLLER); $action = Isset ($url _info[1])? $url _info[1]: $url _info[0]; Switch (url_mode) {case Url_common: return '/index.php?r= '. $controller. '/' . $action; Case Url_rewrite: return '/'. $controller. '/' . $action;}}}
Define the class in the startup file and inherit the base class;
Require_once sq_path. ' basesqier.php '; class SQ extends basesqier{}
You can use the SQ::createUrl()
method directly to create a URL in the global context. This makes it easy to define __url__ constants.
Define a database connection base class with a singleton pattern
Class Db { protected static $_instance; public static function getinstance () { if (!) ( Self::$_instance instanceof Self) { self::$_instance = new self (); } return self::$_instance; } Private Function __construct () { $link = new \mysqli (Db_host, Db_user, Db_pwd, db_name) or Die ("Connection database failed, check database configuration information!") ; $link->query (' Set names UTF8 '); } Public Function __clone () { return self::getinstance (); }}
The core of using singleton mode is:
① the privatization constructor so that the object cannot be created with new, and the subclass inherits it and overrides its constructor;
② holds the current object with a static variable, defines a static method to return an object, such as an object that has not yet been instantiated, instantiates one, deposits a static variable, and returns.
③ constructs its __clone magic method to prevent clone from a new object;
SQL query function for DB class
The DB query function is a very complex part, it is a self-made system of things, such as TP and Yii Query method has its unique place. For the time being, I borrowed the model base class of TP for a while, and then I can fill this up slowly.
Well, to introduce the implementation of the method in a query like TP, the trick is to return the processed query object with return this at the end of each joint search method.
Phase Summary:
The mapping between the data table in the YII2 and the Model class property is cool (though it's been deep), and the module I've been avoiding before (module, I can imagine the trouble of parsing when adding it to the URI) has time to think about it.
Next, continue to refine the framework (iii)
The main contents of this update are:
① introduces the mechanism of exception handling
② Perfect Exception and error handling
③ data table with model class mapping
Exception handling
Exception handling: Exception handling is a mechanism in a programming language or computer hardware that handles anomalies in software or information systems (i.e., certain special conditions that are beyond the normal execution of a program)
Exception handling is used to handle the exception condition in the program, although the "abnormal state", but still in the program writer's expected, in fact, the program's exception handling can be replaced by the ' if Else ' statement, but exception handling naturally has its advantages.
The individual summarizes its advantages as follows:
① can quickly terminate the process, reset the system state, clean up variables and memory consumption, and in a normal Web application, fast CGI automatically cleans up variables and contexts after a single request is completed, but it can be handy if you execute a daemon script in PHP's command-line mode.
② a large number of if else statements can make the code confusing, and using exception handling makes the logic of the program clearer, after all, the entry that handles the exception is only a catch statement.
③ the function in a volume program has abnormal results or conditions, if you use the return method of the function returns the exception information, layer up, each time to make a return judgment. Using exception handling we can assume that all return information is normal and avoids a lot of code duplication.
While putting code in a try catch block can be slightly inefficient, it is less expensive than the advantages. So how do you use exception handling in PHP?
PHP has a built-in exception class that allows us to throw exceptions by instantiating exception classes. We put the code in a try statement and then use catch to try to catch the exception thrown in the try code block and handle the exception. We can also use the finally statement block after the catch snippet, whether or not an exception will execute the code of the finally code block, as in the following code:
try{ throw new exeption (' msg ' [, ' Code ', $previous _exeception]);} catch (Exeption $var) { process ($var);} catch (MyException $e) { process ($e)}finally{ dosomething ();}
With the try Catch statement, you need to be aware that:
① when we throw an exception, we instantiate an exception class, which can be defined by itself, but in a catch statement, we need to specify the class name of the exception object to be captured, and only the exception object of the particular class is captured. Of course, we can catch an exception base class (PHP built-in exception class) at the end to ensure that exceptions can be caught.
② when an exception is thrown, the program is terminated, and the backtracking code finds the first catch statement that captures it, and the try Catch statement can be nested, and the CACTH statement, as shown in the preceding code, can be defined more than once.
The ③ finally block executes after the try catch block finishes, even if return is returned using return in the Try Catch block, and the program is not executed to the last.
Exception handling in the framework
Having said so much about the anomaly (and of course explaining it to understand and use the framework), how does it work in the framework?
Overriding exception classes
We can rewrite the exception class to refine its internal methods:
<?phpclass exception{ protected $message = ' Unknown Exception '; Exception information protected $code = 0; Exception code protected $file; The file name of the exception that occurred protected $line; The code line number where the exception occurred function __construct ($message = null, $code = NULL, $previous _exeception = null); Final function getMessage (); Returns the exception information final function GetCode (); Returns the exception code final function GetFile (); Returns the file name of the exception that occurred final function getLine (); Returns the line number of the code where the exception occurred final function gettrace (); Returns an exception trace array final function gettraceasstring (); Return exception trace Information /** * Record error log */ protected function log () { Logger::d ebug ();} }
As above, the final method is not to be rewritten, besides, we can define our own methods, such as logging the Exception log, like my custom log method, in the catch code block, you can directly use $e->log to record an exception log.
Registering Global Exception methods
We can use Set_exception_handler (' Exceptionhandler ') to globally capture an exception that is not caught by a catch block, and this exception handler needs to pass in an exception handling object to parse this exception handling information. Avoids the system to appear the non-humanized prompt, enhances the frame robustness.
function Exceptionhandler ($e) { echo ' has an uncaught exception, in '. $e->getfile (). "Of the". $e->getline (). "Line!";}
Other global functions
By the way, other global processing functions:
① set_shutdown_function('shutDownHandler')
to execute the function at the end of the script, this function is called automatically even after the error has ended.
② is set_error_handler('errorHandler')
called automatically when an error occurs in PHP, note that an error must be made before the error function is registered. The function parameter form should be($errno, $errstr, $errfile, $errline);
Note, however, that these global functions need to be re-registered before the code snippet has been defined.
ActiveRecord Mapping of data tables and model classes
First use YII2 Activcerecord class feel good convenient, only need to define its field with the same name property and then call the Save method OK (good magic AH), it is how to achieve it, read the next source, understand its approximate implementation process (base class).
1. Use the ' describe table_name ' query statement;
2. Parse the query result: For each field, there is field (field name), type (data type), null (is empty), Key (index information, ' PRI ' represents the primary key), Default, Extra (additional information, such as Auto_increment)
3. By judging its primary key ($row [' key '] = = ' PRI ') information, save to see if there is primary key information, if present, is updated; not present, insert.
4. In addition, the parsed field information is more magical ~ ~