MVC工作原理

6 PHP框架 - 图1

用户注册举例:
用户注册的时候需要有一个界面,用于填写账号密码,这个界面就是视图层。
用户把填写的账号密码点击提交之后,发送给了后端,后端需要对用户填入的账号密码进行一些处理,如账号密码是否符合规定等操作,要根据我们的业务规定进行处理。这个层叫做业务处理层。
业务处理层通过之后,将用户填写的账号密码发送给数据操作层,进行一些判断,账号是否唯一等,符合要求之后将其存入数据库,然后给业务处理层返回操作结果,业务处理层更具状态再显示不同的界面或者加载不同的提示。
6 PHP框架 - 图2

MVC框架

laravel, ThinkPHP,Yii2,CI,Yaf

单一入口工作原理:

mvc框架大多是单一入口的,一般每次请求都会经过index.php。
用一个处理程序文件处理所有的HTTP请求,根据请求时的参数不同区分不同的模块和操作的请求。
优势:
可以进行统一的安全性检查。
集中处理程序
缺点:
url不美观(URL重写进行URL美化)
处理效率会稍低

多入口的框架,安全性不好控制。所以现在很多框架都是单入口的。
对于一个项目中包含前后台的程序,可以创建两个入口。如adminindex.php作为后台入口

模板引擎原理:

php有很多可以使php代码和html代码分开的模板引擎(并不是完全分离,只是用更简洁的语法替换原生PHP的写法,使的HTML中嵌入的PHP代码更加容易理解和美观)
如:smarty Twig HAML liquid等模版引擎
MVC框架都自带模版引擎了,如thinkPHP自带的模版引擎
laravel框架使用的是blade模版引擎,所有的Blade视图都会被编译成原生PHP代码并缓存起来,这意味着对应用的性能而言Blade基本上是零开销。
Blade的两个最大优点是模板继承和切片:

  1. return view('layouts,child')->with('data',$data);
<!-- 存放在 resources/views/layouts/master.blade.php -->
<html>
    <head>
        <title>App Name - @yield('title')</title>
    </head>
    <body>
        @section('sidebar')
            This is the master sidebar.
        @show

        <div class="container">
            @yield('content')
        </div>
    </body>
</html>

<!-- 存放在 resources/views/layouts/child.blade.php -->
@extends('layouts.master') 继承

@section('title', 'Page Title')  该标题

@section('sidebar')
    @parent 追加内容
    <p>This is appended to the master sidebar.</p>
@endsection

@section('content') 注入内容
    <p>This is my body content.</p>
Hello, {{ $data->name }} 两个大括号里面直接写PHP变量


if语句

@if (count($records) === 1)
    I have one record!
@elseif (count($records) > 1)
    I have multiple records!
@else
    I don't have any records!
@endif


循环

@foreach ($users as $user)
    <p>This is user {{ $user->id }}</p>
@endforeach


包含子视图

@include('shared.errors')

@endsection 结束继承


模版引擎的工作原理:

强大完善的正则表达式替换库。
如:laravel给视图传递参数:return view(‘layouts,child’)->with(‘data’,$data);
模版引擎会把layouts/child.blade.php文件全部抓取,然后将里面的{{$data->name}}类似的标签替换成$data变量的值。

常见框架优缺点

laravel框架

优点:
1 框架结构组织清晰(抽象了中间件,任务,服务等模块),提供的artisan开发工具提升开发效率,
2 大量的第三方开源库,composer进行包依赖
3 安全机制非常齐全,提交表单的数据验证(验证有差不多80种,能想到的基本都有),提交数据时产生随机_token验证,避免非法提交,能避免跨域攻击;
4 blade 模版引擎
5、中间件和路由,对访问进行过滤及控制,调用函数类和方法前进行判断请求的合法性,避免非法请求;
6、错误处理机制简单好用,如果出错直接调用$error->all(),即可输出全部错误,对表单验证尤其好用;
7 包含的功能丰富,队列,搜索,数据库迁移,定时脚本
8 思想先进,服务容器,服务提供者,缓存

