表单提交中的Get和Post的异同点

  • get 请求一般用于向服务端获取数据,post 一般向服务端提交数据
  • get 传输的参数在 url 中,传递参数大小有限制,post 没有大小限制,
  • get 不安全,post 安全性比get高
  • get请求在服务端用Request.queryString 接受 ,post 请求在服务端用Requset.form 接受

echo(),print(),print_r()的区别

  • echo: 输出一个或多个参数

  • print: 输出一个参数

  • print_r: 打印简单信息

  • var_dump: 打印带类型的变量(类型,长度,值)

构造函数和析构函数

  • 构造函数:__construct :会在每次创建新对象时先调用此方法

  • 析构函数:__destruct :会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行

析构函数即使在使用 exit() 终止脚本运行时也会被调用。在析构函数中调用 exit() 将会中止其余关闭操作的运行。

析构函数在脚本关闭时调用,此时所有的 HTTP 头信息已经发出。脚本关闭时的工作目录有可能和在 SAPI(如 apache)中时不同。

试图在析构函数(在脚本终止时被调用)中抛出一个异常会导致致命错误。

数组[‘a’, ‘b’, ‘c’] 转换成字符串 ‘abc’

echo implode(‘’,[‘a’, ‘b’, ‘c’]);
echo join([‘a’, ‘b’, ‘c’],'');

获取字符串’aAbB’中A首次出现的位置

$str=‘aAbB’;
echo strpos($str,"A");

编写一段用最小代价实现将字符串完全反序, e.g. 将 “1234567890” 转换成 “0987654321”.

1、使用函数
echo strrev("Hello World!");

2、不使用函数
$s = '1234567890';
$o = '';
$i = 0;
while(isset($s[$i]) && $s[$i] != null) {
    $o = $s[$i++].$o;
}

echo $o;

数组处理函数

PHP数组处理函数大全

字符串处理函数

PHP字符串处理函数大全

public、protected、private、final 区别

PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承

类的静态调用和实例化调用

  • 静态调用不需要实例化即可调用

  • 静态方法不能调用非静态属性,因为非静态属性需要实例化后,存放在对象里

  • 静态方法可以调用非静态方法,使用 self 关键字。php 里,一个方法被 self:: 后,自动转变为静态方法

  • 调用类的静态函数时不会自动调用类的构造函数

PHP 不实例化调用方法

静态调用、使用 PHP 的反射

面向对象

  • 封装:能降低依赖,实现松耦合
  • 继承:子类自动继承父类的属性和方法,并可以重写,增加代码重用性,PHP不支持多继承
  • 多态:多个子类虽然都继承父类的同一个方法,但是可以得到不同的结果,增加了代码的灵活性

