以下是一個通過高級模版安裝后典型的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應用的目錄結構。
公共目錄
這里的公共目錄可不止 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 目錄下寫代碼。
前台的目錄結構
其實,前台和后台是一樣的,只是我們邏輯上的一個划分。 典型的, frontend 具有如下的一個目錄結構:
.
├── assets
├── config
├── controllers
├── models
├── runtime
├── views
├── web
└── widgets
按照順序來講:
- assets 目錄用於存放前端資源包PHP類。 這里不需要了解什么是前端資源包,只要大致知道是用於管理CSS、js等前端資源就可以了。
- config 用於存放本應用的配置文件,包含主配置文件 main.php 和全局參數配置文件 params.php 。
- models views controllers 3個目錄分別用於存放數據模型類、視圖文件、控制器類。這個是我們編碼的核心,也是我們工作最多的目錄。
- widgets 目錄用於存放一些常用的小掛件的類文件。
- tests 目錄用於存放測試類。
- web 目錄從名字可以看出,這是一個對於Web服務器可以訪問的目錄。 除了這一目錄,其他所有的目錄不應對Web用戶暴露出來。這是安全的需要。
- runtime 這個目錄是要求權限為 chmod 777 ,即允許Web服務器具有完全的權限, 因為可能會涉及到寫入臨時文件等。 但是一個目錄並未對Web用戶可見。也就是說,權限給了,但是並不是Web用戶可以訪問到的。
backend 目錄與 frontend 的結構、內容是一樣一樣的。 所謂的前台和后台,只是人為從邏輯上對Web應用的功能划分,目的在於分解應用的規模和復雜程度, 便於維護和使用。從代碼角度來講,Yii壓根就不認得哪個是前台,哪個是后台。
前面提到的,傳說中的入口文件 index.php 就位於 web 目錄下面。
入口文件index.php
首先來看看 index.php 文件的內容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?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();
|
設置調試模式和代碼環境
- 前兩行是兩個 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_PRODYII_ENV_TEST 的值為 true 。 這樣,在應用的配置中,特別是在相同的一個配置文件中,可以對不同環境作出不同的配置。
比如,你希望在測試環境下,使用另一個數據庫。在開發環境下,啟用調試工作條,等等。那么,可以這么做:
1 2 3 4 5 6 7 8 |
$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 之類的判斷語句,這會使本來已經顯得很長的配置文件看上去更臃腫。
引入必要的文件
緊接着兩個 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應用引導的代碼,比如定義一系列的路徑別名:
1 2 3 4 5 6 |
<?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應用就跑起來了。
命令行應用入口腳本
命令行應用的入口腳本是 path/to/digpage.com/yii 文件。這個文件被 init 腳本設為可執行的。 他的內容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#!/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 文件,只是沒有加上PHP后綴而已 。
接下來, define('STDIN') 和 define('STDOUT') 則為fcgi定義了標准輸入和標准輸出。
在各require語句中,由於 yii 的位置與 index.php 不同,是位於應用根目錄下,所以目錄結構上更簡單些。
最后,在Yii應用跑起來后,還要獲取其返回值,並以該返回值退出腳本,通知操作系統退出時的狀態。
對於Windows系統而言,命令行的入口腳本仍然是 yii ,但是命令行下無法直接運行。所以, 細心的Yii為我們准備了一個 yii.bat 。這個文件會以 php yii 形式調用PHP來運行入口腳本 。
http://www.digpage.com/