缺点:
1、稍复杂,上手比一般框架要慢;
2、大量引用第三方包,但我们只使用类中的部分方法,代码显得有些冗余;
3、代码有些过于优雅丧失了一些性能。性能与一般框架稍有优势,

总结:
laravel设计思想是很先进的,集合了php比较新的特性,以及各种各样的设计模式,Ioc容器,依赖注入,契约,门面,路由,验证,定时任务,数据迁移,单元测试等,所以导致了效率稍低的问题。但是web程序的运行效率从来就不在框架,而在数据库,框架那一点点消耗根本不会是什么负担。个人感觉利大于弊。
就像将军需要手下办一件事,将军直接呼叫士兵,让士兵办事可以很快的做完。但是这样做没有规则制度,不利于管理。所以将军需要吩咐给大校,-大尉-士兵,利于管理。

简述laravel一次请求的生命周期

1 index.php
Laravel 采用了单一入口模式,应用的所有请求入口都是 public/index.php 文件。
载入Composer生成的自动加载设置。并从 bootstrap/app.php 文件获取到 Laravel 应用实例。Laravel 的第一个动作就是创建一个自身应用实例 / 服务容器

2 HTTP / Console 内核
接下来,传入的请求会被发送给 HTTP 内核或者 console 内核,(控制台访问则进入console内核)
让我们现在只关注位于 app/Http/Kernel.php 的 HTTP 内核。
HTTP 内核继承自 Illuminate\Foundation\Http\Kernel 类,它定义了一个 bootstrappers 数组,数组中的类在请求真正执行前进行前置执行。 这些引导程序配置了错误处理,日志记录,检测应用程序环境,以及其他在请求被处理前需要完成的工作。
HTTP 内核同时定义了一个 HTTP 中间件 列表,所有的请求必须在处理前通过这些中间件,这些中间件处理 HTTP session 的读写,判断应用是否在维护模式, 验证 CSRF token 等等。

3 服务提供者
在内核引导启动的过程中最重要的动作之一就是载入 服务提供者 到你的应用。所有的服务提供者都配置在 config/app.php 文件中的 providers 数组中。
服务提供者负责引导启动框架的全部各种组件,例如数据库、队列、验证器以及路由组件。因为这些组件引导和配置了框架的各种功能,所以服务提供者是整个 Laravel 启动过程中最为重要的部分。

4 分发请求(路由)
一旦应用完成引导和所有服务提供者都注册完成,Request将会移交给路由进行分发。路由将分发请求给一个路由或控制器,同时运行路由指定的中间件

什么是依赖注入

应用程序对需要使用的依赖「插件」在编译(编码)阶段仅依赖于接口的定义,到运行阶段由一个独立的组装模块(容器)完成对实现类的实例化工作,并将其「注射」到应用程序中称之为「依赖注入」。

就是:面向接口编程

什么是依赖注入容器

在依赖注入过程中,由一个独立的组装模块(容器)完成对实现类的实例化工作,那么这个组装模块就是「依赖注入容器」。(就是laravel的服务容器)
通俗一点讲,使用「依赖注入容器」时无需人使用 new 关键字去实例化所依赖的「插件」,转而由「依赖注入容器」自动的完成一个模块的组装、配置、实例化等工作。

什么是控制反转(IoC)

IoC(Inversion of Control)译为 「控制反转」,我认为控制反转是一个约定,实现方式是依赖注入
什么是「控制反转」?对象 A 功能依赖于对象 B,但是控制权由对象 A 来控制:
控制反转意思是说将依赖类的控制权交出去,由主动变为被动。
控制反转 的最终目标是为了 实现项目的高内聚低耦合,而 实现这种目标 的方式则是通过 依赖注入 这种设计模式。

