PHP成员变量获取对比

有如下4个代码示例,你认为他们创建对象,并获得成员变量的速度排序是怎样的?

1:将成员变量设置为public,通过赋值操作给成员变量赋值,直接获取变量

	class Foo {
		public $id;
	}
 
	$data = new Foo;
	$data->id = 10;
	echo $data->id;

2:将成员变量设置为public,通过构造函数设置成员变量的值,直接获取变量

        class Foo2 {
		public $id;
		public function __construct($id) {
			$this->id = $id;
		}
	}
 
	$data = new Foo2(10);
	echo $data->id;

3:将成员变量设置为protected,通过构造函数设置成员变量的值,通过成员方法获取变量

 
     class Foo3 {
		protected $id;
		public function __construct($id) {
			$this->id = $id;
		}
 
		public function getId() {
			return $this->id;
		}
	}
	$data = new Foo3(10);
	echo $data->getId();

4:将成员变量设置为protected,通过构造函数设置成员变量的值,通过魔术方法获取变量

 
     class Foo4 {
		protected $id;
		public function __construct($id) {
			$this->id = $id;
		}
 
		public function __get($key) {
			return $this->id;
		}
	}
	$data = new Foo4(10);
	echo $data->id;

按执行速度快慢排序: 1243
咱们先看其opcode:

1:

   	1  ZEND_FETCH_CLASS	4 	:4 	'Foo'
	2  NEW      			$5	:4
	3  DO_FCALL_BY_NAME			0          
	4  ASSIGN     				!0, $5
	5  ZEND_ASSIGN_OBJ			!0, 'id'
	6  ZEND_OP_DATA				10
	7  FETCH_OBJ_R			$9	!0, 'id'
	8  ECHO        				$9

2:

	1  ZEND_FETCH_CLASS	4 	:10	'Foo2'
	2  NEW             		$11	:10
	3  SEND_VAL        			10
	4  DO_FCALL_BY_NAME		1 
	5  ASSIGN    				!1, $11
	6  FETCH_OBJ_R			$14	!1, 'id'
	7  ECHO        				$14

3:

	1  ZEND_FETCH_CLASS	4 	:15	'Foo3'
	2  NEW         			$16	:15
	3  SEND_VAL     			10
	4  DO_FCALL_BY_NAME			1          
	5  ASSIGN  	   			!2, $16
	6  ZEND_INIT_METHOD_CALL	!2, 'getId'
	7  DO_FCALL_BY_NAME		0 	$20     
	8  ECHO       				$20

4:

	1  ZEND_FETCH_CLASS	4  :21	'Foo4'
	2  NEW          		$22	:21
	3  END_VAL      			10
	4  DO_FCALL_BY_NAME		1          
	5  ASSIGN        			!3, $22
	6  FETCH_OBJ_R  		$25 !3, 'id'
	7   ECHO  				$25

根据上面的opcode,参照其在zend_vm_execute.h文件对应的opcode实现,我们可以发现什么?

一、PHP内核创建对象的过程分为三步:

  1. ZEND_FETCH_CLASS 根据类名获取存储类的变量,其实现为一个hashtalbe EG(class_table) 的查找操作
  2. NEW 初始化对象,将EX(call)->fbc指向构造函数指针。
  3. 调用构造函数,其调用和其它的函数调用是一样,都是调用zend_do_fcall_common_helper_SPEC

二、魔术方法的调用是通过条件触发的,并不是直接调用,如我们示例中的成员变量id的获取(zend_std_read_property),其步骤为:

  1. 获取对象的属性,如果存在,转第二步;如果没有相关属性,转第三步
  2. 从对象的properties查找是否存在与名称对应的属性存在,如果存在返回结果,如果不存在,转第三步
  3. 如果存在__get魔术方法,则调用此方法获取变量,如果不存在,报错

回到排序的问题:

一、第一个和第二个的区别是什么?

第二个的opcode比第一个要少,反而比第一个要慢一些,因为构造函数多了参数,多了一个参数处理的opcode。参数处理是一个比较费时的操作,当我们在做代码优化时,一些不必要的参数能去掉就去掉;当一个函数有多个参数时,可以考虑通过一个数组将其封装后传递进来。

二、为啥第三个最慢?

