# :-: 一、容器与门面
>[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依赖管理工具