<?php
class a
{
    public function add(){
         // BInterfaceImp是一个接口的实现,a类依赖于BInterfaceImp类,需要在a类中创建b类的实现。
         //如果想使用Interface的另外一个实现如:CInterfaceImp,就需要在a类中更改代码了。耦合关系就产生了。如果其他很多地方都是用的BInterfaceImp,需要改动的地方就太多了。
        $b  = new BInterfaceImp();
     }
}


IoC最大的好处是什么?
解耦合,高扩展。因为把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是实现于某种接口的),只要修改XML就可以了,
IoC最大的缺点是什么?
(1)生成一个对象的步骤变复杂了(
(2)对象生成因为是使用反射编程,在效率上有些损耗。但相对于IoC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高。

实现策略
IoC是一个很大的概念,可以用不同的方式实现。其主要形式有两种:
1 依赖注入:容器负责的组件的装配,它会把符合依赖关系的对象通过构造函数传递给需要的对象。将依赖关系作为构造函数参数传入的做法称为构造器注入。
2 依赖查找:容器提供回调接口和上下文条件给组件。容器将调用这些回调方法,从而让应用代码获得相关资源。

服务容器

laravel学习之IOC容器分析https://www.cnblogs.com/gikkson/p/6849052.html
Laravel 服务容器、服务提供器、契约实例讲解:https://learnku.com/articles/17638
神奇的服务容器: https://www.insp.top/learn-laravel-container
深入剖析 Laravel 服务容器https://learnku.com/articles/12575/deep-analysis-of-the-laravel-service-container


概述
Laravel 服务容器是用于 管理类的依赖执行依赖注入工具。。它的目的就是解耦依赖。
(依赖注入是一个花俏的名词,它实质上是指:类的依赖通过构造器或在某些情况下通过「setter」方法进行「注入」)
服务容器可以自动解析类。其实就是一个超级工厂的设计模式,该类位于:
vendor/laravel/framwork/src/Illuminate/Container/Container.php
Laravel 容器中,依赖解析的关键代码是:https://github.com/laravel/framework/blob/v5.7.20/src/Illuminate/Container/Container.php#L770-L813 ,无论是手动解析还是通过自动注入的方式,实现原理都是基于 PHP 的反射机制:

<?php
// 通过反射获取实现类构造函数
$constructor = $reflector->getConstructor();

// 如果实现类并没有定义构造函数,说明这个实现类没有相关依赖。
// 我们可以直接实例化这个实现类,而无需自动解析依赖(自动注入)。
if (is_null($constructor)) {
    array_pop($this->buildStack);
    return new $concrete;
}

// 否则获取到实现类构造函数依赖参数
$dependencies = $constructor->getParameters();
// 解析出所有依赖
$instances = $this->resolveDependencies(
    $dependencies
);

array_pop($this->buildStack);
// 创建服务实例并返回
return $reflector->newInstanceArgs($instances);


例子:
构造函数参数Mailer $mailer 表示$mailer必须是Mailer接口的实现

<?php
namespace App\Jobs;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Contracts\Bus\SelfHandling;
class PurchasePodcast implements SelfHandling{
    protected $mailer;
    /**
     * 创建一个新的实例
     * @param  Mailer  $mailer
     * @return void
     */
    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }
}


原则:
高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口。
抽象接口不应该依赖于具体实现。而具体实现则应该依赖于抽象接口。
所以这个 PurchasePodcast类 应该依赖于抽象接口,而非具体类。

所以这里Mailer类是接口,但是接口是不能被实例化的。解决这个问题,只需把接口绑定到一个具体的实现类上

如何知道Mailer接口的具体实现是什么?
laravel在服务启动的时候时候会运行服务容器,服务容器可以自动解析类。其实就是一个类似超级工厂的设计模式,
该类位于:vendor/laravel/framwork/src/Illuminate/Container/Container.php,
简单的一个服务容器:该类最起码需要一个绑定和一个解析绑定的方法
查看laravel目录下笔记:简单的服务容器例子

