php 调试利器: debug_backtrace

 

debug_backtrace是一个bug工具,如果加入到某个类函数里面,那么可以看到调用这个函数的类方法,依次上推,直至index.php
下面是我打印的一个yii2组件的日志:

public function actionBootstrap($app){
    
    $d = debug_backtrace();
    foreach($d as $e){
      $function = $e['function'];
      $class = $e['class'];
      $file = $e['file'];
      $line = $e['line'];
      echo $file.'('.$line.'),'.
      $class.'::'.$function.'()<br/>';
    }
    echo '<br/><br/>';

输出的日志为:

(),fecshop\services\Store::actionBootstrap()
/www/web/develop/fecshop/vendor/fancyecommerce/fecshop/services/Service.php(58),::call_user_func_array()
/www/web/develop/fecshop/vendor/fancyecommerce/fecshop/components/Store.php(22),fecshop\services\Service::__call()
/www/web/develop/fecshop/vendor/fancyecommerce/fecshop/components/Store.php(22),fecshop\services\Store::bootstrap()
/www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(316),fecshop\components\Store::bootstrap()
/www/web/develop/fecshop/vendor/yiisoft/yii2/web/Application.php(66),yii\base\Application::bootstrap()
/www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(267),yii\web\Application::bootstrap()
/www/web/develop/fecshop/vendor/yiisoft/yii2/base/Object.php(107),yii\base\Application::init()
/www/web/develop/fecshop/vendor/yiisoft/yii2/base/Application.php(206),yii\base\Object::__construct()
/www/web/develop/fecshop/appfront/web/index.php(44),yii\base\Application::__construct()

这样就可以记录出来这个函数被调用的位置。

php 一个容易被混淆的概念,深入理解成员变量和 属性。

本文章有误,不要阅读该文章。

1.对于public 定义的类的变量称为成员变量,譬如:

public $age;

2.通过 get开头的方法定义的为属性,他们都可以通过$ob->age 访问,譬如:

public function getSex() {}  
public function setSex($sex) {}

3.下面我们看一下他们执行的优先级

class ob{  
  # 类成员变量
  public  $age =10;  
  
  # 属性方法
  public function getAge(){  
    return 18;  
  }  
    # 魔术方法 
  public function __get($name){  
    return 22 ;  
  }  
  
}

如果我执行:

$ob = new ob();  
$ob->age

执行的结果为:

1.返回的是public 定义的成员变量, 10

2.如果去掉public $age的定义,那么返回的是魔术方法   22

因为魔术方法对应的是,当某些成员变量不存在的时候,就会访问魔术方法__get

3.如果去掉 __get的方法的定义,才会访问对象的属性,那么返回的是 18

 

二:下面我们执行一点高级点的php知识:

有时候我们看似一个简单的赋值,可能在后面执行了很多我们看不到的东西,这个在yii2中有很多地方用到了。

class ob{  
    public $age = 10;  
    
  public function  setScore($score){  
    $this->age = 22;   
    }  
   
  public function  getScore(){  
    $this->age = 55;
    return 199;  
    }  
}

当我们执行:

$ob = new $ob()  
echo $ob->age;  # 结果输出10,这个没有问题,很容易理解
echo $ob->score;  # 结果为199
echo $ob->age;  # 结果输出55 
$ob->score = 10;  #并没有给score赋值,而是给成员变量age赋值22
echo $ob->age;  # 结果输出55

上面我写的代码有点离谱,不过对于理解这个概念很有帮助,可以看到我们利用封装的对象$ob,进行赋值,在当前代码中并没有赋值,而是干了其他的事情。

譬如在yii的model有一个赋值的函数

$model->attributes = […],这里执行的就是一个属性,也就是setAttributes,结果保存到了private类成员变量$_attributes中了。

 

 

 

对接口和事件概念的深究

1.对于接口,个人认为更多的解决依赖颠倒。

接口的作用,1:可以解耦,我个人认为更确切的是,接口可以让依赖关系颠倒,原来是a和b的关系(a依赖b),变成了a和c(a定义c),b和c的关系(b依赖c),其中c是接口,通过中间层c,间接让b依赖a,有点控制反转的味道。 2.接口,定义类的某些方法不能更改方法名和方法传入的参数。

工作顺序的颠倒: 两个人合作做一个功能,A和B分别做2个模块,A是消费方,B是生产方,原来的先后顺序为: B生产出来一个方法,告诉A调用这个方法,来消费B生产的方法,

变成了:A定义个接口C,然后A使用C接口里面的方法实现了自己的功能,然后把C接口扔给B,让B去实现这个接口。

由生产方做主导的工作顺序,转变成消费方做主导的工作顺序。

详细的请看:http://www.fancyecommerce.com/2016/07/01/%e5%af%b9%e6%8e%a5%e5%8f%a3%e7%9a%84%e8%a7%81%e8%a7%a3-yii2/

2. 关于事件:

事件给人感觉很啰嗦,下面举个例子:
在电商系统加入购物车功能中,定义
A .为加入购物车,把产品的信息写入到购物车表中。
A 完成了后,老板说,加入购物车功能中得加入一个log记录功能,然后程序猿在原来的文件中加入了
B 日志功能
后来,老板说,加入购物车扣库存,程序猿在原来的文件中加入了功能C,扣产品库存
后来,老板说,加入优惠券,程序猿在原来的文件中加入了D功能
a,B,C,D功能都在一个文件中,后期很难维护。

升级模式:

分为购物车模块A,log模块B,产品模块C,优惠券模块D
让BCD三个模块实现对应的方法,然后给予A,然后A 实现购物车模块的加入购物车

也就是说,A需要等BDC三个模块完成,然后A才能完成,也就是说A依赖于BCD

继续升级:

A做了购物车模块,然后抛出事件,然后执行事件,后续如果有BCD想要添加功能,直接在事件触发前绑定事件就可以
这样,这样通过事件的配置文件,实现了解耦,也就是A的功能不依赖于BCD来,A 只需要抛出事件,然后BCD绑定过去,然后A触发即可,
这样A不依赖于BCD了。A可以先完成工作,然后,BCD的事件函数做好后,绑定上就可以,
而且在使用购物车模块的时候,我有很多事件,如果我想使用B日志功能,我就绑定上去,如果不使用我就不绑定,
这样就可以在不同的场景下面,有不同的购物车功能。

在上面,我们可以看到,每次添加事件,对于A来说,还是要更该A里面的内容,绑定事件。
我们进行下面的思考:

我是一个电商的开发商,你是一个我的系统的使用者,还是老样子啊,我的库包你是不能改动的
但是你又想改动我的功能
我在我的库包中,做了一个加入购物车的功能,请问,你如何做到不改动我的文件的前提下,在加入购物车这个动作中,添加你的一个功能呢,譬如你想在加入购物车的时候,添加一个日志记录功能。

解决办法:还是依赖注入,依赖的是配置文件,在配置文件中加入事件,我在做加入购物车的功能的时候,就想到了肯定很多人想在这里加代码,哪里我就提前部署好了事件,
通过配置文件中的事件配置,来绑定事件执行。
如果你想在加入购物车这个动作中加入你的代码,你可以做一个事件,然后在配置中加入你写的事件配置就可以了,对吧?
你没有改动我的系统文件,你改动的是配置文件。

总结:
接口:是为了解决依赖关系,由A依赖B,变成了,A定义接口,让B依赖接口,间接实现B依赖A
事件:A依赖BCD,变成了A定义事件,让BCD依赖事件,间接实现了让BCD依赖A,
配置文件注入:通过配置文件注入到组件的初始化中,可以在配置文件中添加事件配置,间接实现了 更改系统功能,但是不改动系统文件内容。

Terry对接口的见解 – php

A的行为 依赖于B的一些方法,但是B还没有开始做,所以,我们先通过接口实现定义各个空的方法,用来拿数据,大致的意思,我用一个比喻形容:A和B是革命党,A是 军方,需要用服装,B是生产方,必须等B生产好了,B告知A到哪里拿服装,哪里拿鞋子,A的服装队伍才能去拿衣服,也就是A依赖于B的信息,现在有了契 约,A直接告诉B衣服放到哪里,鞋子放到哪里,而且还有相应的生产尺码,A到时候去拿,等B生产好衣服鞋子的时候,按照契约C,直接把衣服和鞋子放到相应 的地方,对于A来说,有点皇帝的味道,到了时间,直接去拿衣服鞋子,另外一个比喻:税收就像接口,定义好了大家都要交税,国家就是定义接口的主体,原来是 农民B生产了粮食,A去拿粮食,A有点要饭的味道,因为A依赖于B,现在通过税收契约,直接规定B必须上交,不按照接口规定就办了你(报错退出),A有了 皇帝的味道。通过这个例子我们可以看到,原来是A依赖B,变成了,A定义C契约(接口),B依赖C,这样由A依赖B,间接实现了B依赖A。这就是接口的作 用。在产品设计方面可以颠倒主从依赖关系,有点控制反转的味道。

也就是让两个彼此影响的个人,通过接口,让其依赖相互颠倒

通过调用接口有什么好处呢?看似很啰嗦。我认为好处有如下几点: 1.在程序设计,尤其是产品,如果A依赖于B,A是产品的一部分,譬如用户组件(user component),B是用户自定义(譬如user,User extends ActiveRecord implements IdentityInterface),按照常理,需要B先行,然后A在行动,但是呢?在产品中,我们需要A先行动,B在行动,这就需要A行动的时候,告 诉B,你如何如何,这就是接口,告诉B要实现那些方法,A在B没有的情况下直接使用B的方法,然后强制B实现这些方法(通过接口),这样通过契约接口颠倒 了依赖,在程序实现上,完成了解耦, 也就是说,接口是使用方定义的,而不是提供方定义的,这个有点像客户让工厂加工某些东西,提供了图纸一样,如果B不按照图纸,A就不验收,就报错退出。 2.个人认为接口一旦存在,就是强制执行,除非法律更改(接口改变),在工作当中,为了扩展,经常的对某些方法进行修改,譬如 方法的参数增加了个数,由于php不想java那样,还可以限制传入的参数类型和返回的类型,所以php的接口限制要弱一些。不过还是比较有效,可以限制 开发者脑门一热,方法增加参数,或者把这个函数名改变了。

总结:接口的作用,1:可以解耦,我个人认为更确切的是,接口可以让依赖关系颠倒,原来是a和b的关系(a依赖b),变成了a和c(a定义c),b和c的关系(b依赖c),其中c是接口,通过中间层c,间接让b依赖a,有点控制反转的味道。 2.接口,定义类的某些方法不能更改方法名和方法传入的参数。

 

工作顺序的颠倒: 两个人合作做一个功能,A和B分别做2个模块,A是消费方,B是生产方,原来的先后顺序为: B生产出来一个方法,告诉A调用这个方法,来消费B生产的方法,

变成了:A定义个接口C,然后A使用C接口里面的方法实现了自己的功能,然后把C接口扔给B,让B去实现这个接口。

由生产方做主导的工作顺序,转变成消费方做主导的工作顺序。

 

另外:补充接口的另外的用途,可以做类型判断,譬如:

if ($identity instanceof IdentityInterface) { }

如果一个类继承了接口IdentityInterface,接可以使用instanceof 判断这个类是否实现了这个接口 ,进而可以在程序中做控制,譬如:

./yiisoft/yii2/web/User.php:202: if ($identity instanceof IdentityInterface) {

php – 一个关于在对象方法中require文件的有意思的地方

在php的文件包含,一般都是引入类文件,有点像加入类库的方式,现在探讨的是一种在php对象方法中require文件的特色。

在对象A中的a()方法中,require文件B,那么,在B文件中是可以使用$this->xxx(),

验证:我们在test/文件夹下面新建了两个文件  index.php  和 test.php

index.php文件内容如下:

<?php
class test{
  
  public function index(){
    
    echo 111;
    $this->test1();
  }
  public function test1(){
    require("./test.php");
  }
  public function xx(){
    
    echo 3333;
  }
}
$test = new test();
$test->index();

test.php:

fdssafsdafads
<?php
 echo $this->xx();
?>

访问 http://xx.com/test/index.php

可以看到输出结果:

111fdssafsdafads 3333

也就是说,$this 就是$test对象,

参看文件:yii2 页面功能块配置实现原理(前端+后端提供数据类),以及Views深度嵌套

结合yii2 view的利用ob函数生成view的原理,就会更加透彻。