#### 核心運(yùn)行類
現(xiàn)在我們已經(jīng)有了配置類,至于配置文件中有什么樣的鍵值對(duì),我們可以邊做,需要時(shí)再去定義,這相當(dāng)于我們的準(zhǔn)備工作,現(xiàn)在我們可以開始繼續(xù)順著我們的執(zhí)行流程繼續(xù)做啦~
接下來(lái)我們要做的是核心運(yùn)行類``S.php``,在前面我寫過(guò)的``入口文件及核心文件``的最后,是引入核心運(yùn)行類,并執(zhí)行這個(gè)類的run方法
~~~
4.引入加載函數(shù)
``include(CORE_PATH . 'S.php');``
5.執(zhí)行加載函數(shù)的run方法
``S::run();``
~~~
下面開始寫這個(gè)核心運(yùn)行類``S.php``:
(先貼出全部代碼,再進(jìn)行詳細(xì)解釋)
~~~
<?php
namespace S;
class S{
private static $prefixes = [];
public static function run(){
//應(yīng)該做的是:設(shè)置字符集,系統(tǒng)類映射,自動(dòng)加載注冊(cè)方法,實(shí)例化路由
self::setHeader();
self::getMapList();
spl_autoload_register('self::s_autoload');
try{
new Route();
}catch (Exception $e){
$e->getDetail();
}
}
private static function setHeader(){
header("Content-type:text/html;Charset=".C('default_charset'));
date_default_timezone_set(C('default_timezone'));
}
public static function addNamespace($prefix, $base_dir, $prepend = false)
{
//格式化命名空間前綴,以反斜杠結(jié)束(去除兩側(cè)的反斜杠,只在最后加一個(gè)反斜杠)
$prefix = trim($prefix, '\\') . '\\';
//格式化基目錄以正斜杠結(jié)尾,DIRECTORY_SEPARATOR是系統(tǒng)常量,目錄分隔符,把基目錄右側(cè)斜杠去掉,換成系統(tǒng)支持的斜杠,然后最后統(tǒng)一為正斜杠
$base_dir = rtrim($base_dir, '/') . DIRECTORY_SEPARATOR;
$base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';
//初始化命名空間前綴數(shù)組
//如果前綴已存在數(shù)組中則跳過(guò),否則存入數(shù)組
if (isset(self::$prefixes[$prefix]) === false) {
self::$prefixes[$prefix] = [];
}
if ($prepend) {
//命名空間前綴相同時(shí),后增基目錄(array_unshift() 函數(shù)用于向數(shù)組插入新元素。新數(shù)組的值將被插入到數(shù)組的開頭。)
array_unshift(self::$prefixes[$prefix], $base_dir);
} else {
//前增,向數(shù)組尾部增加值
array_push(self::$prefixes[$prefix], $base_dir);
}
}
private static function getMapList()
{
//實(shí)例化Config類,執(zhí)行g(shù)et函數(shù),獲取到namespace_map_list的值,循環(huán)更改$prefixes的值
foreach (Config::getInstance()->get('namespace_map_list') as $key => $value) {
self::addNamespace($key, $value);
}
}
private static function s_autoload($className){
// 當(dāng)前命名空間前綴
$prefix = $className;
//從后面開始遍歷完全合格類名中的命名空間名稱,來(lái)查找映射的文件名
//strpos獲取參數(shù)2在參數(shù)1中最后出現(xiàn)的位置,substr截取字符串
while (false !== $pos = strrpos($prefix, '\\')) {
// 命名空間前綴
$prefix = substr($className, 0, $pos + 1);
// 相對(duì)的類名
$relative_class = substr($className, $pos + 1);
//嘗試加載與映射文件相對(duì)的類
$mapped_file = self::loadMappedFile($prefix, $relative_class);
// var_dump($mapped_file);
if ($mapped_file) {
return $mapped_file;
}
//去除前綴的反斜杠
$prefix = rtrim($prefix, '\\');
}
return false;
}
private static function loadMappedFile($prefix, $relative_class)
{
//這個(gè)命名空間前綴是否存在基本的目錄?
if (isset(self::$prefixes[$prefix]) === false) {
return false;
}
$relative_class = str_replace('\\', '/', $relative_class);
foreach (self::$prefixes[$prefix] as $base_dir) {
$file = $base_dir . $relative_class . '.php';
// 如果映射文件存在就加載它
if (self::requireFile($file)) {
return true;
}
}
return false;
}
private static function requireFile($file)
{
if (file_exists($file)) {
include $file;
return true;
}
return false;
}
}
~~~
好了,現(xiàn)在進(jìn)行詳細(xì)解釋:
首先,我們執(zhí)行的是這個(gè)類的run方法:
~~~
public static function run(){
self::setHeader(); //執(zhí)行本類的setHeader方法,該方法用于設(shè)置字符集
self::getMapList(); //然后執(zhí)行g(shù)etMapList方法,用來(lái)把命名空間的路徑映射為真實(shí)目錄路徑
spl_autoload_register('self::s_autoload'); //自動(dòng)加載函數(shù),當(dāng)需要實(shí)例化一個(gè)沒有找到的類時(shí),就會(huì)調(diào)用本類的s_autoload方法
try{
new Route(); //實(shí)例化路由,這個(gè)類用于解析URL
}catch (Exception $e){
$e->getDetail();
}
}
~~~
下面開始詳細(xì)講解里面用到的方法
setHeader方法
~~~
private static function setHeader(){
//設(shè)置默認(rèn)字符集,這里用到了上一章我們定義的C函數(shù),獲取到了配置項(xiàng)‘default_charset’的值
header("Content-type:text/html;Charset=".C('default_charset'));
//設(shè)置默認(rèn)時(shí)區(qū),這個(gè)設(shè)置主要就是影響時(shí)間函數(shù)中取得的結(jié)果
date_default_timezone_set(C('default_timezone'));
}
~~~
header()函數(shù)是一個(gè)非常重要的函數(shù),用于設(shè)置響應(yīng)頭部,比如我們?cè)谂渲梦募屑尤肱渲庙?xiàng)``'default_charset'=>'UTF-8',``那么當(dāng)我們通過(guò)網(wǎng)址訪問(wèn)網(wǎng)站,所獲得的響應(yīng)就會(huì)使用uft8進(jìn)行編碼,如果網(wǎng)頁(yè)中的字符集設(shè)置的是GBK的話,那么就會(huì)出現(xiàn)亂碼,所以指定一個(gè)統(tǒng)一的字符集是非常必要的。
date_default_timezone_set()設(shè)置時(shí)區(qū),這里在配置文件中添加配置項(xiàng)``'default_timezone'=>'PRC'``,表示默認(rèn)時(shí)區(qū)是中國(guó)時(shí)區(qū)
* * * * *
接下來(lái)是``getMapList()``方法:
~~~
private static function getMapList()
{
//實(shí)例化Config類,執(zhí)行g(shù)et函數(shù),獲取到namespace_map_list的值,循環(huán)更改$prefixes的值
foreach (Config::getInstance()->get('namespace_map_list') as $key => $value) {
self::addNamespace($key, $value);
}
}
public static function addNamespace($prefix, $base_dir, $prepend = false)
{
//格式化命名空間前綴,以反斜杠結(jié)束(去除兩側(cè)的反斜杠,只在最后加一個(gè)反斜杠)
$prefix = trim($prefix, '\\') . '\\';
//格式化基目錄以正斜杠結(jié)尾,DIRECTORY_SEPARATOR是系統(tǒng)常量,目錄分隔符,把基目錄右側(cè)斜杠去掉,換成系統(tǒng)支持的斜杠,然后最后統(tǒng)一為正斜杠
$base_dir = rtrim($base_dir, '/') . DIRECTORY_SEPARATOR;
$base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';
//初始化命名空間前綴數(shù)組
//如果前綴已存在數(shù)組中則跳過(guò),否則存入數(shù)組
if (isset(self::$prefixes[$prefix]) === false) {
self::$prefixes[$prefix] = [];
}
if ($prepend) {
//命名空間前綴相同時(shí),后增基目錄(array_unshift() 函數(shù)用于向數(shù)組插入新元素。新數(shù)組的值將被插入到數(shù)組的開頭。)
array_unshift(self::$prefixes[$prefix], $base_dir);
} else {
//前增,向數(shù)組尾部增加值
array_push(self::$prefixes[$prefix], $base_dir);
}
}
~~~
詳細(xì)解釋:這里的內(nèi)容比較抽象,請(qǐng)不要著急,慢慢理解。
在前面我說(shuō)過(guò)很多次的類映射,將命名空間的路徑映射為項(xiàng)目中控制器類的真實(shí)路徑。下面舉個(gè)栗子~,我在配置文件中添加一個(gè)配置項(xiàng)
`` 'namespace_map_list' => [
'S' => S_PATH . 'S/core',
'Home' => S_PATH . 'Application/Home/Controller',
],``
鍵名是``namespace_map_list``,而對(duì)應(yīng)的值是一個(gè)數(shù)組,然后看這個(gè)數(shù)組,這個(gè)數(shù)組中的每一個(gè)鍵值對(duì)中的鍵,就是命名空間的路徑,而對(duì)應(yīng)的值,就是真實(shí)的項(xiàng)目路徑,例如:項(xiàng)目中存在一個(gè)``Home``模塊,這個(gè)模塊下的所有控制器類的命名空間都是``Home``,所對(duì)應(yīng)的真實(shí)路徑是``S_PATH . 'Application/Home/Controller'``,當(dāng)路由解析URL得到的模塊名是Home,控制器名是IndexController時(shí),就會(huì)查找映射配置中是否存在這個(gè)模塊,如果有的話,就去對(duì)應(yīng)的項(xiàng)目路徑中去尋找真正的控制器類并動(dòng)態(tài)加載進(jìn)來(lái)。這就是類映射存在的意義。
所以,``getMapList()``方法先獲得了配置文件中的映射配置項(xiàng),然后遍歷這個(gè)配置項(xiàng)(每個(gè)鍵值對(duì)都是一個(gè)映射),對(duì)每一個(gè)鍵值對(duì)都調(diào)用``addNamespace()``方法,把得到的名稱存到一個(gè)靜態(tài)數(shù)組中,這樣以后實(shí)例化控制器時(shí),就直接在這個(gè)靜態(tài)數(shù)組中進(jìn)行尋找真實(shí)路徑。
* * * * *
接下來(lái)是自動(dòng)加載類:
~~~
private static function s_autoload($className){
// 當(dāng)前命名空間前綴
$prefix = $className;
//從后面開始遍歷完全合格類名中的命名空間名稱,來(lái)查找映射的文件名
//strpos獲取參數(shù)2在參數(shù)1中最后出現(xiàn)的位置,substr截取字符串
while (false !== $pos = strrpos($prefix, '\\')) {
// 命名空間前綴
$prefix = substr($className, 0, $pos + 1);
// 相對(duì)的類名
$relative_class = substr($className, $pos + 1);
//嘗試加載與映射文件相對(duì)的類
$mapped_file = self::loadMappedFile($prefix, $relative_class);
if ($mapped_file) {
return $mapped_file;
}
//去除前綴的反斜杠
$prefix = rtrim($prefix, '\\');
}
return false;
}
private static function loadMappedFile($prefix, $relative_class)
{
//這個(gè)命名空間前綴是否存在基本的目錄?
if (isset(self::$prefixes[$prefix]) === false) {
return false;
}
$relative_class = str_replace('\\', '/', $relative_class);
foreach (self::$prefixes[$prefix] as $base_dir) {
$file = $base_dir . $relative_class . '.php';
// 如果映射文件存在就加載它
if (self::requireFile($file)) {
return true;
}
}
return false;
}
//文件加載
private static function requireFile($file)
{
if (file_exists($file)) {
include $file;
return true;
}
return false;
}
}
~~~
每一行的注釋我都寫的很清楚啦,慢慢理解,這里很抽象~~~