绑定接口到实现
一个类要被容器所能够提取,必须要先注册至这个容器。既然 laravel 称这个容器叫做服务容器,那么我们需要某个服务,就得先注册、绑定这个服务到容器,那么提供服务并绑定服务至容器的东西,就是 服务提供者(ServiceProvider)。
服务提供者主要分为两个部分,register(注册) 和 boot(引导、初始化),具体参考文档。register 负责进行向容器注册“脚本”,但要注意注册部分不要有对未知事物的依赖,如果有,就要移步至 boot 部分。
laravel几乎所有的服务容器绑定都是在服务提供者中完成。在一个服务提供者中,可以通过$this->app变量访问容器

我们可以在laravel的服务提供者(打开Laravel自带的config/app.php文件,将会看到一个providers数组)中有:Illuminate\Mail\MailServiceProvider::class,可以在这个邮件服务提供者中register方法重新进行服务容器绑定

好处:
再本类中我们需要发送邮件。由于该服务是被注入的,可以方便的使用其另一个实现来替换它。A类中构造函数注入的时候会从服务容器中解析出该接口的实现类,完全不需要修改主类A


服务提供者

服务提供者做什么事情:
一般而言,我们指的是注册事务,包括注册服务容器绑定,事件监听器,中间件,甚至路由。服务提供者是配置应用程序的中心所在。

所有的服务提供者都配置在 config/app.php 文件中的 providers 数组中,这些是将被应用程序加载的服务提供者类。当然,它们其中有许多是「延迟」提供者,意味着它们不会每次请求都加载,只会按需加载。
Laravel框架服务提供者的目录一般都在Illuminate目录下,
默认的应用程序服务提供者在app/Providers 目录中。

编写服务提供者
所有服务提供者都需要继承 Illuminate\Support\ServiceProvider 类。大多数服务提供者都包含 register和 boot 方法。在 register 方法中,您应该只能将事务绑定到 服务容器。不应该在 register 方法中尝试注册任何事件监听器,路由或者任何其他功能。
Artisan 命令行可以生成一个新的提供者通过 make:provider 命令:
php artisan make:provider SktServiceProvider
在服务提供者中,你可以通过 $this->app 属性访问服务容器,几乎所有服务容器的绑定都是在 服务提供者 中进行的
6 PHP框架 - 图3

注册服务提供者
要注册您的提供程序,只需将其添加到数组:

'providers' => [
    App\Providers\ComposerServiceProvider::class,
],


使用服务提供者
方法1:
通过 app ()->make (‘ 提供器中绑定时的键名 ‘); 来获取功能类对象,用 bing 方法绑定的,获取出来的都是新对象,用 singleton 绑定的,获取的是单例。

$fooService1 = app()->make('App\Contracts\SktContract'); //这里产生的不是单例,这里用bind方法绑定的
$fooService3 = app()->make('skt'); //这里产生的是单例,因为这里skt是用singleton绑定的


方法2:使用facade门面更简单的调用
创建一个文件:/vendor/laravel/framework/src/Illuminate/Support/Facades/Skt.php

<?php
namespace Illuminate\Support\Facades;
use Illuminate\Contracts\Console\Kernel as ConsoleKernelContract;
/**
 * @method static bool list_team() //加上这行注释是为了让php_storm编辑器能够追踪函数
 * Class Skt
 * @package Illuminate\Support\Facades
 */
class Skt extends Facade  //类名随便起,但是为了规范还是和功能类为主也叫Skt吧(功能类是叫SktService)
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor
    {
        return 'skt'; //这里一定要return一个绑定的键名,代表你接下来会获得那个键名绑定的对象
    }
}


上面这个类主要是getFacadeAccessor方法返回的字符串要和你绑定时候的键名对应上,这里返回的是skt,之前在服务提供器中,不是有一句:

