以下是一個通過高級模版安裝后典型的Yii應用的目錄結構:
~~~
.
├── backend
├── common
├── console
├── environments
├── frontend
├── nbproject
├── tests
├── vendor
├── composer.json
├── composer.lock
├── init
├── init.bat
├── LICENSE.md
├── README.md
├── requirements.php
├── yii
└── yii.bat
~~~
對于高級應用而言,相當于有?backend?frontend?console?三個獨立的Yii應用。 由于?console?類的應用比較特殊,我們稍后再講。這里講典型的Web應用的目錄結構。
## 公共目錄[](http://www.digpage.com/app_struct.html#id1 "Permalink to this headline")
這里的公共目錄可不止?common?目錄,但這個目錄從字面上來看, 是所有公共目錄里最“公共”的。common?目錄下的東西, 對于本高級應用的任一獨立的應用而言,都是可見、可用的。一般情況下,common?具有以下結構:
~~~
.
├── config
├── mail
└── models
~~~
其中:
* config?就是通用的配置,這些配置將作用于前后臺和命令行。
* mail?就是應用的前后臺和命令行的與郵件相關的布局文件等。
* models?就是前后臺和命令行都可能用到的數據模型。 這也是?common?中最主要的部分。
除了?common?之外,還有一個很重要的公共目錄,?vendor?。 這個目錄從字面的意思看,就是各種第三方的程序。 這是Composer安裝的其他程序的存放目錄,包含Yii框架本身,也放在這個目錄下面。 如果你向?composer.json?目錄增加了新的需要安裝的程序,那么下次調用Composer的時候, 就會把新安裝的目錄也安裝在這個?vendor?下面。
好了,現在問題來了。對于?frontend?backend?console?等獨立的應用而言, 他們的內容放在各自的目錄下面,他們的運作必然用到Yii框架等?vendor?中的程序。 他們是如何關聯起來的?這個秘密,或者說整個Yii應用的目錄結構的秘密, 就包含在一個傳說中的稱為入口文件的地方。
但是在了解入口文件index.php之前,有必要先看看諸如?frontend?等獨立應用的目錄結構。 這比起整個Yii應用的目錄結構面言,更為重要。因為你往往是在?frontend?等目錄下寫代碼。 但是,不大會在path\to\digpage?目錄下寫代碼。
## 前臺的目錄結構[](http://www.digpage.com/app_struct.html#id2 "Permalink to this headline")
其實,前臺和后臺是一樣的,只是我們邏輯上的一個劃分。 典型的,?frontend?具有如下的一個目錄結構:
~~~
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/../../common/config/bootstrap.php');
require(__DIR__ . '/../config/bootstrap.php');
$config = yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/../../common/config/main.php'),
require(__DIR__ . '/../../common/config/main-local.php'),
require(__DIR__ . '/../config/main.php'),
require(__DIR__ . '/../config/main-local.php')
);
$application = new yii\web\Application($config);
$application->run();
~~~
### 設置調試模式和代碼環境[](http://www.digpage.com/app_struct.html#id3 "Permalink to this headline")
前兩行是兩個?define?語句::
~~~
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
~~~
定義當前的運行模式和環境。如果定義了?YII_DEBUG?, 那么表示當前為調試狀態,應用在運行過程中,會有一些調試信息的輸出。 在拋出異常時,也會有一個詳細的調用棧的顯示。默認情況下,YII_DEBUG?為?false?。 但在開發過程中,最好按上面寫的那樣,定義為?true?這樣便于查找和分析錯誤。
如果定義了?YII_ENV?,那么就是指定了當前應用的運行環境。 上面的代碼顯示應用將運行于?dev?環境。默認情況下,?YII_ENV?為?prod?表示產品環境。
這些環境只是一個名稱,具體的意義和環境內容要看環境的定義。?dev?prod?是Yii安裝后默認的兩個環境,分別表示開發環境和最終的產品環境。 此外還有一個?test?環境,表示測試環境。
環境與模式的作用不同。環境在代碼中主要是影響配置文件。?YII_ENV?的?dev?prod?test?三種環境, 會分別使?YII_ENV_DEV?YII_ENV_PROD?YII_ENV_TEST?的值為?true?。 這樣,在應用的配置中,特別是在相同的一個配置文件中,可以對不同環境作出不同的配置。
比如,你希望在測試環境下,使用另一個數據庫。在開發環境下,啟用調試工作條,等等。那么,可以這么做:
~~~
$config = [...];
if (!YII_ENV_TEST) {
// 以下配置項僅在測試環境中起作用
$config['bootstrap'][] = 'debug';
$config['modules']['debug'] = 'yii\debug\Module';
$config['modules']['gii'] = 'yii\gii\Module';
}
~~~
其實,這個?YII_ENV?的定義就是一個與?init?腳本環境切換的相互補充。 如果各環境比較明晰,用init?來切換各種環境的配置是完全夠用的。 不必在腳本中再有如?YII_ENV_TEST?之類的判斷語句,這會使本來已經顯得很長的配置文件看上去更臃腫。
### 引入必要的文件[](http://www.digpage.com/app_struct.html#id4 "Permalink to this headline")
緊接著兩個?define?語句之后,就是4個?require?語句:
~~~
require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/../../common/config/bootstrap.php');
require(__DIR__ . '/../config/bootstrap.php');
~~~
這4個語句的前3個中都使用到了相對于當前目錄的叔伯目錄中的php文件, 第4個語句使用了相對于當前目錄的兄弟目錄。
我們知道,?__DIR__?表示當前文件?index.php?所在的目錄。?/../../?表示的是當前目錄的爺爺目錄。 若?index.php?的當前目錄是?/path/to/digpapge.com/frontend/web?, 那么爸爸目錄就是?frontend,爺爺目錄就是?digpage.com?了。
第一個require引入了?/path/to/digpage.com/vendor?下面的?autoload.php?。 這個是composer的類自動加載機制注冊文件。引入這個文件后,可以使用composer的類自動加載功能。
第二個引入了?vendor?下面的?yiisoft/yii2/Yii.php?,這是Yii的工具類文件。 引入了這個類文件后,才能使用Yii的提供的各種工具, 才有?Yii::createObject()?Yii::$app?之類的東東可以使用。
第三個引入了?/path/to/digpage.com/common?下面的?config/bootstrap.php?。 這個文件主要用于執行一些Yii應用引導的代碼,比如定義一系列的路徑別名:
~~~
<?php
Yii::setAlias('common', dirname(__DIR__));
Yii::setAlias('frontend', dirname(dirname(__DIR__)) . '/frontend');
Yii::setAlias('backend', dirname(dirname(__DIR__)) . '/backend');
Yii::setAlias('console', dirname(dirname(__DIR__)) . '/console');
Yii::setAlias('vendor', dirname(dirname(__DIR__)) . '/vendor');
~~~
這是默認安裝后定義好的?common?frontend?backend?console?vendor?5個路徑別名, 如果你要新增一個用于表示插件的目錄?plugin?可以自己在這個文件里面加一行:
~~~
Yii::setAlias('plugin', dirname(dirname(__DIR__)) . '/plugins');
~~~
第四個require引入了?path/to/digpage.com/frontend?下面的?config/bootstrap.php?。 作用與上面類似,只是其中的代碼僅適用于當前應用(frontend)。 而第三個require中的,是適應于所有應用(common)。
再接下來,是一個函數?yii\helpers\ArrayHelper::merge()?。 這個函數的作用在于合并參數所指定的各個數組。其中,后面的數組會把前面數組中相同下標的元素覆蓋掉。 這個語句的作用,就是讀取、合并應用的各配置文件并保存在?$config?變量中。 這里我們看到一共是讀取了4個配置文件:
~~~
require('path/to/digpage.com/common/config/main.php'),
require('path/to/digpage.com/common/config/main-local.php'),
require('path/to/digpage.com/frontend/config/main.php'),
require('path/to/digpage.com/frontend/config/main-local.php')
~~~
依次是通用目錄common下的2個配置文件,和當前應用frontend下的2個配置文件。 在優先順序上,當前的配置覆蓋通用的配置。 同時,帶有?-local?的配置文件在后,所以,本地配置文件覆蓋團隊配置文件。
最后,以?$config?為參數,實例化了一個?Application?對象,并調用他的?run()?函數。 這時,Yii應用就跑起來了。
## 命令行應用入口腳本[](http://www.digpage.com/app_struct.html#id5 "Permalink to this headline")
命令行應用的入口腳本是?path/to/digpage.com/yii?文件。這個文件被?init?腳本設為可執行的。 他的內容如下:
~~~
#!/usr/bin/env php
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
// fcgi doesn't have STDIN and STDOUT defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
require(__DIR__ . '/vendor/autoload.php');
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/common/config/bootstrap.php');
require(__DIR__ . '/console/config/bootstrap.php');
$config = yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/common/config/main.php'),
require(__DIR__ . '/common/config/main-local.php'),
require(__DIR__ . '/console/config/main.php'),
require(__DIR__ . '/console/config/main-local.php')
);
$application = new yii\console\Application($config);
$exitCode = $application->run();
exit($exitCode);
~~~
對比于Web應用的?index.php?入口腳本,?yii?并沒有太多的新東西,其中核心的東西根本就沒變。
我們先來看看這個這個?yii?是什么?
首先,它沒有擴展名,我們不好知道其具體類型。 但是從文件內容的第一行?#!/usr/bin/env?php?來看,這是一個bash腳本。 第一行在告訴bash,也在告訴我們,這是一個使用PHP運行的腳本。
但第二行的??又清楚的向我們表明,這貨其實也是個 PHP 文件,只是沒有加上PHP后綴而已 。
接下來,?define('STDIN')?和?define('STDOUT')?則為fcgi定義了標準輸入和標準輸出。
在各require語句中,由于?yii?的位置與?index.php?不同,是位于應用根目錄下,所以目錄結構上更簡單些。
最后,在Yii應用跑起來后,還要獲取其返回值,并以該返回值退出腳本,通知操作系統退出時的狀態。
對于Windows系統而言,命令行的入口腳本仍然是?yii?,但是命令行下無法直接運行。所以, 細心的Yii為我們準備了一個?yii.bat?。這個文件會以?php?yii?形式調用PHP來運行入口腳本 。
如果覺得《深入理解Yii2.0》對您有所幫助,也請[幫助《深入理解Yii2.0》](http://www.digpage.com/donate.html#donate)。 謝謝!
- 更新記錄
- 導讀
- Yii是什么
- Yii2.0的亮點
- 背景知識
- 如何閱讀本書
- Yii基礎
- 屬性(Property)
- 事件(Event)
- 行為(Behavior)
- Yii約定
- Yii應用的目錄結構和入口腳本
- 別名(Alias)
- Yii的類自動加載機制
- 環境和配置文件
- 配置項(Configuration)
- Yii模式
- MVC
- 依賴注入和依賴注入容器
- 服務定位器(Service Locator)
- 請求與響應(TBD)
- 路由(Route)
- Url管理
- 請求(Reqeust)
- Web應用Request
- Yii與數據庫(TBD)
- 數據類型
- 事務(Transaction)
- AcitveReocrd事件和關聯操作
- 樂觀鎖與悲觀鎖
- 《深入理解Yii2.0》視頻教程
- 第一講:基礎配置
- 第二講:用戶登錄
- 第三講:文章及評論的模型
- 附錄
- 附錄1:Yii2.0 對比 Yii1.1 的重大改進
- 附錄2:Yii的安裝
- 熱心讀者