因为其获取参数其本质上是一次对象成员方法的调用,方法的调用成本高于变量的获取

三、为啥第四个比第三个要快?

因为第四个的操作实质上获取变量,只不过其内部实现了魔术方法的调用,相对于用户定义的方法,内部函数的调用的效率会高。因此,当我们有一些PHP内核实现的方法可以调用时就不要重复发明轮子了。

四、为啥第四个比第二个要慢?

因为在PHP的对象获取变量的过程中,当成员变量在类的定义不在在时,会去调用PHP特有的魔术方法__get,多了一次魔术方法的调用。

总结一下:

  1. 使用PHP内置函数
  2. 并不是事必面向对象(OOP),面向对象往往开销很大,每个方法和对象调用都会消耗很多内存。
  3. 尽量少用魔术方法 — 除非有必要,不要用框架,因为框架都有大量的魔术方法使用。
  4. 在性能优先的应用场景中,将成员变量设置为public,不失为一种比较好的方法,当你需要用到OOP时。
  5. 能使用PHP语法结构的不要用函数,能使用内置函数的不要自己写,能用函数的不要用对象

2013年的多与少

多,甲骨文字形,从二“夕”。“夕”本义指“黄昏时刻”,转义指“黄昏时刻农夫聚集”。“二夕”叠加指“农夫加倍聚集”。表示数量大,与“少”、“寡”相对。
少,小篆从小,本义:不多。

2014年的第一天,带着包包大人,和家人一起爬山,登高而没有望远。
睡醒,起床,开始这篇该有的年度总结。

跨过一年,表示我离不二的年纪不远了,呵,回首一年,生活因包包大人的到来而发生了彻底的改变,技术没有明显的进步,进了想去的公司。
一年,有得有失,在选择中成长;
一年,有累有喜,在成长中成熟;
一年,有多有少,在变化中变化。

一、2013年的多与少

多了什么?
多了一个小天使
多了一份责任
多了一个完完整整的家,有你们的地方就是家
多了并不是多余,只是增加。

少了什么?
少了二人世界,世界将围绕小天使转
少了看书的时间
少了周末的爬山和运动
少了并不是真少了,只是转移,有更多需要你来兼顾。

多了什么?
多了能够见识的东西,更多的同事,更多的学习资料、更多的组件,更多的之前都没有见过的东西。
多了需要面对的用户,海量的用户,一个小小的手抖可能就是一大堆的投诉
多了需要考量的因素,所处的环境更复杂

少了什么?
少了责任,不再需要考虑他人的成长,关注更具体的事情。
少了思考,思考个人的成长?思考自己的价值,思考过去所做的事情,思考将来要做的?这些少了。慢慢就少了,无声无息的。
不在那个位置,你就不会去思考那个位置的问题,所谓 在其位谋其政,在其位的驱动力没有了,是否还能自动的去思考?
少了沉淀 一方面博客没有及时更新了,一方面看书的时间也减少了,每天围绕着需求在变。

一些个人感觉:
重回一线,开始了重新写代码的日子。
站的位置不同,看到的东西也不同,开发是最了解实现的人,也应该是最能发现bug的人,只是我们懒了,
如何去做?自测,严格的自测,除了正常流,遍历代码中所有的异常流,只是能有几人可以做到,因为我们都相信我们的代码是没有问题的。

更深刻的体会了业务决定实现,也决定了你对一个技术使用熟练程度。不经历一线的考验的技术不可能到一个很熟练的程度。

最重要的是什么?我们知道事分轻重缓急,重要紧急的事先做,只是往往我们认不清自己在做的是不是最重要的。
一个时间点上,你最重要的是什么?
一件事中,你最重要的是什么?
一些人里,你最重要的是谁?

2013年是一个变化年,生活状态的变化,因为变化,对比上一篇总结的计划:
1、过PMP【完成】
2、看5+管理 10+技术 10+其它书籍 【完成】只是效果不是太好,特别是临近这两三个月。
3、熟悉YII框架的源代码,对其各个模块有清晰的认识(貌似这里没量化了)【基本完成】最近在工作中也用得比较多
4、加强对Linux的认识(量化一些,虐APUE两遍,并结合PHP内核)【未完成】
5、完成媳妇的两个梦想【完成】
6、加强在工作上的掌控力,项目的按时完成率在80%以上。【需求变化】
7、Blog – 平均每月两篇+,【没有完成】博客没有及时更新,这就不找借口了,主要原因是个人没有积累,也没有去关注了。

