开发过程中的偏误

开发的一生应当这样度过:当他回首往事的时候,不会因为没有时间陪妹子而悔恨,也不会因为没有浏览过祖国的大好河山而愧疚。这样,在临终的时候,他就能够说:‘我已把自己整个的生命和全部的精力献给了世界上最壮丽的事业——编程。

作为一个开发人员,每天吭吭哧哧的编码,调试,面对产品MM的需求变更,应对测试MM的各种挑战,呼啦啦,需求上线了,看数据,有告警,马上处理,没有告警,安心做下一个需求,眼睛一睁一闭,一天就过去了,要是一闭不睁呢?

在无比享受的开发过程中,我们可能会存在一些偏误,你见或不见,它就在那里,不增不减,不悲不喜。

在开发的世界中,总会有一种老子天下第一的气势,所谓文无第一,武无第二,开发也是。当一个需求开发完成后,我们都会认为我们的代码是很完美的,它能很好的实现产品的需求,没有bug,当测试MM说开发哥,你这里是不是有bug?第一反应是:怎么可能?是不是环境有问题。这是属于开发的过度自信偏误,但这并不是开发独有的,我们大多数人都有这个问题,只是强弱程度不一样。过度自信偏误对专家的影响比对非专家的影响要大,男人比女人更大,作为以男性居多的开发领域的专家,这个问题比较突出就不奇怪了。

建议:我们需要基于现场的实际情况做出自己的判断。

隔壁组开始用Go写他们的业务了,NB大发了,要不我们也试试?昨天那个分享中有介绍node.js,现在用的人蛮多的,咱们是不是也用一下试试?现在业内流行特定领域语言,咱们也是不是开发一套……

为什么我们会对大家都在使用的一些东西比较关注?为什么?从众心理。剧场中,当表演到精彩处,有一个人带头鼓起掌来,于是整个剧场都将掌声雷动;喜剧节目或访谈节目时,在关键位置插入笑声往往会引发观众的笑声。如果只是你一个人,也许就没有这样的效果了。
技术人员对技术有着特殊的激情,当一堆人开始讨论某些技术的时候,如果自己不知道一些总会觉得不太和谐,于是开始研究,应用,甚至为这种技术布道。

建议:对于热门的技术和大家都在谈论的技术,了解可以,具体应用到业务,需要做详细的评估,确认此种技术出现的应用场景,以及是否适用我们当前的业务,一种技术的引进有时是种进步,有时会成为技术负债。

系统告警,组内的人都收到了短信,可这是一个公共模块,谁去处理呢?你想,嗯,总会有人去处理的,于是等啊等啊,终于,一堆的客户投诉到了客服那里,事故升级,大家都挨批了。什么原因?三个和尚没水喝的故事再次重演了呗。如同一堆人一起拉绳子,人越多,个人的贡献越低。这被称之为社会性懈怠,在团队里不仅我们的效率会被克制,我们的责任也会被克制,如同刚才告警的例子,谁也不想为之负责。但这是一种理由性行为:假如使出半力就行,又不会引起注意,为什么要使出全力呢?

建议:团队业务模块化,责任到人,通过彰显个人效率来缓和这种懈怠。

以上只是我们常遇到的一小部分偏误,不仅仅是我们开发,大部分人也有同样的问题,偏误很多,只是你有没有清醒的认识到?

奇志大兵的相声中有这样一句词:偏爱,偏爱离正爱就不远了吧

偏误也是!

PHP的$this变量

手册上的一个有意思的小示例。

http://www.php.net/manual/zh/language.variables.basics.php

	$this = 'text'; // error
	$name = 'this';
	$$name = 'text'; // sets $this to 'text'
	echo $$name;

在PHP的词法分析时,$this变量是符合其规则的,在语法解析生成中间代码时,PHP内核会根据变量类型在生成赋值的中间代码时判断是否为$this变量,如果是则报错。这里为什么要报错呢?因为this作为一个特殊的变量,在对象的成员方法等调用初始化时会将this变量添加到活动符号表。

在类的成员方法里面,可以用 ->(对象运算符):$this->property(其中 property 是该属性名)这种方式来访问非静态属性。

当一个方法在类定义内部被调用时,有一个可用的伪变量 $this。$this 是一个到主叫对象的引用(通常是该方法所从属的对象,但如果是从第二个对象静态调用时也可能是另一个对象)。

在词法分析、语法分析并生成中间代码时,$this作为一个特殊的变量存在,特别是在生成中间代码时,代码中充斥着对于this的特殊处理。这些都是为后面的运行做准备,如识别标记出某处使用this变量,在存储opcode的zend_op_array结构体中专门有一个变量this_var标识是否有this变量。一个函数或一个类方法都会生成一个新的zend_op_array,在生成中间代码时,判断当前变量是否为this变量。