$this->app->singleton('skt',function(){ //注意,这里的键名是skt
    return new SktService();
});


好了,Facades 文件也写完了,接下来我们回到控制器中,把调用功能类的代码直接改为以下代码:

<?php
use Illuminate\Support\Facades\Skt; //这里要引入facades
class Test extends Controller
{
public function test()
    {
        Skt::list_team('LOL-ssb');
    }
}

这里引入了 Illuminate\Support\Facades\Skt; 获得了一个叫做 Skt 的类,这个 Skt 类的 getFacadeAccessor 方法 return 了一个’skt’ 字符串,然后框架内部获取到这个’skt‘字符串之后,又根据这个’skt’ 字符串为键名,找到了之前通过 singleton 方法绑定的对象,那个对象才是真正的功能类。Skt::list_team () 调用的实际上是 SktService 类的 list_team () 方法

总结:
laravel的服务提供者中有:App\Providers\AppServiceProvider::class, AppServiceProvider 默认就在框架的加载列表中了,直接用就。对于简单的依赖,可以在这个APP服务提供者中register方法进行绑定接口到实现,而无需在写一个特定的服务提供者。

<?php
// IProduct是产品接口,ProductImpl是该接口的实现类
$this->app->bind('App\Facade\Service\Product\IProduct','App\Impl\Product\ProductImpl');

将接口和实现进行绑定之后,当一个类需要IProduct的实现的时候,将会注入ProductImpl实现类

但是一旦各种各样的功能都在 AppServiceProvider 类中进行绑定的话,就会很乱,不符合单一功能原则,最好还是一个功能类就创建一个服务提供器。不过要想引入功能类大可不必这么麻烦呢,直接用 trait,哪里想用就 use 进来就行了,省事。

反射

https://www.cnblogs.com/youyoui/p/7300340.html

反射机制,它提供了一套强大的反射API,允许你在PHP运行环境中,访问和使用类、方法、属性、参数和注释等,就算是类成员定义为private也可以在外部访问,不用创建类的实例也可以访问类的成员和方法。
其功能十分强大,经常用于高扩展的PHP框架,自动加载插件,自动生成文档,甚至可以用来扩展PHP语言。由于它是PHP內建的oop扩展,为语言本身自带的特性,所以不需要额外添加扩展或者配置就可以使用。

ReflectionClass 报告了一个类的有关信息
<?php
$class = new ReflectionClass('Person');//建立 Person这个类的反射类,Person为类名。
//getProperties()获取该类的所有属性。//getMethods();获取该类的所有方法
$properties = $class->getProperties();  
foreach($properties as $property) {  
    echo $property->getName()."\n";  
}

IoC中最基本的技术就是“反射”编程。反射又是一个生涩的名词,通俗的说反射就是根据给出的类名(字符串)来生成对象。这种编程方式可以让对象在生成时才决定要生成哪一种对象。

在过去,反射编程方式相对于正常的对象生成方式要慢10几倍,这也许也是当时为什么反射技术没有普遍应用开来的原因。但经SUN改良优化后,反射方式生成对象和通常对象生成方式,速度已经相差不大了(但依然有一倍以上的差距)。


Contract (契约)是什么?

Contract(契约)是 laravel 定义框架提供的核心服务的接口。Contracts其实就是倡导面向接口编程,来达到解耦的目的。

Laravel中大部分类是通过服务容器进行解析的,包括控制器、事件监听器、中间件、队列任务,甚至路由闭包等。因此,想要获取一个契约的实现,可以在被解析的类的构造函数中类型提示该契约接口。

<?php
namespace App\Jobs;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Contracts\Bus\SelfHandling;
class PurchasePodcast implements SelfHandling{
    protected $mailer;
    /**
     * 创建一个新的实例
     * @param  Mailer  $mailer
     * @return void
     */
    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }
}