请用递归实现一个阶乘求值算法 F(n): n=5;F(# n)=5!=54321=120

function F($n)
{
    if ($n == 0) {
        return 1;
    } else {
        return $n * F($n - 1);
    }
}

将字符长fang-zhi-gang 转化为驼峰法的形式

// 方法一:
function Fun($str)
{
    if (isset($str) && !empty($str)) {
        $newStr = '';
        if (strpos($str, '-') > 0) {
            $strArray = explode('-', $str);
            $len      = count($strArray);
            for ($i = 0; $i < $len; $i++) {
                $newStr .= ucfirst($strArray[$i]);
            }
        }
        return $newStr;
    }
}

// 方法二:
function Fun($str)
{
    $arr1 = explode('_', $str);
    $str  = implode(' ', $arr1);
    return ucwords($str);
}

数组内置的排序方法有哪些?

sort($array);   //数组升序排序
rsort($array);  //数组降序排序
asort($array);  //根据值,以升序对关联数组进行排序
ksort($array);  //根据建,以升序对关联数组进行排序
arsort($array); //根据值,以降序对关联数组进行排序
krsort($array); // 根据键,以降序对关联数组进行排序

用PHP写出显示客户端IP与服务器IP的代码

$_SERVER["REMOTE_ADDR"]

语句include和require的区别是什么?为避免多次包含同一文件,可用(?)语句代替它们?

1、加载失败的处理方式不同
include与require除了在处理引入文件的方式不同外,最大的区别就是:
include在引入不存在的文件时,产生一个警告且脚本还会继续执行,
require则会导致一个致命性错误且脚本停止执行。
如果hello.php不存在,echo ‘world’这句是可以继续执行的。
如果hello.php不存在,echo ‘hello’这句是不会执行的,到require时就停止了。

2、include()是有条件包含函数,而 require()则是无条件包含函数。
3、文件引用方式
include有返回值,而require没有
可以用include_once,require_once代替,表示文件只引入一次,引入之后则不在引入,作为优化点

PHP 不使用第三个变量实现交换两个变量的值

list($b,$a)=array($a,$b);
var_dump($a,$b);

写一个方法获取文件的扩展名

//方法一
function get_extension($file){
    return substr(strrchr($file,'.'), 1);
}

//方法二
function get_extension($file){ 
    return end(explode('.', $file));
}

echo get_extension('fangzhigang.png'); //png

用PHP打印出前一天的时间格式是2017-3-22 22:21:21

$a = date("Y-m-d H:i:s", strtotime("-1 days"));

PHP 如何接口调用?

CURL

用PHP header()函数实现页面404错误提示功能

Header("HTTP/1.1 404 Not Found");

全局变量

变量名 说明
$GLOBALS 引用全局作用域中可用的全部变量
$_SERVER 服务器和执行环境信息
$_GET HTTP GET 变量
$_POST HTTP POST 变量
$_FILES HTTP 文件上传变量
$_REQUEST HTTP Request 变量
$_SESSION Session 变量
$_ENV 环境变量
$_COOKIE HTTP Cookies

魔术方法

方法名 说明
__get() 当调用一个未定义的属性时访问此方法
__set() 给一个未定义的属性赋值时调用
__isset() 当在一个未定义的属性上调用isset()函数时调用此方法
__unset() 当在一个未定义的属性上调用unset()函数时调用此方法
__call() 当调用一个未定义(包括没有权限访问)的方法时调用此方法
__autoload() 使用尚未被定义的类时自动调用
__construct() 构造函数,见上
__destruct() 析构函数,见上
__clone() 使用clone方法复制一个对象时,对象会自动调用__clone魔术方法
__toString() 将一个对象转化成字符串时自动调用
__sleep() 串行化的时候用
__wakeup() 反串行化的时候调用
__set_state() 当调用var_export()时,这个静态 方法会被调用(自PHP 5.1.0起有效)
__invoke() 当尝试以调用函数的方式调用一个对象时,__invoke 方法会被自动调用

魔术常量

附录1:运算符优先级

附录2:HTTP状态码

一般记住以下几个就可以了:

  • 200 :成功
  • 302 :重定向
  • 404 :没找到页面
  • 500 :服务器内部错误
  • 502 :从远程服务器接收到了一个无效的响应
  • 503 :超载暂时无法处理请求
  • 504 :超时,未从远端服务器获取请求

composer是什么?Composer和PHP有什么关系?

Composer 是PHP的一个依赖(dependency)管理工具,在我们的项目中声明所依赖的外部工具库(libraries),Composer 可以帮助我们安装这些依赖的库文件。Composer可以全局安装也可以局部安装,默认不是全局安装的,是基于指定项目的某个目录进行安装的。

composer团队协作怎么保证版本统一?

  • 安装组件使用composer install 而不是composer update,
  • .lock文件加入版本控制当中。

OOP思想,特征和其意义

  • 抽象、封装、继承和多态是面向对象的基础。

  • 抽象:提取现实世界中某事物的关键特性,为该事物构建模型的过程。对同一事物在不同的需求下,需要提取的特性可能不一样。得到的抽象模型中一般包含:属性(数据)和操作(行为)。这个抽象模型我们称之为类。对 类进行实例化得到对象。

  • 封装:封装可以使类具有独立性和隔离性;保证类的高内聚。只暴露给类外部或者子类必须的属性和操作。类封装的实现依赖类的修饰符(public、protected和private等)

  • 继承:对现有类的一种复用机制。一个类如果继承现有的类,则这个类将拥有被继承类的所有非私有特性(属性和操作)。这里指的继承包含:类的继承和接口的实现。

  • 多态:多态是在继承的基础上实现的。多态的三个要素:继承、重写和父类引用指向子类对象。父类引用指向不 同的子类对象时,调用相同的方法,呈现出不同的行为;就是类多态特性。多态可以分成编译时多态和运行时多态。

  • 帮助理解: https://www.cnblogs.com/waj6511988/p/6974291.html

OOP的七大设计原则是什么?

  • 开闭原则:对扩展开放,对修改关闭

  • 里氏替换原则:继承 必须保证 父类中的性质在子类中仍然成立

  • 依赖倒置原则:面向接口编程,而不面向实现类

  • 单一职责原则:控制 类的 粒度的大小 ,增强内聚性,减少耦合

  • 接口隔离原则:要为各个类提供所需的专用接口

  • 迪米特法则:迪米特法则(Law of Demeter)又叫作最少知识原则(The Least Knowledge Principle),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和 陌生人说话。英文简写为: LOD。

  • 合成复用原则:尽可能使用组合或者聚合等关系来关联类,其次才考虑使用继承。

前五个合称 SOLID原则(单一职责原则、开放关闭原则、里氏替换原则、接口隔离原则和依赖倒置原则)

mvc框架的生命周期说一下

session与cookie的区别是什么?

为什么session的安全性大于cookie?

session与cookie的应用场景有哪些?

php7新特性

标量类型声明
返回值类型声明
语法糖:null合并运算符,太空船操作符
define允许定义常量数组
匿名类,
新增了一些函数intdiv(),随机函数,

1、php7.0相比于php5.6的新特性 参考:http://php.net/manual/zh/migration70.new-features.php

2、php7.1相对于php7.0的新特性 参考:http://php.net/manual/zh/migration71.new-features.php

3、php7.2相对于php7.1的新特性 参考:http://php.net/manual/zh/migration72.new-features.php

php8新特性

新增联合类型(Union Types);
添加了 WeakMap;
添加了 ValueError 类;
新增的特性大多是语法糖,主要是JIT。
JIT是一种编译器策略,它将代码表述为一种中间状态,在运行时将其转换为依赖于体系结构的机器码,并即时执行,在PHP8中,Zend VM不需要解释某些操作码,并且这些指令将直接作为CPU级指令执行。
IT和opcache区别
要说明opcode cache与JIT的区别,得先明白,字节码,又叫中间码与机器码的区别。简答的说,提升php执行效率,更快了。

参考:鸟哥博客

手写一个单例模式吧

所谓单例模式,即在应用程序中最多只有该类的一个实例存在,一旦创建,就会一直存在于内存中!

单例设计模式常应用于数据库类设计,采用单例模式,只连接一次数据库,防止打开多个数据库连接。

单例模式的编写遵循三私一公,代码如下

<?php

class Database
{
    private $instance;

    private function __construct()
    {
        // Do nothing.
    }

    public static function getInstance()
    {
        if (!(self::$instance instanceof self)) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __clone()
    {
        // Do nothing.
    }
}

$a = Database::getInstance();
$b = Database::getInstance();
// true var_dump($a === $b);

php垃圾回收机制

关键词:使用了引用计数器

  • PHP可以自动进行内存管理,清除不需要的对象,主要使用了引用计数。

  • 在zval结构体中定义了ref_countis_ref , ref_count是引用计数 ,标识此zval被多少个变量引用 , 为0时会被销毁 。is_ref标识是否使用的 &取地址符强制引用。

  • 为了解决循环引用内存泄露问题 , 使用同步周期回收算法。

  • 当数组或对象循环的引用自身 , unset掉数组的时候 , 当refcount-1后还大于0的 , 就会被当成疑似垃圾 , 会进行遍历 ,并且模拟的删除一次refcount-1如果是0就删除 ,如果不是0就恢复。

参考:https://www.php.net/manual/zh/features.gc.php

php-fpm是什么?

重点:php-fpm是fastcgi的实现。

PHP5.3.3开始集成了php-fpm模块,不再是第三方的包了。

PHP-FPM提供了更好的PHP进程管理方式,可以有效控制内存和进程、可以平滑重载PHP配置。

php-fpm的运行模型?

重点:多进程同步阻塞模式

  • php-fpm是一种master(主)/worker(子)多进程架构模型。

  • 当PHP-FPM启动时,会读取配置文件,然后创建一个Master进程和若干个Worker进程(具体是几个Worker进 程是由php-fpm.conf中配置的个数决定)

  • Worker进程是由Master进程fork出来的。

  • master进程主要负责CGI及PHP环境初始化、事件监听、Worker进程状态等等,worker进程负责处理php请 求。

  • master进程负责创建和管理woker进程,同时负责监听listen连接,master进程是多路复用的;

  • woker进程负责accept请求连接,同时处理请求,一个woker进程可以处理多个请求(复用,不需要每次都创建销毁woker进 程,而是达到处理一定请求数后销毁重新fork创建worker进程),但一个woker进程一次只能处理一个请求。

cgi,php-cgi,php-fpm,fastcgi的区别?

cgi

cgi是一个web server与cgi程序(这里可以理解为是php解释器)之间进行数据传输的协议,保证了传递的是标准数据。

php-cgi

php-cgi是php解释器。他自己本身只能解析请求,返回结果,不会管理进程。php-fpm是调度管理php-cgi进 程的程序。

Fastcgi

Fastcgi是用来提高cgi程序(php-cgi)性能的方案/协议。

cgi程序的性能问题在哪呢?"PHP解析器会解析php.ini文件,初始化执行环境",就是这里了。 标准的CGI对每个请求都会执行这些步骤,所以处理的时间会比较长。

Fastcgi会先启一个master,解析配置文件,初始化执行环境,然后再启动多个worker。当请求过来时,master会传递给一个worker,然后立即可以接受下一个请求。这样就避免了重复劳动,效率自然提高。而且当worker 不够用时,master可以根据配置预先启动几个worker等着;当然空闲worker太多时,也会停掉一些,这样就提 高了性能,也节约了资源。这就是Fastcgi的对进程的管理。

php-fpm

fastcgi是一个方案或者协议,php-fpm就是FastCGI的后端实现,也就是说,进程分配和管理是FPM来做的。官 方对FPM的解释:【Fastcgi Process Manager】【Fastcgi 进程管理器】。 php-fpm的管理对象是php-cgi,他负责管理一个进程池,来处理来自Web服务器的请求。 对于php.ini文件的修改,php-cgi进程是没办法平滑重启的,有了php-fpm后,就把平滑重启成为了一种可能, php-fpm对此的处理机制是新的worker用新的配置,已经存在的worker处理完手上的活就可以歇着了,通过这 种机制来平滑过度的。

php-fpm如何完成平滑重启?

修改php.ini之后,php-cgi进程的确是没办法平滑重启的。

php-fpm对此的处理机制是新的worker用新的配置,

已经存在的worker处理完手上的活就可以歇着了,通过这种机制来平滑过度。

php-fpm和nginx的通信机制是怎么样的?

怎么选定用tcp还是套接字的方式和nginx通信?

tcp方式是面向链接的协议,更稳定。

套接字效率更高,但是限制nginx和php-fpm都在一台服务器。

php-fpm在请求链路的体现,画出来?

php-fpm有几种工作模式?

PHP-FPM进程管理方式有动态(Dynamic)、静态(Static)、按需分配(Ondemand)三种。

动态

会初始化创建一部分worker,在运行过程中,动态调整worker数量,最大worker数受pm.max_children和process.max

  1. 当空闲进程数小于min_spare_servers时,创建新的子进程,总子进程数小于等于pm.max_children,小于 等于process.max
  2. 当空闲进程数大于max_spare_servers,会杀死启动时间最长的子进程
  3. 如果子进程(idle状态)数大于max_children,会打印warning日志,结束处理
  4. process小于 max_children ,计算一个num,启动num个worker
  5. 优点:动态扩容,不浪费系统资源
  6. 缺点:所有worker都在工作,新的请求到来需要等待创建worker进程,最长等待1s(内部存在一个1s的定时 器,去查看,创建进程),频繁启停进程消耗cpu,请求数稳定,不需要频繁销毁

静态

启动固定大小数量的worker,也有1s的定时器,用于统计进程的一些状态信息,例如空闲worker个数,活动 worker个数

  1. 优点:不用动态判断负载,提升性能
  2. 缺点:如果配置成static,只需要考虑max_children数量,数量取决于cpu的个数和应用的响应时间,一次启 动固定大小进程浪费系统资源

按需分配

php-fpm启动的时候不会启动worker进程,按需启动worker,有链接进来后,才会启动

连接到来时(只有链接,不没有数据也会创建,telnet也会创建),创建新worker进程,worker进程数的创建收 max_children设置限制,也受限于全局的process.max设置(三种模式都受限此,下文中有全局配置项讲解), 如果空闲时间超过了process_idle_timeout的设置就会销毁worker进程

优点:按流量需求创建,不浪费系统资源, 缺点:因为php-fpm是短连接的,如果每次请求都先建立连接,大流量场景下会使得master进程变得繁忙,浪费cpu,不适合大流量模式

不推荐使用此模式

工作模式 特点
动态 均衡优先,适合小内存服务器,2g左右
静态 性能优先, 适合大内存机器
按需分配 内存优先,适合微小的内存,2g以下

怎么选定php-fpm的worker进程数?

动态建立进程个数

  • N+20% 到 M/m之间

  • N是cpu核数,M是内存,m是每个php进程内存数

静态进程个数

  • M/(m*1.2)
  • pm.max_requests, 设置最大请求数,达到这个数量以后,会自动长期worker进程,繁殖内存意外增长

cpu密集型的pm.max_children不能超过cpu内核数,但是web服务属于IO密集型的,可以将pm.max_children的值设置大于cpu核数。

注意:PHP程序在执行完成后,或多或少会有内存泄露的问题。这也是为什么开始的时候一个php-fpm进程只占 用3M左右内存,运行一段时间后就会上升到20-30M。所以需要每个worker进程处理完一定的请求后,销毁重 新创建。

php-fpm如何优化?

  • 避免程序跑死(hang) 在负载较高的服务器上定时重载php-fpm,reload可以平滑重启而不影响生产系统的php脚本运行,每15分钟reload一次,定时任务如下:

0-59/15 * * * * /usr/local/php/sbin/php-fpm reload

  • 合理增加单个worker进程最大处理请求数,减少内存消耗 最大处理请求数是指一个php-fpm的worker进程在处理多少个请求后就终止掉,master进程会重新respawn新 的。该配置可以避免php解释器自身或程序引起的memory leaks。默认值是500,可以修改为如下配置:

pm.max_requests = 1024

  • 开启静态模式,指定数量的php-fpm进程,减少内存消耗

说下你最常用的php框架(laravel框架)的生命周期?

怎么理解 依赖注入(DI)与控制反转(Ioc)?

这让我想起了怎么理解nginx正向代理和反向代理...

IOC(inversion of control)控制反转模式;控制反转是将组件间的依赖关系从程序内部提到外部来管理;

DI(dependency injection)依赖注入模式;依赖注入是指将组件的依赖通过外部以参数或其他形式注入;

依赖注入和控制反转说的实际上是同一个东西,它们是一种设计模式,这种设计模式用来减少程序间的耦合。

依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。

  • 依赖注入是从应用程序的角度在描述,可以把依赖注入,即:应用程序依赖容器创建并注入它所需要的外部 资源;

  • 而控制反转是从容器的角度在描述,即:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要 的外部资源

laravel的控制反转

是通过反射和递归实现的容器,容器作为全局注册表,使用容器的依赖注入做为一种桥梁来解决依赖,使类之间 耦合度更低。

php的弱类型是怎么实现的?

php是通过c语言进行实现,但是c语言为强类型,那php的弱语言类型是通过PHP底层设计了一个zval(“Zend value”的缩写)的数据结构,可以用来表示任意类型的PHP值。通过共同体实现弱类型变量声明。

简单说下对php 底层变量(zval)数据结构的理解

  • 变量存储结构 变量的值存储到以下所示zval结构体中。 zval结构体定义在Zend/zend.h文件,其结构如下:
typedef struct _zval_struct zval;
...
struct _zval_struct {
    /* Variable information */
    zvalue_value value; /* value */
    zend_uint refcount__gc;
    zend_uchar type; /* active type */
    zend_uchar is_ref__gc;
};

PHP使用这个结构来存储变量的所有数据。和其他编译性静态语言不同,PHP在存储变量时将PHP用户空间的变量类型也保存在同一个结构体中。这样我们就能通过这些信息获取到变量的类型。

zval结构体中有四个字段,其含义分别为:

属性名 含义 默认值
refcount_gc 表示引用计数 1
is_ref_gc 表示是否为引用 0
value 存储变量的值
type 变量具体的类型
  • 变量类型

zval结构体的type字段就是实现弱类型最关键的字段了,

type的值可以为: IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT和IS_RESOURCE 之一。

从字面上就很好理解,他们只是类型的唯一标示,根据类型的不同将不同的值存储到value字段。

除此之外,和他们定义在一起的类型还有 IS_CONSTANT和IS_CONSTANT_ARRAY。

这和我们设计数据库时的做法类似,为了避免重复设计类似的表,使用一个标示字段来记录不同类型的数据。

常见的设计模式有哪些?

简单说下单例模式,注册树模式,适配模式,策略模式,观察者模式。

单例

//简单工厂模式
//适用场景:创建对象比较少,业务逻辑不太复杂
<?php

//产品
class bike
{
    public function product($destination)
    {
        print_r($destination);
    }
}

class car
{
    public function product($destination)
    {
        print_r($destination);
    }
}

//工厂
class simpleFactory
{
    public function createBike($obj)
    {
        return new $obj();
    }
}

$factory = new simpleFactory();
$bike    = $factory->createBike("bike");
$bike->product("hello,bike");
$car = $factory->createBike("car");
$car->product("hello,car");
?>

观察者模式

//观察者模式 //适用场景:订阅者通知 //自己对观察者模式对理解: 需求:有事情变化你要通知我 实现: 1、要通知对人必须在我这里注册,否则我不知道要通知谁。 2、想要获取通知的人必须遵守我的规则,实现我指定的通知事件,不然我没办法统一通知,(比如高考 查分数,只支持电话查询,你非要微信查我肯定通知不到你) 3、事件发生的时候我会逐一通知你们。

<?php

//定义一个事件产生接口
interface obServer
{
    public function update($event_info = null);
}

//定义观察者接口

abstract class genEvent
{
    private $ob_servers = [];

    //增加观察者
    public function addObs($ob_server)
    {
        $this->ob_servers[] = $ob_server;
    }

    //通知
    public function notify()
    {
        if (!empty($this->ob_servers)) {
            foreach ($this->ob_servers as $ob_server) {
                $ob_server->update();
            }
        }
    }
}

class obServer1 implements obServer
{
    public function update($event_info = null)
    {
        echo "观察者1 收到执行通知\n";
    }
}

class obServer2 implements obServer
{
    public function update($event_info = null)
    {
        echo "观察者2 收到执行通知\n";
    }
}

class event extends genEvent
{
    //事件触发
    public function trigger()
    {
        $this->notify();
    }
}

//实现
$event = new event();
$event->addObs(new obServer1());
$event->addObs(new obServer2());
$event->trigger();
?>

适配器模式

<?php

//定义抽象类
abstract class Toy
{
    public abstract function openMouth();

    public abstract function closeMouth();
}

//定义dog
class dog extends Toy
{
    public function openMouth()
    {
        echo "Dog open Mouth\n";
    }

    public function closeMouth()
    {
        echo "Dog close Mouth\n";
    }
}

//cat
class cat extends Toy
{
    public function openMouth()
    {
        echo "Cat open Mouth\n";
    }
    public function closeMouth()
    {
        echo "Cat close Mouth\n";
    }
}

//红枣狗可以自己判断开关
interface redTarget
{
    public function doMouthOpen();
    public function doMouthClose();
}

interface greenTarget
{
    public function operateMouth($type=0);
}

//组成适配器
class redAdapter implements redTarget
{
    private $adaptee;

    //初始化对象
    public function __construct(Toy $adaptee)
    {
        $this->adaptee = $adaptee;
    }

    //委派调用Adaptee的sampleMethod1方法
    public function doMouthOpen()
    {
        $this->adaptee->openMouth();
    }

    public function doMouthClose()
    {
        $this->adaptee->closeMouth();
    }
}

//组成绿色遥控
class greenAdapter implements greenTarget
{
    private $adapter;
    //初始化对象
    public function __contruct(Toy $adapter)
    {
        $this->adapter = $adapter;
    }
    public function operateMouth($type = 0)
    {
        $type ? $this->adapter->openMouth() : $this->adapter->closeMouth();
    }
}

//测试
class testDriver
{
    public function run()
    {
        //实例化玩具狗
        $dog         = new dog();
        $adapter_dog = new redAdapter($dog);
        $adapter_dog->doMouthOpen();
        $adapter_dog->doMouthClose();
    }
}

$test = new testDriver();
$test->run();

注册树模式

<?php
//创建单例
class Single
{
    public           $hash;
    static protected $ins = null;

    final protected function __construct()
    {
        $this->hash = rand(1, 9999);
    }

    static public function getInstance()
    {
        if (self::$ins instanceof self) {
            return self::$ins;
        }
        self::$ins = new self();
        return self::$ins;
    }
}

//工厂模式
class RandFactory
{
    public static function factory()
    {
        return Single::getInstance();
    }
}

//注册树
class Register
{
    protected static $objects;

    public static function set($alias, $object)
    {
        self::$objects[$alias] = $object;
    }

    public static function get($alias)
    {
        return self::$objects[$alias];
    }

    public static function _unset($alias)
    {
        unset(self::$objects[$alias]);
    }
}

//调用
Register::set('rand', RandFactory::factory());
$object = Register::get('rand');
print_r($object);