二、2014年计划

1、博客 两周一篇 是为了沉淀,也是为了让自己思考,给自己留下思考的时间;
2、读书 与上面相关,虽然这是一种投入产出比不高的活动,但其精髓在于慢,数量为40;
3、技术 需要对linux有更深入的了解,APUE,linux内核,操作系统,系统架构;
4、运动 每两周至少大活动量的运动一次。
5、包包大人健康的长大
6、老婆大人的新年愿望

三、感恩

感谢老婆大人,是你辛苦将我们的宝贝包包大人带到这个世界,辛苦了!!
感谢两边的父母亲,你们养育了我们,为我们操碎了心,小宝宝出来了,你们还得操心,谢谢!
感谢在WS的朋友和同事,虽然离开了,但是你们曾经对我的帮助铭刻在心。
感谢兄弟的一路相随!
感谢er、reeze、张老板,terry,blank,感谢这一年在我生命中出现的人们,感恩。

四、结束语

2013还可回首望望,只是已经过去,现在还在我们手里,把握现在的你手里的,你身边的,相信未来会更美好。
2013~2014,很好的年份,1314,希望没有找到另一半早点成家,成了家的早点有小宝宝,一生一世。

五、附录A 2013读书列表

《你的灯亮着吗?》读书笔记

1、当遇到一个问题时,先不要急着去解决问题,而是问下自己 问题是什么?为谁解决问题?谁有问题? 确切的说是需要寻找问题的本质,更全面的考虑问题
2、问题其实是人期望的东西和你体验的东西之间的差别,简单点说与预期不符。
3、不要过早的下结论,但是也不要忽略你的第一印象
4、当解决一个问题的时候,可能会带来一个新的问题,从而子子孙孙无穷尽也。
5、问题最难处理的却是意识到问题的存在,这里的关键是思考,思考之后才会能发现问题。
6、一旦我们把一个东西当作一个问题,我们通常会给它“变形”
7、当你在寻找问题定义的道路上疲倦地游荡时,不要忘记随时都回头看看,看看你是不是已经迷路了。
8、注意你说话的上下文,有些信息是你知道,而别人不知道的,所以,当说一件事情的时候需要确保信息对等,如果信息不对等,请在你的表述中将这些信息带上。
9、一旦你用文字表述一个问题,请仔细推敲这些文字,以使这种表述在每个人的头脑中都是同一个意思。
10、当别人能够很好地解决自己的问题的时候,千万不要越俎代庖。这时不是看问题了,而是解决问题,一线的问题让一线的人解决吧。
11、如果某人能够解决这个问题,但是他本人却并不会遇到这一问题时,那么你们首先要做的就是让他也感受到这个问题。
12、当指责别人没有解决问题时,试试换过来指责你自己-即使只有一秒钟。
13、你的灯亮着吗?
14、问题的根源常常在你自己身上。
15、问题从哪儿来?找到问题的根源。
16、在这个世界上有两种人,一种人做事,另一处人给别人找事做。这个世界上也可能有另外两种人,一种人做事,一种人享受荣誉。
17、看看我们现在处理的难题,那些你看起来不可能解决的问题只需要再多花一点时间就能解决。当你解决了这个问题时,回过头来看看,觉得也许没有想象中的那么困难,行百里者半于九十
18、追求什么,就能够学到什么
19、不管看上去如何,人们很少知道他们想要什么,直到你给了他们想要的东西。
20、最终的分析表明,并没有多少人真的希望他们的问题被解决。
21、开始之前,想想我是否真的想要一个解决方法?
22、解决问题不要太快,快往往代表着仓促,仓促往往会容易出错。
23、过去的已经过去,现在的还在现在,将来的永远是将来。忘掉过去,把握现在,相信将来!
24、首先,对自己要真诚。对自己要真诚,在这里的意思是,在你靠近一个解决方案、甚至一个定义之前,你必须考虑道德的问题,然后再开始降低你的敏感度。这种考虑永远都不会是浪费你的时间,因为解决问题永远都不会是一个道德上中立的行为――不管它是多么的吸引它的从业者们。