PHP语言特性小结-2017.07.17

PHP语言特性小结 - 2017.07.17

背景

作为PHP工程师,也写了3年的PHP了,除了使用框架以及平时业务流程上的实现外,也不断加深了对语言的理解,这里记录一些关注过的特性,尤其是此种特性与OO之间的联系。因为对计算机语言设计没有一个特别清晰的概念,真正业务上使用过、了解的语言也只有C、PHP、Golang三种,希望能通过学习,掌握计算机语言设计上基本的范式,避免对一些语法糖沾沾自喜而错过真正的理论。话说《7天7语言》也买了1年了,并没有去阅读,啪啪打脸呀。


参考资料


正文

a. 关于php interface 和abstract class的使用场景?

  • 首先写接口/抽象类的程序员定义其他用户如何使用该抽象。如果只限定子类实现的方法,则使用接口;如果除了限定子类实现哪些方法,还提供了通用工具接口(已经实现的方法),则使用抽象类。一种普遍存在的最佳实践就是:当你拿不准一种抽象该使用抽象类还是接口的时候,设计一个接口,然后用一个抽象类implement它。
  • 具体到语言细节:
    • interface:可以用implements实现,也可以用extends继承,所有方法必须是public,类实现interface必须实现所有的public function,并且参数表保证包含关系。关于常量,接口常量与类常量的设计完全相同,唯一不同的就是无法被子类/接口覆盖。
    • abstract:当类用此关键字修饰时候,则不可以实例化;如果类中有1个或以上抽象方法则该类必须定义为抽象类;实现抽象方法时与接口的要求相同,(可见性要大于抽象类),抽象方法的实现必须包含声明的参数列表; 继承抽象类与普通的父子继承相同(可以overlload方法)
    • 区别:一个子类可以实现多个接口,但只能继承一个抽象类;并且接口里面的常量不可以被实现TA的类覆盖,抽象类可以overload,前提是开放程度要高于抽象类;

b. php foreach是否能保证array的顺序?

  • 可知结论就是foreach出的kv对和key的顺序无关,只与当前array中元素插入的顺序相关,先插入的先遍历。称之为线性遍历。而for循环以\(i的赋值为基础,和可以与\)i的算法相关了。

c. php中 $this self static 都代表什么,在什么场景下使用?

  • \(this和self经常放到一起对比,实际上比较好区分:静态的属性/方法在类内部被调用时候,因为不需要当前类被实例化,因此直接用self引用,并且在PHP中使用引用符号"::";反之,引用一个非静态的属性/方法时候,使用\)this比较合适。为什么把static单独提出来,因为这个关键字应用范围广(从我的角度看,过于广了,根据功能不同,应该拆开为3个关键字):
    • 在类中 static指定当前属性/方法为静态的,可以不实例化当前类,直接用::引用;
    • 在局部函数/方法中 static表示变量是局部静态变量,当多次调用这一函数的时候,本变量会保存上次调用时该变量的值。这一特性在递归调用时非常有作用,避免了递归调用缺少终止条件的情况,可以指定一个静态计数器到一定层数直接退出,以免无线循环用完系统资源;
    • 在php5.3之后,static用于指定后期静态绑定,解决self只能引用定义类中的成员,无法实现多态的问题;即使用static::value的方式引用value成员,完成一部分多态的特性,可以面向变量编程了。

d. trait关键字的特性,应用的场景、最佳实践?

  • 当有一段代码在类之间被copy paste的时候,可以多想想trait关键字;
  • 打破php单继承的限制(后续在设计模式中确定应用场景)5.4之后支持;但是使用的是多个class组合的方式,因此trait本身使用不需要继承,使用use关键字即可;
  • 类中use trait的时候,同名方法的优先级是:类中function > trait中function > 父类中function
  • use用在trait中时在class内部,use用在namespace时,在class定义外部文件头部,替代include、require
  • 当类中用多个trait的时候,trait内有同名方法会报fatal error。解决方法是显示的使用insteadof确定一个具体的那个方法
  • trait支持属性、方法、静态方法
  • trait中的属性,类无法定义同名