当构造函数执行的时候,服务容器会读取类的构造函数上的类型提示(通过反射机制),然后注入适当的值。
问题:
契约定义的是接口,服务容器得到的是契约类型的提示,那它是怎么知道需要解析哪一种实现契约的类呢?
答:服务提供者中注册接口与实现的绑定。
如上例中的Mailer契约具体实现是怎么找到的?
具体步骤:服务提供者中的(app.php中的providers数组中定义)Database契约:
Illuminate\Mail\MailServiceProvider::class,打开这个文件,在register方法里面绑定了mailer的实现类是Illuminate\Mail\Mailer.php。
6 PHP框架 - 图4
打开Illuminate\Mail\Mailer.php,该类实现了Mailer契约。所以当PurchasePodcast类的构造函数执行的时候,服务容器会读取类的构造函数上的类型提示,然后注入适当的值。这个值就是通过服务容器中解析出的具体的实现类。
6 PHP框架 - 图5

Facades(门面) 是什么?

Facades(一种设计模式,通常翻译为外观模式)提供了一个”static”(静态)接口去访问注册到 IoC 容器中的类。提供了简单、易记的语法,而无需记住必须手动注入或配置的长长的类名。类似于一个类的别名。
laravel中的门面就是一个提供访问容器中对象的类。
在config/app.php中的aliases(别名)数组,列出了每个门面及其对应的底层类
如 ‘Mail’ => Illuminate\Support\Facades\Mail::class,打开这个文件:
6 PHP框架 - 图6
Mail门面继承Facade基类并定义了getFacadeAccessor方法,该方法的工作就是返回服务容器绑定类的别名,当用户引用Mail类的任何静态方法时,Laravel从服务容器中解析mailer绑定(服务提供者中已经绑定Mailer类到服务容器,绑定的别名是mailer),然后在解析出的对象上调用所有请求方法。

facades和Contract区别
Facades是通过一个简便的方式去访问注册到olc容器中的类,不再需要像契约那样从服务容器中类型提示和解析契约,实际上使用一个门面,如Cache门面,访问的是注册到容器中的Cache接口的实现。
而contrace契约实际上一个服务的接口,通过在服务容器中注册接口的实现,当使用契约的时候,服务容器会读取类的构造函数上的类型提示,然后注入适当的值。

其实facades和Contract的目的都是为了降低耦合,契约就像一个大商场,规定了每家门店只能卖东西。而facades就像是一家家门店,销售具体的东西。只是在laravel中,使用契约的时候,需要绑定一个该接口的实现到服务容器里面,这一步在服务提供者中完成。所以在使用上,这两个本质上没有区别,都是通过服务容器解析对象。

谈谈 Laravel 和 YII 框架的区别

在 YII 框架中的路由是通过书写 Controller、Action 间接定义路由,而 Laravel 中是在 route 路由文件中直接定义路由入口
Laravel 提供 ORM 对象关系映射,使读写数据库的操作更加简单
Laravel 提供更多的 Artisan 命令和脚手架开发
Laravel 的 Composer 扩展包比 Yii 框架更多,开发更加高效

thinkPHP框架

优点:容易上手
缺点:感觉没亮点,不像laravel那样先进的设计思想。
tp5向laravel框架进行了学习。中规中矩
6 PHP框架 - 图7

6 PHP框架 - 图8

6 PHP框架 - 图9

6 PHP框架 - 图10

6 PHP框架 - 图11

yaf

Yaf是一款C语言写的PHP框架。它以PHP扩展的方式运行框架。 在 PHP 启动的时候加载, 并常驻内存. 更快的执行速度, 更少的内存占用.
只实现了MVC最核心部分的功能:路由、MVC。
Yaf内核够精简稳定,所以,几乎不会遇到运行上的问题。风险可控,性能优异。当然,因为简单,所以,你需要实现DB的封闭、Session的扩展等操作。

优缺点:需要PHP原生代码,运行效率高,但是开发效率低
6 PHP框架 - 图12