Thinkphp Framework Guided class analysis
This type of file is in: thinkphp/library/think/think.class.php
This class can be said to be the core of the thinkphp framework of the class Library, responsible for many configuration loading, registration core system extensions (Automatic loading class library, exception handling, error handling, etc.), management and maintenance of class instances, alias mappings, can be said to be a framework of the factory (the class has a few object-oriented drawbacks, For example: In violation of the object-oriented single responsibility, its responsibility for complex functions, related libraries and documents more, there is a worry of a pull hundred. The functions encountered in the class are thoroughly analyzed after such analysis, and the other class libraries involved are specifically explained.
First, class structure
namespace think;//defines the namespace class Think {private static $_map = Array ();//class library alias mapping private static $_instance = Array ();//Save class instance (This is not reasonable, and so will analyze the function when specified) static public function start () {}//application initialization static public function Addmap ($class, $map = ") {}// Register Classmap static public function getmap ($class = ') {}//get Classmap public static function AutoLoad ($class) {}//Class library auto Load static public Function instance ($class, $method = ') {}//Gets object instance support for calling a class statically appexception ($e) {}//Custom exception handling static public function Apperror ($errno, $errstr, $errfile, $errline) {}//custom error handling static public function FatalError () {}//Fatal error capturing static public function halt ($error) {}//error output static public function trace ($value = ' [Think] ' , $label = ', $level = ' DEBUG ', $record =false) {}//Add and get page trace record}
Second, the application initialization start () method analysis, the method contains a set of error and exception handling mechanism, very useful. This method is used as the boot interface of thinkphp framework to implement error, exception handling, configuration loading, alias mapping, behavior registration, including running cache generation, Web application directory detection, automatic class library loading behavior registration.
/** * Application Initialization * @access public * @return void */static public function start () {//Use SPL standard library to provide __aut The default implementation of the Oload () function, which is more efficient than __autoload (), is more flexible//can use the Spl_autoload_register (array (' Think\think ', ' autoload ')); It is recommended to use Spl_autoload_register (__namespace__. ' \think::autoload '); implementation//All registration methods can be used in the form above 3 to pass parameters Spl_autoload_register (' think\think::autoload '); Registering the global script "destructor", the function registered with this method, is called before the end of the script, most of which is used to handle fatal error register_shutdown_function (' Think\think::fatalerror '); Set a custom error handler to handle error messages Set_error_handler (' Think\think::apperror '); Set non-exception handling function Set_exception_handler (' think\think::appexception '); Register_shutdown_function (), Set_error_handler (), Set_error_handler () 3 functions can be combined to complete a custom, diversified error handling module//According to Storage_ The value of TYPE sets the Distributed file storage scheme, Storage is a factory class that is used to manage and maintain distributed file storage components//The Storage class is explained in detail later, and the design defect Storage::connect (Storage_type) is pointed out; Generate the compiled cache file under run cache directory according to the run mode App_mode. ' ~runtime.php ', thus reducing IO overhead//The way in which the cache file is generated is described in detail below $runtimefile = Runtime_path. App_mode. ' ~runtime. php '; If it is not in debug mode and the compiled cache file exists, load the compiled cache directly if (! App_debug && Storage::has ($runtimefile)) {storage::load ($runtimefile); }else{//Determine if the compiled cache file exists, delete if (Storage::has ($runtimefile)) Storage::unlink ($runtimefile); Precompiled content variable $content = '; Determine if there is a run mode profile and load Mode_path if it does not exist. App_mode. '. PHP ', run-mode profiles will affect the following loading different class libraries and configuration//Run profiles later will be explained in detail $mode = include is_file (conf_path. ' core.php ')? Conf_path. ' core.php ': Mode_path. App_mode. '. PHP '; All of the following configuration item loads overwrite previous configuration items according to the order of loading, typically loading the thinkphp default configuration, loading the application configuration//core subscript to determine the core class and function file to load foreach ($mode [' core '] as $file) { if (Is_file ($file)) {include $file; If it is not debug mode, the contents of the file are compiled and stored in the precompiled content variable if (! App_debug) $content. = Compile ($file); }}//config the subscript determines the core profile to be loaded foreach ($mode [' config '] as $key = + $file) {//determines if the subscript is a number, and if not, the configuration item in the configuration file is loaded into the corresponding Key, which is equivalent to adding a latitude is_numeric ($key) to the configuration item? C (include $file): C ($key, include $file); }//If it is not normal run mode, then determine if there is a run Mode app configuration file if (' common '! = App_mode && is_file (conf_path. ' Config_ '. App_mode. '. php ') C (include Conf_path. ' Config_ '). App_mode. '. php '); Alias subscript record class Library alias mapping rules, thinkphp original alias mechanism, used to improve the efficiency of automatic loading if (isset ($mode [' Alias ')]) {//By this code can see the alias rule can be an array, Or separate the rule array as a file Self::addmap (Is_array ($mode [' Alias '])? $mode [' Alias ']:include $mode [' Alias ']; }//load the aliases defined in the app configuration if (Is_file (conf_path. ' alias.php ')) Self::addmap (include Conf_path. ' alias.php '); tags subscript is used to identify the system behavior, the behavior extension specifically by the Hook Hook class implementation if (Isset ($mode [' tags ']) {//By this code can see the tags rule can be an array, or the rule array as a single file Hook::impor T (Is_array ($mode [' tags '])? $mode [' Tags ']:include $mode [' tags ']); }//load the behavior extension in the app configuration if (Is_file (conf_path. ' tags.php '))//allow the app to increase the development mode configuration definition Hook::import (include Conf_path. ' Tags. php '); Loading the framework's underlying language pack, the Default_lang configuration item in the core configuration file determines L (include Think_path. ' lang/'. Strtolower (C (' Default_lang ')). php '); If not debug mode, generate the compile cache file if (! App_debug) {//namespace {} This way is used to declare that the namespace in the code block belongs to the global namespace//This code is used to generate the PHP code that loads the alias map $content. = "\nnamespace {think\think::addmap (". Var_export (Self::$_map,true). ");"; L (". Var_export (L (), true)."); Build language Load Code//c (". Var_export (C (), true). '); Build configuration item Load code//think\hook::import ('. Var_export (Hook::get (), true). Generate the hook load code $content. = "\nl (". Var_export (), true). "); NC (". Var_export (C (), true). '); Think\hook::import ('. Var_export (Hook::get (), true). ');} '; $content variable content is stripped of comments and wraps, and then written to runtime to compile the cache file Storage::p ut ($runtimefile, Strip_whitespace ('
Third, the class library alias mapping mechanism to implement the Addmap () and Getmap () method analysis; This mechanism uses THINK::_MAP variables to store alias mapping records, add or modify alias mapping records by Think::addmap (), use Think:getmap () Gets the alias mapping record.
/** * Register or modify class Library alias mapping Records * @access Public * @param class String| Array if the array key is a class library alias, the key value is the actual location of the class library, or if the string represents the class library alias * @param map String If class is a string, this parameter represents the actual location of the class library, otherwise meaningless * @return void */static Public function Addmap ($class, $map = ") {//Determines whether class is an array, if it is, merges the current class library alias record, if the same record overwrites if (Is_array ($class ) {Self::$_map = Array_merge (Self::$_map, $class); }else{//If class is not an array, it is stored as an alias in the class library alias record, and the map is used as the actual class library location self::$_map[$class] = $map; }}/** * Gets the actual address of the class library according to the alias * @param class String class Library alias * @return array| String| Null if class is empty gets all class library alias records, no returns the class library location of the alias */static Public function getmap ($class = ") {///If class is empty, return all class library alias Records directly if ("= = = $class) {return self::$_map; Determine if the corresponding alias is in the alias mapping record, if there is a return class library physical address, no return null}elseif (Isset (self::$_map[$class])) {return self::$_map[$class]; }else{return null; } }
IV, thinkphp class library automatic loading mechanism autoload () method analysis, the method is the Think::start () method in the first sentence Code registration implementation Spl_autoload_register (' Think\ Think::autoload ');
/** * Class Library automatically loaded * @param string $class object class name * @return void */public static function AutoLoad ($class) { Determine if there is an alias mapping//Tag a bug, if using Think::addmap (' think\test '), registration alias is over, the program logic is not rigorous, there is no big security issues, can ignore//specific can see Think::add The implementation of the MAP () method if (Isset (self::$_map[$class])) {include self::$_map[$class]; Suggest to Renturn here; Determine if the \ symbol exists, the use of the namespace loading mechanism//here with the configuration instructions do not match ' app_autoload_path ' = ',//automatic loading of the path after closing app_use_namespace valid// Now determine whether to use the naming convention in the class name instead of using the App_use_namespace configuration, of course, the configuration is primarily in the routing module, followed by the}elseif (Strpos ($class, ' \ \ ')) {//Get the first life of the namespace Name Range $name = strstr ($class, ' \ \ ', true); Determines whether the first named range of the namespace is in the Think contract scope class and exists in the library directory if the directory if (In_array ($name, Array (' Think ', ' Org ', ' Behavior ', ' Com ', ' Vendor ')) | | Is_dir (Lib_path. $name)) {//library directory below the namespace auto-location $path = Lib_path; }else{//Detect custom namespaces Otherwise, the module is the namespace $nAmespace = C (' Autoload_namespace '); $path = Isset ($namespace [$name])? DirName ($namespace [$name]). ' /': App_path; }//You can see that the naming convention for the thinkphp namespace is the directory principle with Lib_path and App_path as the root directory, or you can customize the naming rule root for autoload_namespace configuration items $filename = $path. Str_replace (' \ \ ', '/', $class). EXT; Determine if the file exists, why not to determine the name of the file when registering the existence of it? Does that make it possible to ignore some problematic alias mappings and use the correct loading rules? if (Is_file ($filename)) {//If running in a Windows environment, use STRPOS to detect case-consistency issues if inconsistent directly returns if (Is_win && false = = = Strpos (str_replace ('/', ' \ \ ', Realpath ($filename)), $class. EXT)) {return; }//Import class file include $filename; return is recommended here; }}else{//Do not use the value of App_use_namespace in the configuration file to determine whether the item is app_autoload_layer configured//The following rules will be invoked as long as the namespace is not used in the class library load To find the class library (the class to instantiate cannot declare a naming convention) foreach (Explode (', ', C (' App_autoload_layer ')) as $layer) {//Determine if the last class name conforms to the App_autoload_layer configuration item if (SUBSTR ($class,-strlen ($layer)) = = $layer) {//load the corresponding class under the current module File, this can actually directly determine whether the file exists, and use the include to load, there is no need to call the Require_cache function//above is a personal opinion, because the above loading mechanism is not used, I think should be unified, and automatically load the class file, do not limit System is loaded once, and this method is not called if it is already loaded. if (Require_cache (Module_path. $layer. /'. $class. EXT)) {//load file successfully returned directly; The file is automatically searched and loaded according to the path rules of the App_autoload_path configuration settings fore Ach (Explode (', ', C (' App_autoload_path ')) as $path) {//here is the same as, is it possible to load or unify without calling the import () method? if (Import ($path. '. '). $class))//returns return if the Load class succeeds; } } }
V. Management class instance or ' Cache ' class static method call result instance () method analysis, it is not reasonable to say that the $_instance variable holds the class instance in the class structure analysis, because the class can also invoke the static method of the class and ' cache ' the result. I put some personal insights into this method annotation, not that the design of the method is unreasonable, this is thinkphp proprietary method, any project design will not like me to consider a method of many problems, The first question is whether it is sufficient to solve the project application. Also see this article friends can think, in the project whether there are many classes do not need multiple instances (without mandatory constraints), if there is a can design a pseudo-singleton factory for their own projects to manage the instances of these classes.
/** * Get object instance support call class static method * parsing: I think of this class as a pseudo-singleton factory, not strictly strict class is not a single case, uniform use of this method to get class objects, you can implement a singleton mode (very suitable for PHP this more flexible language) * said this is a singleton mode factory does not Qualified because there is no strict requirements the managed classes must conform to the singleton pattern constraints. * Problem: This method says that this class can invoke a static method of a class, and there is no convention that a static method must return an instance of the Class (self), which can return arbitrary results, which surprises me. * If you want to cache the result of a class method call, should I provide the method pass parameter option? * If you are only trying to control instances of a class (such as a class that is designed strictly according to a singleton pattern, if you want to manage it, you must use a static method), should you indicate or detect the return value in the method? * Doubt: This is not the method provided to the application (of course, can be used, the original design is certainly not, this is the Thinkphp development team established rules (verbal restraint use) it? * This at least gives us a revelation that you can manage the pseudo-singleton pattern (verbally constrained way, of course, more reliably than that), before I had designed such a factory class in a project (at that time did not analyze the source code of any product) * @param string $class object class name * @param the static method name of the string $method class * @return Object */static Public Function instance ($class, $method = ") {//RAW An instance management identity $identify = $class. $method; Determine if there is already a modified instance ID if (!isset (self::$_instance[$identify])) {//Determine if the class exists, here the reaction cannot use the automatic loading mechanism, the class file must be loaded before calling the method if (class_exists ($class)) {//Instantiate class, it can be seen here that the strict singleton class $o = new $class () is not managed; Determines whether a static method is to be called and whether the class exists if the method if (!empTy ($method) && method_exists ($o, $method))//Returns the result of the call (not exactly what it is) self::$_instance[$ide Ntify] = Call_user_func (Array (& $o, $method)); else//Storage class Instance Object self::$_instance[$identify] = $o; } else//Output error message Self::halt (L (' _class_not_exist_ '). ': '. $class); }//Returns the instance object return self::$_instance[$identify]; }
Six, thinkphp built-in error handling and exception handling implementation analysis. The Appexception () method is implemented by Set_exception_handler (' think\think::appexception ') in Think::start (); The Apperror () method is Set_error_handler (' Think\think::apperror ') in Think::start (); Statement implementation, FatalError () method by Think::start () Register_shutdown_function (' think\think::fatalerror '); Statement implementation. The halt () method is used to output important error messages and exceptions, and to terminate program execution. The trace () method is used to record and manage error messages in the Trace debugging tool.
/** * Custom Exception Handling * @access public * @param mixed $e Exception Object * @param void */static public function Appex Ception ($e) {$error = array (); Gets the exception error message $error [' message '] = $e->getmessage (); Get BackTrace () backtracking information $trace = $e->gettrace (); Determines whether the exception handler is thrown, thinkphp custom throws an exception handler if (' E ' = = $trace [0][' function ']) {$error [' file '] = $trace [0][' File '];//get error file $error [' line '] = $trace [0][' lines '];//get error row number}else{$error [' file '] = $e GetFile ();//Get error file $error [' line '] = $e->getline ();//Get Error line number}//Formatted error backtracking information $error [' Tra Ce '] = $e->gettraceasstring (); Write to error log log::record ($error [' message '],log::err); Send 404 message header (' http/1.1 404 Not Found '); Header (' status:404 not Found '); Display error message Self::halt ($ERROR); }/** * Custom error handling * @access public * @param int $errno ErrorType * @param string $ERRSTR error message * @param string $errfile error file * @param int $errline Wrong number of rows * @return void */static Public function Apperror ($errno, $errstr, $errfile, $errline) {switch ($errno) {//* Some important error messages will After the impact of the program execution case e_error:case e_parse:case e_core_error:case e_compile_error: Case E_USER_ERROR://Empty the output buffer (do not know where to open, the analysis will encounter) Ob_end_clean ();//actually is the PHP default output error message erased//error Information $errorStr = "$errstr". $errfile. "$errline line."; Determines whether to write an error log if (C (' Log_record ')) log::write ("[$errno]"), depending on whether Log_record logs an error message. $ERRORSTR, Log::err); Output error message Self::halt ($ERRORSTR); Break Error messages that can be ignored, are not output, are logged to the trace, and can be viewed using the Show_page_trace configuration error message default://Here The program continues to execute, cannot empty the output buffer, if you do not want to display such error messages, you should When adjusting in php.ini, or using Ini_set () function to change//error message $ERRORSTR = "[$errno] $errstr". $errfile. "$errlineLine. "; Record to trace Self::trace ($errorStr, ' ', ' notic '); Break }}//Fatal error capturing static public function FatalError () {//Fatal error must be saved to log log::save (); Gets the last error message, skipping without error (⊙0⊙) if ($e = Error_get_last ()) {//Handling fatal error message switch ($e [' type ']) { Case E_error:case e_parse:case e_core_error:case e_compile_error: Case E_USER_ERROR://Empty output cache, all causes the program to stop, also show what Ah Ob_end_clean (); Output error message Self::halt ($E); Break }}}/** * ERROR output * @param mixed $error error * @return void */static public function halt ($e Rror) {$e = array (); Determines whether the debug mode, or command-line mode if (App_debug | | IS_CLI) {//debug mode output error message//If the error message is not an array, backtrack the last execution method's information if (!is_array ($error)) { $trace = Debug_backtrace (); $e [' message '] = $error; $e [' file '] = $trace [0][' file ']; $e [' line '] = $trace [0][' line ']; Ob_start ();//Start output cache debug_print_backtrace ();//output a backtracking message $e [' trace '] = Ob_get_clean ();//Get Output buffer information, and empty} else {$e = $error; } if (IS_CLI) {//Command line mode, convert to GBK encoding, terminate program execution and output error message exit (Iconv (' UTF-8 ', ' GBK ', $e [' message ']). Php_eol. ' File: '. $e [' file ']. ' ('. $e [' line ']. ') '. Php_eol. $e [' Trace ']); }} else {//not debug mode, redirect to error page $error _page = C (' error_page ');//Get Set error page if (!empty ($error _page)) {//Redirect to error page redirect ($error _page); } else {///depending on the show_error_msg configuration, whether to display verbose error messages or error messages set by Error_message $message = Is_array ($ Error)? $error [' message ']: $error; $e [' message '] = C (' show_error_msg ')? $message : C (' error_message '); }}//According to the Tmpl_exception_file configuration decision to invoke the error message display template, the use of the thinkphp default template $exceptionFile = C (' Tmpl_exception_file ', Null,think_path. ' Tpl/think_exception.tpl '); Include $exceptionFile; It is important to exit;//to terminate the program operation. }/** * Add and Get page Trace records * @param string $value variable * @param string $label tag * @param string $level log level ( or tab for page trace) * @param boolean $record log * @return void */static public function trace ($value = ' [Thin K] ', $label = ', $level = ' DEBUG ', $record =false) {//Use static variable to store trace record static $_trace = Array (); if (' [think] ' = = = $value) {//Get trace information return $_trace; }else{//error message $info = ($label? $label. ': ': '). Print_r ($value, true); $level = Strtoupper ($level);//Convert the error level to uppercase//if it is an AJAX request or does not display the Trace Debug tool, or $record requires logging, the error message is not logged if ((Defined (' Is_ajax ') && is_ajax) | |! C (' show_page_trace ') | | $record) { Log::record ($info, $level, $record);//Writes error information to the log file}else{//determines if the error level exists or if the error message is of the wrong type Record upper limit, configured by Trace_max_record if (!isset ($_trace[$level]) | | count ($_trace[$level]) >c (' Trace_max_record ')) { Here's a place where I'm amazed when the error category record reaches the upper limit of the error type record, why should I reset the type Error record//instead of not logging the current error message, or deleting the error message from the first one, append to the last I suggest is to ignore the more reasonable, because debugging errors, there are also, the first to solve the errors encountered before, you will see the new error (this is a bit pit ⊙0⊙) $_trace[$level] = array (); }//error messages are logged in the wrong category $_trace[$level] [] = $info; } } }
Vii. Summary: For this kind of analysis, the main control of PHP error handling and exception handling knowledge, and understand the rules based on the automatic namespace loading of the definition of the foundation, also exposed to the thinkphp run-time compilation caching mechanism brought about by the IO optimization idea and class library alias mechanism to the class automatically loaded with the optimization. in the analysis of this class, standing in the thinkphp application of this class raised several questions, only for the individual's understanding and cognition of object-oriented design, not as a detailed reference.