YII framework source code analysis
Baidu Alliance Business Department——Huang Yinfeng
Table of Contents
1. Introduction 3
1.1, Yii Introduction 3
1.2. Content and structure of this article 3
2. Componentization and modularization 4
2.1, Framework loading and running process 4
2.2, YiiBase static class 5
2.3, Component 6
2.4, Module 9
2.5, App application 10
2.6, WebApp application 11
3. System components 13
3.1. Log routing component 13
3.2, Url management component 15
3.3. Exception handling component 17
3.4, Cache component 17
3.5, Angular Access Control Component 19
3.6. Global state component 21
4. Controller layer 23
4.1, Action 23
4.2, Filter 24
4.3, Action and Filter execution process 26
4.4. Access control filter 27
5. Model layer 30
5.1, DAO layer 30
5.1.1, Database connection component 30
5.1.2, transaction object 31
5.1.3, Command object 31
5.2, Metadata and Command Constructor 32
5.2.1. Table structure query 32
5.2.2. Query condition object 33
5.2.1, Command constructor 33
5.3, ORM (ActiveRecord) 34
5.3.1, table metadata information 34
5.3.2, single table ORM 34
5.3.3, multi-table ORM 36
5.3.4, CModel and CValidator 37
6. View layer 38
6.1. View rendering process 38
6.2, Widget 39
6.3. Client script component 40
1. Introduction
1.1, Yii Introduction
The author of Yii is Chinese-American "Xue Qiang", who was originally one of the core development members of Prado. In 2008, Xue Qiang started anew and developed the Yii framework, and released the Yii1.0 version on December 3, 2008.
Yii is currently one of the more excellent PHP frameworks. Its supported features include: MVC, DAO/ActiveRecord, I18N/L10N, caching, AJAX support, user authentication and role-based access control, scaffolding, Input validation, widgets, events, theming, web services, and more.
Many of Yii’s ideas refer to some other excellent Web frameworks (Do we also like to refer to other people’s when writing things? Is that true? Hehe, we all like to stand on the shoulders of others and work!) , here is a short list:
|
Reference ideas |
||||||||||||
Prado |
Based on component and event-driven programming model, database abstraction layer, modular application architecture, internationalization and localization, etc. |
||||||||||||
Ruby on Rails |
Configuration ideas, ORM based on Active Record |
||||||||||||
jQuery |
Integrated jQuery |
||||||||||||
Symfony |
Filter design and plug-in architecture |
||||||||||||
Joomla |
Modular design and information translation solution |
1.2. Content and structure of this article
This article conducts an in-depth analysis of the source code of Yii1.1.8 version. The content and structure of this article are: Componentization and modularization: Yii’s base class (CComponent) based on component and event-driven programming model Carry out points
Analysis; analyze the working principles of componentization and modularization; analyze the WebApp application creation Controller process, etc.
System components: Analyze the important components that come with the Yii framework, mainly including: log routing component, Url management component, exception handling component, Cache component, role-based access control component, etc.
Controller layer: The controller mainly includes Action and Filter, and analyzes the working principles of Action and Filter. Model layer: Analyze the principles of DAO layer, metadata, Command constructor, and ORM
View layer: Analyze the rendering process, Widget and client script components of the view layer
Errors or inadequacies in this document are inevitable. We sincerely hope that readers of this document will criticize and correct us!
2. Componentization and modularization
2.1, Framework loading and running process
start
Load YiiBase.php 1. Install the autoload method to prepare for instantiation of the class 2. Obtain the paths of all classes in the framework (208 classes in total in Yii1.1.8)
Create Action object based on ActionId 1. Find Action from member function 2. Find Action from ActionMap
Throw
Load request component
(Get,Post,Cookie)
Create Action No Is it successful? often
Create WebApp instance 1. Initialize aliases: application, webroot, ext 2. Install exception handling handles, and hand them over to the exception handling component when an exception is thrown 3. Configure core components: urlManager, errorHandler, session, db, etc. 4. Configure components, sub-modules, WebApp member attributes, etc. based on configuration information 5. Load preload components: log components, request components, etc.
Run WebApp
1. Trigger onBeginRequest event
Load the Url management component, analyze the url according to the configuration information, and parse the route: route=ControllerId/ActionId
Create a controller based on ControllerId 1. Find from ControllerMap 2. Find from submodule 3. Find from ControllerPath
Create Controller Throw Successfully? No Different
Often
Create all Filter objects of the current Action according to the filters() configuration
Run the preFilter method of Filter1 Run the preFilter method of Filter2
Check whether the parameters of Get parameter and Action are different
Consistent
Run Action
Partial render
Render the core part of html
Layout render
Render the overall html
|
|||||
echo html
2. Processing requests
Run Controller
Run the postFilter method of Filter2 Run the postFilter method of Filter1
3. Trigger onEndRequest event
Registered handle:
Exception handling component
Exception thrown at XX
throw Exception. . .
end
For example, recording logs
The Yii framework loading and running process is divided into 4 stages (it may look a little scary, but it doesn’t matter, let’s get a rough idea first):
Step1: WebApp initialization and running
1.1. Load YiiBase.php, install the autoload method; load the user's configuration file;
1.2. Create a WebApp application, initialize the App, load some components, and finally execute WebApp
Step2: Controller initialization and operation
2.1. Load the request component, load the Url management component, and obtain routing information route=ControllerId/ActionId 2.2. Create a controller instance and run the controller
Step3: Controller initialization and operation
3.1. Create Action
based on routing3.2. Create the Filter of the Action according to the configuration; 3.3. Execute Filter and Action
Step4: Rendering stage
4.1, Rendering partial view and rendering layout view
4.2. Render registered javascript and css
2.2, YiiBase static class
YiiBase provides common basic functions for the operation of the YII framework: alias management and object creation management. When creating a php object, you need to include the definition file of this class first, and then new the object.
In different environments (development environment/test environment/online environment), the configuration of apache's webroot path may be different, so the full path of the definition file of this class will be different. The Yii framework passes YiiBase Alias management solves this problem.
When creating an object, you need to import the definition of the corresponding class. You often need to use these 5 functions: include(), include_once(), require(), require_once(), set_include_path(). Yii solves this problem uniformly by using YiiBase::import(). The following figure describes the working principle of "alias management and object creation management" provided by YiiBase:
Create objects through createComponent
1. If the class does not exist, import
Create objects through new
2. The new object
3. Initialize the attributes of this object according to the input
import
Import a class definition
Import a path into
include_path
autoload
If the class starts with an alias, obtain the full path through the ignore management interface
Alias Management
getPathOfAlias setPathOfAlias
Add the full path of an alias
First look at alias management, which is to give an alias to a folder (a folder often corresponds to a module). In the YII framework, you can use this alias to replace the full path of the folder, such as : The system alias represents the path of the framework /home/work/yii/framework, so you can use system.base.CApplication to represent
The path to the /home/work/yii/framework/base/CApplication.php file. Of course, in the application layer (our) code, you can also register aliases through Yii::setPathOfAlias.
Generally, we use absolute paths or relative paths to reference files. Of course, both cases have disadvantages. Absolute path: When our code is deployed to the test environment or online environment, we need to modify the path of the included file in large quantities; Relative path: When the location of some module folders is adjusted (renamed), all relative paths All need to be modified. The way to use aliases only needs to change one thing: when registering aliases, that is Yii::setPathOfAlias(). In this way, code changes caused by changes in folders can be concentrated in one place.
Look at the import function: a. Import the definition of a class so that objects of that class can be created; b. Add a folder to include_path so that you can directly include all files under this file. Yii::import is equivalent to the following
Unification of 5 functions: include(), include_once(), require(), require_once(), set_include_path(). And generally the speed will be faster than these functions. Of course, Yii::import supports the alias function, which can solve the trouble caused by path changes.
Finally, let’s look at object creation. There are 2 methods to create objects in the YII framework: 1. Use the new keyword; 2.
Use Yii::createComponent method.
When using the new keyword to create an object, autoload will look for the definition of the corresponding class in three steps: a. Determine whether it is a class in the framework (all classes of the framework and the full path of this class are saved in A member variable of YiiBase is equivalent to the entire framework being imported); 2. Determine whether to use Yii::import to import this class. For non-framework classes, we need to import this first when creating objects of this class. Class definition; 3. Search the php script named after this class name from the include_path directory, so when developing, try to keep the class name consistent with the file name, so that we can import the folder containing this file. This eliminates the need to import every file in this folder.
When using the Yii::createComponent method to create an object, it provides more functions than the new keyword: a. Specify the location and class name of the class through the full path alias of this class (the class name must Consistent with the file name), when this class has not been imported, the definition of this class will be automatically imported based on the full path; 2. Assign values to the member variables of the created object. That is, as described in the figure below, it used to take more than 3 lines of code to write, but now it can be done with one line of code (write less, do more).
Yii::import('application.models.Student');
$obj = new Student();
$obj->age = 16;
$obj->name = 'jerry';
$obj = Yii::createComponent(array( 'class'=>'application.models.Student', 'age'=>16,
'name'=>'jerry'
));
2.3. Components
The CComponent class is a component, which provides the basis for component programming and event-driven programming of the entire framework. Most classes in the YII framework use the CComponent class as a base class. The CComponent class provides 3 features for its subclasses: 1. Member variable expansion
Define a member variable by defining two member functions (getXXX/setXXX), such as:
public function getText() {…} public function setText {…}
This is equivalent to defining a text member variable, which can be called like this
$a=new CComponent;
$a=$component->text; // Equivalent to $a=$component->getText();
$component->text='abc'; // Equivalent to $component->setText('abc');
CComponent implements the "member variable expansion" feature through the magic methods get and set. If a member variable that does not exist in the class itself is operated on, php will call the get and set methods of this class for processing. . CComponent uses these two magic methods to implement the "member variable expansion" feature. The figure below describes a subclass of CComponent, which adds two member variables active and sessionName. The figure describes the calling process for these two member variables.
getActive
setActive
Whether this member variable exists
No set()
get()
getSessionName
setSessionName
is to use the member variables of this object
getXXX setXXX
In object-oriented programming, it is enough to directly define a member variable. Why does CComponent implement a member variable by defining 2 functions? One of the main reasons is the need to "delay loading" of member variables. Generally, the member variables of a class are uniformly assigned in the constructor or initialization function, but not every member variable is assigned during the processing of a web request. will be used. For example, the App class defines two member variables: $cache and $db
($cache is a cache object, $db is a database link object), these two objects are initialized when the App class is initialized
is created at the moment, but for some pages of a web website, the content of which can be obtained through cache, then the database link object does not actually need to be created. If you define App as a subclass of CComponent, define two methods in the App class: getCache/getDb, so that when you use the db member variable for the first time, you can call the getDb function to initialize the database link, thus Implement lazy loading - i.e. initialize on first use. Although delayed loading will add one more function call, it can reduce unnecessary initialization of member variables (generally improving the access speed of the website), and can make our code easier to maintain and expand.
Delayed loading should be the most important use of the "member variable expansion" feature. Of course, this feature also has other uses. Think about it, when you operate a member variable, you are actually calling getXXX and setXXX member functions, you are calling a piece of code!
2. Event model
The event model is the "observer pattern" in the design pattern: when the state of an object changes, then this object can notify other objects of the event.
In order to use the event model, you need to implement these three steps: 1. Define the event; 2. Register the event handler; 3. Trigger the event.
A subclass of CComponent defines an event by defining a member function starting with on, such as: public function onClick(){…}, and then registers the event handler by calling the attachEventHandler member function (you can register multiple event handle), and finally trigger the event by calling raiseEvent.
attachEventHandler detachEventHandler raiseEvent
Event handler container
onclick
fun_11 fun_12
„ fun_1n
beforeinsert
fun_2n
afterinsert
fun_3n
„„
Key
|
||||
Value
fun_mn
The CComponent class uses a private member variable to save the event and all handles for processing the event. This member variable can be regarded as a hash table. The key of the hash table is the name of the event, and the value of the hash table is Event handling function linked list.
3. Behavioral binding
There are two ways to add features to a class: 1. Directly modify the code of this class: add some member functions and member variables; 2. Derive: extend through subclasses. Obviously the second method is easier to maintain and expand. If multiple features need to be added to a class (multiple people at different times), then multi-level derivation is required, which obviously increases maintenance costs. CComponent uses a special way to extend class information - behavior class binding. Behavior class is a subclass of CBehavior class. CComponent can add member functions and member variables of one or more CBehavior classes
to yourself and uninstall certain CBehavior classes when not needed. Here is a simple example:
//Calculator class
class Calculator extends CBehavior
{
public function add($x, $y) { return $x + $y; } public function sub($x, $y) { return $x - $y; }
...
}
$comp = new CComponent();
//Add calculator function to my class
$comp->attachbehavior('calculator', new Calculator());
$comp->add(2, 5);
$comp->sub(2, 5);
CComponent implements the "behavior class binding" feature through three magic methods: get, set and call. When calling member variables and member methods that do not exist in the CComponent class, the CComponent class will pass these three magic methods. A magic method is looked up on a "dynamically bound behavior object". That is, routing non-existent member variables and member methods to "dynamic binding objects".
attachBehavior detachBehavior
Bind an object Unbind
obj1
set()
obj2
Doesn’t exist
No get()
Query each object
call()
obj3
is
Use the member variables and member functions of this object
„
Bound object
Use binding object process Binding maintenance process
You can use 3 sentences to summarize the characteristics of the CComponent class:
1. Better configure an object. When setting the member variables of the object, you are actually running a piece of code;
2. Better monitor an object. When the internal state of the object changes, other objects can be notified;
3. To better extend an object, you can add member variables and member functions to an object, and you can also monitor the status of the object.
2.4, module
Modules are relatively independent program units in the entire system, completing a relatively independent software function. For example, the gii module that comes with Yii implements the function of online code generation. CModule is the base class of all module classes. It consists of 3 parts:
a. Basic attributes (module id, module path, etc.); b. Component, which is the core component of the module. The module can be regarded as a container for these components; c. Submodule, which provides the module with Scalability, for example, if a module becomes larger, it can be split into multiple sub-modules (each
sub-module also consists of these 3 parts and is a recursive structure). The following figure is a diagram of the inclusion relationship between a module and its members:
Template base
This attribute component submodule
The following table lists the various components of CModule:
|
Detailed members |
Description |
||||||||||||||||||||||||||||
Basic attributes (Users configure global things of the entire module) |
id |
Module id |
||||||||||||||||||||||||||||
parentModule |
Parent module |
|||||||||||||||||||||||||||||
basePath |
Path of the current module |
|||||||||||||||||||||||||||||
modulePath |
Path of submodule |
|||||||||||||||||||||||||||||
params |
Module parameters |
|||||||||||||||||||||||||||||
preload |
Component id that needs to be pre-loaded |
|||||||||||||||||||||||||||||
behaviors |
Binded behavior class |
|||||||||||||||||||||||||||||
aliases |
The newly added alias is added to the alias management of YiiBase |
|||||||||||||||||||||||||||||
import |
File or path to be included |
|||||||||||||||||||||||||||||
Component (This is the core component of the module) |
components |
Array type, each member of the array describes a component |
||||||||||||||||||||||||||||
Submodule (This provides extensibility to the module) |
modules |
Array type, each member of the array describes a module, Each module also consists of these 3 parts, which is a recursive structure |
It is very convenient to initialize these three components of the module: use an array for configuration. The key of the array is the attribute that needs to be configured, and the value is the value that needs to be configured. The figure below is an example. Why Will it be configured in this way? Because CModule inherits from the CComponent class, when configuring member properties, it is actually running a piece of code, that is, a member function.
array(
'basePath'=>dirname( FILE ).DIRECTORY_SEPARATOR.'..',//The path of the module 'preload'=>array('log'),//The log component needs to be preloaded
'import'=>array('application.models.*', 'application.components.*',),//The path to include
//Component configuration
'components'=>array( 'user'=>array(//Configuration of user components
'allowAutoLogin'=>true
),
'log'=>array(//Configuration of log component 'class'=>'CLogRouter',
'routes'=>array(array('class'=>'CWebLogRoute','levels'=>'trace, profile'))
)
),
//Module configuration
'modules'=>array(
'gii'=>array(//Automatically generate code module configuration 'class'=>'system.gii.GiiModule', 'password'=>'123456'
),
),
);
2.5, App Application
Application refers to the execution context in request processing. Its main task is to analyze user requests and dispatch them to the appropriate controller for further processing. It also serves as a service center, maintaining application-level configurations. For this reason, applications are also called "front-end controllers".
Yii uses the CApplication class to represent App applications. CApplication inherits from CModule. It has made three extensions based on the parent class: 1. Added a run method; 2. Added several member attributes; 3. Added several components.
The run method is equivalent to the main function of C language. It is the entry point for the entire program to start running. The virtual function processRequest is called internally to process each request. CApplication has 2 subclasses: CWebApplication and CConsoleApplication, both of which are implemented the method. The onBeginRequest and onEndRequest events are initiated at the beginning and end of processing each request to notify listening observers. Review the "Yii Framework Loading and Running Process" diagram, from which you can find the role of this method in the entire process.
The added member variables, member functions and components are shown in the table below:
Category |
Name |
Description |
Member variables |
name |
Name of the application |
charset |
The encoding set applied, the default is UTF-8 |
|
sourceLanguage |
The language and region ID number used for encoding, which is needed when developing multiple languages, Default is UTF-8 |
|
language |
The language and region ID required by the app, the default is sourceLanguage |
|
runtimePath |
The path during runtime, for example, the global state will be saved to this path, default thinks application.runtime |
|
extensionPath |
The path to put third-party extensions, the default is application.ext |
|
timezone |
Get or set time zone |
|
locale |
Localization object, used to localize time, numbers, etc. |
|
globalsate |
Global state array, which will be persisted (implemented by statePersister) |
|
Component |
coreMessages |
Translate the framework layer content and support multiple languages |
messages |
Translate application layer content and support multiple languages |
|
db |
数据库组件 |
errorHandler |
异常处理组件,该组件与 App 配合来处理所有的异常 |
|
securityManager |
安全管理组件 |
|
statePersister |
状态持久化组件 |
|
urlManager |
url 管理组件 |
|
request |
请求组件 |
|
format |
格式化组件 |
2.6, WebApp Application
Each web request is processed by the WebApp application, that is, the WebApp application provides a running environment for the processing of http requests. The WebApp application is the CWebApplication class. Its main job is to create the corresponding control class based on the route in the url. The following figure describes the controller creation process, which mainly consists of 3 steps:
1. Search in the member variable controllerMap to determine whether there is a corresponding Controller. ControllerMap has the highest priority
2. Search in the sub-module to determine whether there is a corresponding Controller 3. Search
in the ControllerPath and its subfoldersThe process of searching for a controller
The input route is: seg1/seg2/seg3
Call createController(‘seg1/seg2/seg3’,$app)
1
Look for the control class with id seg1 in controllerMap
Call createController(‘seg2/seg3’,
$subModule)
2 does not exist
Recursive call
in the submodule with id seg1 |
|||||
|
|
does not exist
3
Look for
layer by layer under the ControllerPath path
Does ControllerPath/seg1/Seg2Controller.php exist? ControlId is /seg1/seg2
does not exist. Does ControllerPath/seg1/seg2/Seg3Controller.php exist?
ControlId is /seg1/seg2/seg3
类别 |
名称 |
说明 |
成员变量 |
defaultController |
默认的控制类,如果没有指定控制器,则使用该控制器 |
|
layout |
默认的布局,如果控制器没有指定布局,则使用该布局 |
|
controllerMap |
控制器映射表,给某些特定的路由指定控制器 |
|
theme |
设置主题 |
|
controller |
当前的控制器对象 |
|
controllerPath |
控制器文件的路径 |
Category |
Name |
Description |
Member variables |
defaultController |
Default control class, if no controller is specified, this controller will be used |
layout |
Default layout, if the controller does not specify a layout, this layout will be used |
|
controllerMap |
Controller mapping table, specify controllers for certain routes |
|
theme |
Set theme |
|
controller |
Current controller object |
|
controllerPath |
Path to controller file |
|
ViewPath |
The path of the view layer file, the default is protected/views/ |
|||||||||||||||||||||||||||
SystemViewPath |
The path of the system view file, the default is protected/views/system/ |
||||||||||||||||||||||||||||
LayoutPath |
The path to the layout file, the default is protected/views/layouts/ |
||||||||||||||||||||||||||||
Component |
session |
session component |
|||||||||||||||||||||||||||
assetManager |
Resource management component for publishing private js, css and image |
||||||||||||||||||||||||||||
user |
User components, user login, etc. |
||||||||||||||||||||||||||||
themeManager |
Theme Component |
||||||||||||||||||||||||||||
authManager |
Permission component implements role-based permission control |
||||||||||||||||||||||||||||
clientScript |
Client-side script management component, manages js and css code |
3. System components
3.1. Log routing component
Every Web system needs to record logs during operation. Logs can be recorded to files or databases. During the development phase, logs can be output directly to the bottom of the page, which can speed up development. Yii has done the following two important tasks in log processing:
1. Each Http request may need to record multiple logs (database update log/interaction log with other systems). For example, if a certain HTTP request needs to record 18 logs, should we write to the hard disk once for each log (that is, write 18 hard disks), or should we write to the hard disk all at once when the request is about to end? Obviously, it is faster to save these logs in a php array first. When the request is about to end, it is faster to write all the logs in the array to the hard disk at once.
2. Each log can be classified from 2 dimensions: the severity level of the log and the business logic of the log. Use the table below
To describe the logs of "Baidu Creative Expert" products in these two dimensions:
Severity Level |
Database log |
User Center Interface Log |
Drmc interface log |
Memcache Log |
||||||||||||||||||||||||||||||
trace |
||||||||||||||||||||||||||||||||||
info |
||||||||||||||||||||||||||||||||||
profile |
||||||||||||||||||||||||||||||||||
warning |
||||||||||||||||||||||||||||||||||
error |
According to business logic, it is divided into: database operation log, user center interface log, Drmc interface log, Memcache update log, etc.
Divided according to severity level: trace, info, profile, warning, error. We may want to record logs of different business logic (database logs, logs interacting with other systems) to different
In the files of, you can view them by categories. Because error logs are generally serious, we may also want to log all errors to a separate file or mongodb. The log routing component in Yii can route different types of logs to different destinations (files, databases, mailboxes, and pages), which makes it very convenient to maintain and manage logs.
The following is the configuration of a log routing component. This configuration records logs of different business logic into different files, records error logs separately into the error.log file, and sends serious logs directly to emails. During the development process, logs are also output to the page, which speeds up development. The specific configuration is as follows:
'log'=>array(
'class'=>'CLogRouter', 'routes'=>array(
array(//Database logs are recorded in db.log 'class'=>'CFileLogRoute', 'categories'=>'db.*', 'logFile'=>'db.log ',
),
array(//The log of interaction with the user center is recorded in uc.log 'class'=>'CFileLogRoute', 'categories'=>'uc.*',
'logFile'=>'uc.log',
),
array(//The log of interaction with Drmc is recorded in uc.log 'class'=>'CFileLogRoute', 'categories'=>'drmc.*', 'logFile'=>' drmc.log',
),
array(//All error logs are recorded in error.log 'class'=>'CFileLogRoute', 'levels'=>'error', 'logFile'=>'error.log ',
),
array(//Because the user center is very important, all user center error logs need to be sent out of the email
'class'=>'CEmailLogRoute', 'categories'=>'uc.*', 'levels'=>'error', 'emails'=>'admaker@baidu.com ',
),
array(//During the development process, print all logs directly to the bottom of the page, so you don’t need to log in to the server to view the logs
'class'=>'CWebLogRoute' 'levels'=>'trace,info,profile,warning,error',
),
)
As you can know from the above code, Yii’s logging is driven by the configuration array. Next, log in Yii
Processing for in-depth analysis, the following figure describes the process and principle of log processing in Yii:
1. Generate multiple logging objects according to the configuration of the log routing component
Log routing component
Logging object 1
db.log
2. Save the log to the log buffer
Log buffer
3. Notify the log routing component when the buffer is full or the request ends
Logging object 2
. . .
uc.log
Logging object i
5. Output the log to the specified destination
Send email log
4. Each logging object takes out the log it needs from the buffer
Logging object N
Log output to page
During an HTTP request, the log processing process is divided into the following 5 stages:
Step1: Based on the configuration information of the log router, generate each logging object, namely CFileLogRoute,
CEmailLogRoute and CWebLogRoute objects, the log routing component manages these objects uniformly;
Step2: The programmer calls the logging interface (see the table below) to record logs. All logs are temporarily saved in a php array buffer;
Step3: When the buffer is full or the request processing ends, a Flush event will be triggered to notify the log routing component to retrieve the log. The observer mode is used here;
Step4: Each logging object takes out the logs it needs, such as database logs, user center interaction logs, and error level logs.