e. php override(重写/覆盖)与overload(重载)的含义?

  • 《PHP核心技术与最佳实践》中描述的:“php重载与c++等的重载概念不同,指的是动态创建类的属性与方法,而在JAVA中重载指的是在同一个类中可以包含同名、不同参数列表的方法。”

  • php实现重载的方法是使用magic methods实现,所有方法都必须是public,方法的参数都不可以通过引用传递。这些magic method 包括:__set __get __iset __unset __call __callStatic,提供了一种对所有未预先定义的值/方法的统一处理方式。理解一下__set、__get

  • override很明显是父类与子类之间的一种多态关系,子类对父类允许访问的方法进行重写(参数列表、返回值可以完全不同)。在PHP中子类可以对父类的同名方法进行重写,实现类方法的多态性,而像C++/JAVA这种利用不同参数列表的方式来实现重载,PHP是做不到的,因为其本来就是动态类型,参数本身就可以接收不同类型的参数,同时可以接收不定个数的入参,因此在同一个类中无法定义同名方法。命名空间解决了多个文件之间方法重名的问题。

  • 总结一下计算机语言上实现两者的差异(以对两者实现完整的JAVA为例):

    区别点 重载方法 重写方法
    参数列表 必须修改 一定不能修改
    返回类型 可以修改 一定不能修改
    异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
    访问 可以修改 一定不能做更严格的限制(可以降低限制)
  • php的重写参数列表可以修改、返回类型也可以修改,异常可以修改,访问不能做更严格的限制。所以可以看到PHP在实现多态上与JAVA有很大区别。

f. final关键字需要注意的点,以及用法?

  • 只可以修饰类和方法,继承一个final class会报fatal error

g. 全局变量作用域以及使用方式?

  • PHP中在局部需要引用全局变量,必须显示的指定global。与C语言正好相反,C语言在局部(如函数中)使用全局变量同名变量会直接引用全局,除非在局部重新声明了变量。这种处理符合动态静态类型和动态类型之间的划分,因为C的变量不声明就无法使用,自然的想法就是局部不声明使用全局,而php作为动态类型可以直接使用不声明的变量,局部直接使用局部变量。了解区别,然后开发过程中注意即可。

h. PHP哪些组成部分大小写敏感,此为一小坑。

  • 除了这个坑之外,php还会把同名的函数加载为construct,但在当前版本中会逐渐去掉,不要使用此种特性。言归正传,大小写敏感汇总:

    • 变量名区分大小写
    • 常量名区分大小写(通常为大写)
    • php.ini大小写敏感
    • 函数名、方法名、类名 不区分大小写(保证同样的编码风格,不要滥用)
    • 魔术常量不区分大小写(推荐大写)
    • NULL、TRUE、FALSE不区分大小写
    • 类型强制转换,不区分大小写

i. PHP提供了spl,为现代框架的编写提供了有力的武器,优于5.3之前简单的__autoload函数,可以保证多个不同的加载函数(维护了一个autoload函数的队列)。需要注意的有:

  • __autoload魔术方法全局只能存在一个,含有多个自动加载方法,可以多次调用spl_autoload_register(<func_name_string>),尝试加载顺序会按照本函数调用顺序为准,但spl_autoload_register还有2个可选参数,第一个true/false决定该自动加载逻辑失败时是否throw exception,第二个决定是把此次注册push到list的首还是尾,默认尾部。
  • 当__autoload和spl_autoload_register共存时,必须使用spl_autoload_register('__autoload')显示注册一下自动加载魔术方法,因为一旦调用spl,会将Zend Engine中的__autoload()函数取代为spl_autoload()或spl_autoload_call()。此处需要注意。
  • 简而言之,自动加载应当使用spl_autoload_register并且关注一下注册顺序即可。与composer配合真的是很好用,可以兼容各种不同的命名、寻址规则。当然出于规范的想法,PSR4才是王道,空闲时候可以思考一下,规范与自动化检测工具之间的边界。
  • ps:一个题外话,今天有个例子,项目域下的uri在框架层是做了大小写处理的,统一转成小写字母。但是一线同学不了解这部分工作,甚至以为是浏览器做的工作,这就与我们编写功能强大的底层框架的思路有所违背。当自动化检测工具做的很强大时,人就会变懒,变得不了解规范;但完全依赖规范明显无法提升效率。这个中间的度需要如何掌握?作为一线leader需要反复询问自身。

j. PHP中关于 $this、self、parent变量&关键字的解析。对于new self($val)与new static($val)之间的区别。

  • $this 指向当前实例 self指向类,通常用来调用静态属性、方法 parent顾名思义调用父类,不会混淆
  • new static (\(val) 返回当前类 new self(\)val) 就是当前代码段中定义的这个类

eg:

    <?php
	class A {
	    public static function getSelf() {
	        return new self();
	    }
	
	    public static function getStatic() {
	        return new static();
	    }
	}
	
	class B extends A {
	
	}
	
	var_dump(get_class(A::getSelf()));      //A
	var_dump(get_class(B::getSelf()));      //A
	
	var_dump(get_class(A::getStatic()));    //A
	var_dump(get_class(B::getStatic()));    //B
posted @ 2017-09-14 01:51  asfan  阅读(529)  评论(0编辑  收藏  举报