this变量在执行过程中会有两种存在状态,一种是全局传递的状态,存储在EG(This),一种是当前作用域状态,以this变量存储在EG(active_symbol_table)(当前执行环境的活动符号表)。
在我们执行一个 op_array 时,比如一个对象的方法,PHP内核会给这个 op_array 生成一个 zendexecutedata ,在生成初始化时,EG(This) 会添加到EG(active_symbol_table) 。
在方法调用过程中,如果有用到this变量,则会直接取EG(active_symbol_table)的值。

那么一个对象中的EG(This)在哪里初始化呢?
就EG(This)变量本身来说,在我们初始化PHP的执行环境时,它和其它全局变量(如EG(scope)等)一样都会被初始化为NULL。
对于一个对象来说,当我们创建了一个对象,调用时,PHP内核会将当前获得的对象直接赋值给EG(This),而这个当前获得的对象是在通过new操作生成对象时创建的对象本身。

如下这个简单示例:

	class Foo {
	     public $var = 10;
 
	     function t() {
	          echo $this->var;     
	     }
 
	     function t2() {
	     	echo 33;
		}
	}
 
	$foo = new Foo();
	$foo->t();

其主程序流程生成的中间代码如下:

        function name:  (null)
	number of ops:  8
	compiled vars:  !0 = $foo
	line     # *  op                           fetch          ext  return  operands
	---------------------------------------------------------------------------------
	   2     0  >   NOP                                                      
	  15     1      ZEND_FETCH_CLASS                              4  :1      'Foo'
	         2      NEW                                              $2      :1
	         3      DO_FCALL_BY_NAME                              0          
	         4      ASSIGN                                                   !0, $2
	  16     5      ZEND_INIT_METHOD_CALL                                    !0, 't'
	         6      DO_FCALL_BY_NAME                              0          
	         7    > RETURN                                                   1

this变量原始的对象值出生在 opcode NEW,经过了赋值(ASSIGN)后,在方法初始化时,将变量本身传递给执行环境的调用者,调用者又在执行调用(DO_FCALL_BY_NAME)时将变量传递给EG(This),当执行这个方法的op_array时,初始化当前作用域的环境(zend_execute_data)时,会将EG(This)作为$this变量添加到活动符号表,后续方法中的$this变量的使用就会直接取符号表的变量。

PHP面向对象的历史

PHP面向对象的历史

PHP最开始的perl脚本,到C语言版的PHP/FI,再到PHP/FI 2.0、PHP3.0,直到PHP4,引入Zend Engine使PHP更加的强大,并在PHP5引入新的Zend Engine2,重写PHP的面向对象模型,使PHP不仅可以快速开发,同时也可以实现更加复杂的架构,甚至满足企业应用。

PHP最开始并没有面向对象,直到PHP4才有一些面向对象的影子,到PHP5才真正实现面向对象模型。大概来说,PHP面向对象历史包括两个阶段:

PHP4-Zend Engine阶段

此时并没有真正的面向对象,因为PHP根本没有实现面向对象的三大特性,所有的成员方法和成员函数都是公有的,成员变量通过var声明。

此时的构造函数和类名一样,序列和反序列化时能调用魔术函数_sleep 和 __wakeup。嗯,这是是叫魔术函数而不是魔术方法,因为它本来就是独立出来的函数,当执行序列化时,PHP会判断当前变量是什么类型,如果是IS_OBJECT,则会自动调用__sleep函数。

在4.0.2以后可以使用parent::调用父类的方法。这里的parent仅仅是函数调用时的一个特殊处理。

在PHP的内核实现中类和函数共用一个opcode(ZEND_DECLARE_FUNCTION_OR_CLASS),通过extended_value字段区分,类和函数的存储已经区分开。

总的来说,PHP4的面向对象有点脚手架的味道,各种定制后有了一些面向对象的形。

PHP5-Zend Engine2阶段

5.0.0引入Zend Engine2,至此PHP才真正引入了面向对象的机制。
Zend Engine2重写了PHP的面向对象模型,其中包括对构建器和析构器的定义,增加的私有成员变量、静态成员变量、接口、重载等面向对象特性以及新增加了魔术方法实现。除了面向对象特性外,Zend Engine2引入了异常处理控制流。具体见: http://www.zend.com/engine2/ZendEngine-2.0.pdf

5.1.0 新增:__isset 和 __unset 方法。

5.3.0 新增: __invoke 方法、 后期静态绑定、 heredoc 和 nowdoc 支持类的常量和属性的定义、__callStatic 方法等

5.4.0 增加Traits,Trait 和类相似,但它的目的是用细粒度和一致的方式来组合功能。Trait 不能实例化。它为传统继承增加了水平特性的组合;也就是说,应用类的成员不需要继承。

总的来说,PHP5已经实现了面向对象模型,可以基于PHP5实现企业级应用。但是一些新的功能和特性,在实际的开发过程中使用得并不多,如Traits、命名空间等。很多时候,业务决定技术,需求决定实现。

然,此篇仅为整理之作,只为理自己对于PHP面向对象的思路。
久不沾笔,些许生疏。

参考资料

  • http://php.net/manual/zh/history.php.php
  • http://www.php.net/ChangeLog-4.php
  • http://www.php.net/ChangeLog-5.php