# :-: 一、容器與門面
>[info] 容器: 類與類實例的集合, 至少包括類實例的綁定與創建二個功能
* 設計模式中的容器的原理與應用場景
1. 容器可以簡單的理解為類與類實例的集合
2. 本例中,我們將模型與視圖的實例放在一個容器中進行統一管理
3. 容器本身也是一個類, 類中至少要提供二個方法: 創建實例和取出實例
>[info] 容器
>> Controller.php 文件
```php
// 加載'模型類'
require 'Model.php';
// 加載'視圖類'
require 'View.php';
/****************************************************************************/
// 創建容器類,容器中可保存很多類型,這里僅以類實例方法來演示
class Container{
// 創建容器屬性,保存著類實例的創建方法
protected $instance = [];
// $alias: 類實例別名, $process: 類的實例化過程/函數/Closure閉包類型
public function bind($alias, Closure $process){
// 將類實例方法存入到容器中
$this->instance[$alias] = $process;
}
// 創建類的實例: 執行容器中的類的實例方法,$alias參數是容器中的類實例別名
public function make($alias, $params=[]) {
return call_user_func_array($this->instance[$alias], $params);
}
}
// 將要用到的類實例(Mode/View)綁定到容器中
$container = new Container();
// 將Model類實例的方法綁定到容器中,標識為'model'
$container->bind('model', function () { return new Model(); });
// 將View類實例的方法綁定到容器中,標識為'view'
$container->bind('view', function() { return new View(); });
/****************************************************************************/
// 控制器
class Controller {
// 將容器對象注入到控制器方法中
public function index(Container $container){
//獲取數據: 容器對象的make()方法
$data = $container->make('model')->getData();
//渲染模板
return $container->make('view')->fetch($data);
}
}
//客戶端調用
$controller = new Controller();
//以容器對象$container為實參調用
echo $controller->index($container);
```
* 將類的實例過程使用容器進行包裝,是一個非常不錯的解決方案
* 但是我還是覺得在控制器中充斥著大量的make()方法,吃相太難看,能不能再進行簡化呢?
* 當然可以,現在有請"Facade門面模式"閃亮登場
>[info] 門面: 給容器中的類方法調用,提供一個統一的靜態訪問接口
>> Controller.php 文件
```php
// 加載'模型類'
require 'Model.php';
// 加載'視圖類'
require 'View.php';
/********************************************************************************/
// 創建容器類,容器中可保存很多類型,這里僅以類實例方法來演示
class Container {
// 創建容器屬性,保存著類實例的創建方法
protected $instance = [];
// $alias: 類實例別名, $process: 類的實例化過程/函數/Closure閉包類型
public function bind($alias, Closure $process){
// 將類實例方法存入到容器中
$this->instance[$alias] = $process;
}
// 創建類的實例: 執行容器中的類的實例方法,$alias參數是容器中的類實例別名
public function make($alias, $params=[]){
return call_user_func_array($this->instance[$alias], []);
}
}
// 將要用到的類實例(Mode/View)綁定到容器中
$container = new Container();
// 將Model類實例的方法綁定到容器中,標識為'model'
$container->bind('model', function () { return new Model(); });
// 將View類實例的方法綁定到容器中,標識為'view'
$container->bind('view', function() { return new View(); });
/****************************************************************************/
//聲明Facade類,來接管對容器中類實例方法的調用
class Facade
{
// 控制器中調用到了Model類中的getData()方法, View類中的view()方法
// 我們就為這二個訪問創建一個統一訪問的靜態接口
// 調用方法使用的關鍵字static: 可以實現在靜態繼承上下文環境中,實現靜態成員調用者的動態設置
// 后面的Product 就是 Facade類的子類,如果不使用static::就無法動態調用子類中的成員
// 如果你用不到子類,這里可以不用static后期靜態綁定,可以使用self::替代
// 但是使用static , 會使代碼更具健壯性,能用性
protected static $container = null;
protected static $data = [];
public static function initialize(Container $container) {
static::$container = $container; // static::是后期靜態綁定的語法
}
// 設置獲取數據的靜態代理方法:getDate()
public static function getData(){
// static::$container 是當前引用類'Model'的實例
static::$data = static::$container->make('model')->getData();
}
// 設置渲染模板的靜態代理方法:fetch()
public static function fetch(){
// static::$container 是當前引用類'View'的實例
return static::$container->make('view')->fetch(static::$data);
}
}
// 聲明一個商品類
class Product extends Facade {
// 自定義的業務邏輯
}
/***************************************************************************/
// 控制器
class Controller {
//在構造方式中調用Facade中初始化方式,完成容器的實例化: 依賴注入
public function __construct(Container $container) {
Product::initialize($container);
}
// 將容器對象注入到控制器方法中
public function index(){
Product::getData(); // 獲取數據
return Product::fetch(); // 渲染模板
}
}
//客戶端調用
$controller = new Controller($container);
//以容器對象$container為實參調用
echo $controller->index();
```
* 門面Facade模式,使類方法調用更加的優雅,全部采用靜態方式統一調用
* 相當于給類中的方法戴了一個靜態面具/靜態代理
* 在Larval、ThinkPHP6.0 大量的使用到了Facade模式,請一定要掌握,否則會成為框架學習的最大障礙
*****
# :-: 二、路由的原理與實現
### 1、從pathinfo地址切割為獨立的數組單元
* pathinfo: 用目錄分隔符組成一個URL地址, 注意?后面的查詢字符串不屬于它
* 以請求地址: http://www.ouyangke.com/route.php/admin/index/hello 為例
* 可能大家對模塊的概念不是很清楚, 模板可以理解為一個應用, 例如一個網站會有一個前臺模塊, 一個后臺模塊, 各司其責
```php
$uri = $_SERVER['REQUEST_URI'];
$request = explode(DIRECTORY_SEPARATOR, $uri);
// 框架的基本原理,就是根據HTTP請求的URL地址, 從中解析出對應的函數進行處理
// 而操作函數有二個載體, 一是函數本身, 二是類方法
// 路由到函數, 其實這個函數就是所謂的閉包, 所謂路由到閉包, 就是由一個函數來執行HTTP請求
// 路由到類方法最為常用, 這個類, 就是控制器, 方法就是控制器類中的某個操作方法(本質仍是函數)
// 大家現在是否發現, 路由就是一個請求分發器, 要么將HTTP請求交給一個函數處理, 要么交給一個類方法來處理
echo '<pre>' . print_r($request,true);
```
### 2、從pathinfo數組中解析出:模塊,控制器和操作
```php
$uri = $_SERVER['REQUEST_URI'];
$request = explode(DIRECTORY_SEPARATOR, $uri);
// 我們所關心的只是模塊,控制器和方法,所以從數組的第3項開始來獲取數據
$route = array_slice($request,2,3);
// 查看獲取到的module/controller/action
echo '<pre>' . print_r($route,true);
// list()語法結構, 可以從將一個索引數組中每個元素,設置一個字符串鍵名,并轉為獨立變量使用
// list()并非函數, 因為它可以用到等號左邊, 接受賦值操作, 而函數是絕對不允許也不可能用在等號左邊的
list($module, $controller, $action) = $route;
echo "模塊:{$module}<br>控制器: {$controller}<br>操作: {$action}<br>";
// 將pathinfo內容解析到一個數組中保存
$pathinfo['module'] = $module;
$pathinfo['controller'] = $controller;
$pathinfo['action'] = $action;
// 使用數組函數: compace()簡化, 該函數可以將多個變量轉為關聯數組
$pathinfo = compact('module','controller', 'action');
echo '<pre>' . print_r($pathinfo,true);
```
>[info] 因為路由最終會指向一函數或類方法, 該函數/類方法是允許有參數的,并且可能不止一個。所以,路由必須要具備從URL中解析出正確的參數鍵值對的能力
### 3、從pathinfo數組中解析出:參數鍵值對
* 以請求地址: http://www.ouyangke.com/route.php/admin/index/hello/name/peter/position/lecture 為例
```php
$uri = $_SERVER['REQUEST_URI'];
$request = explode(DIRECTORY_SEPARATOR, $uri);
$values = array_slice($request,5);
for ($i=0; $i<count($values); $i+=2) {
// 將鍵值對中的第一個做為鍵,第二個做為值,該操作僅在第二個值存在的情況下執行
if( isset($values[$i+1])) {
$params[$values[$i]] = $values[$i+1];
}
}
// 查看解析出來的參數
echo '<pre>' . print_r($params,true);
```
### 4、創建與pathinfo中的`控制器/操作/參數鍵值對`對應的類和方法
```php
$uri = $_SERVER['REQUEST_URI'];
$request = explode(DIRECTORY_SEPARATOR, $uri);
$route = array_slice($request,2,3);
list($module, $controller, $action) = $route;
$pathinfo = compact('module','controller', 'action');
$values = array_slice($request,5);
for ($i=0; $i<count($values); $i+=2) {
// 將鍵值對中的第一個做為鍵,第二個做為值,該操作僅在第二個值存在的情況下執行
if( isset($values[$i+1])) {
$params[$values[$i]] = $values[$i+1];
}
}
class Index{
public function hello($id,$name){
return "Index控制器hello()操作: $id= {$id}, $name= {$name}";
}
public function demo($a, $b, $c){
return "Index控制器demo()操作,它的參數是: $a = {$a}, $b={$b}, $c={$c}";
}
}
echo call_user_func_array([(new $pathinfo['controller']()), $pathinfo['action']], $params);
```
*****
# :-: 三、模板引擎的原理與實現
* 模板引擎應該具備的基本功能:
* 存儲一組變量
* 讀取模板文件
* 組成前二者生成輸出
>[info] index.php文件
```php
// 模板引擎的調用腳本
// 加載模板引擎類
require 'template.php';
// 實例化模板引擎類
$template = new template('hello.php');
// 模板賦值: $hello = 'php模板引擎很簡單', 變量不存在則顯示默認值
$template->set('hello','php模板引擎很簡單');
// 輸出模板
echo $template->display();
// 瀏覽器訪問: index.php, 輸出: php模板引擎很簡單
```
>[info] hello.php文件
```php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>模板文件</title>
</head>
<body>
<h2>
<?php echo isset($hello) ? $hello : '歡迎訪問' ?>
</h2>
</body>
</html>
```
>[info] template.php文件
```php
// 模板引擎
class template{
private $data = [];
private $file;
// 獲取模板文件名
public function __construct($file){
$this->file = $file;
}
// 設置模板變量
public function set($name,$value){
$this->data[$name] = $value;
}
// 生成并輸出模板
public function display(){
// 提取模板變量: 關聯數組轉變量名值對
extract($this->data);
// 開啟輸出緩沖, 只生成數組,并不會立即輸出到瀏覽器
ob_start();
// 加載模板文件
include $this->file;
// 返回緩沖區中的內容
$string = ob_get_contents();
// 清空緩沖區并關閉輸出緩沖
ob_end_clean();
// 返回模板HTML
return $string;
}
}
```
- 序言
- PHP基礎
- 認識PHP
- 環境安裝
- PHP語法
- 流程控制
- PHP數組
- PHP函數
- PHP類與對象
- PHP命名空間
- PHP7新特性
- PHP方法庫
- PHP交互
- 前后端交互
- 項目常規開發流程
- MySQL數據庫
- 會話控制
- Ajax分頁技術
- 細說函數
- 類與對象
- 對象進階
- 類與對象進階
- OOP面向對象
- 設計模式
- 路由與模板引擎
- 異常類
- PHP爬蟲
- PHP抓取函數
- PHP匹配函數
- 正則表達式
- PHP字符串函數
- 抓取實戰
- PHP接口
- 了解接口
- PHP插件
- PHPSpreadsheet
- ThinkPHP6
- 安裝
- 架構
- 數據庫
- 數據庫操作
- 視圖
- 模版
- 模型
- 雜項
- 命令行
- 交互
- 微信小程序
- 介紹
- 配置
- 組件
- 交互
- API
- 其他知識
- 百度小程序
- 介紹
- 配置
- 組件
- 交互
- API
- 其他知識
- Linux
- 服務器上線流程
- 安裝svn
- MySQL
- 認識MySQL
- MySQL函數
- 雜項
- composer依賴管理工具