特性介绍

最近看了一遍ModernPHP,有不少收获,特此简单总结一番,记录下读后感受。


命名空间

刚开始学习开发PHP时,基于ThinkPHP3.1版本实践,并没应用到命名空间这个玩意儿,在偶尔看博客的时候会有提到,但是觉得好像也没多大意义。这里我的感受是,基于框架写项目能提高开发效率,同时能帮助解决很多系统运行问题,比如记录系统分析日志、渲染模版文件、实现ORM映射、路由等等。但是不可过度依赖框架,一想到数据库连接,第一反应是M();一想到页面展示,就又冒出display()。那么脱离框架本身,我们好像就变得无所适从。

不过依然是在偶然间接触到了ThinkPHP5,发现命名空间变得无处不在了。那么它到底有什么用呢?一番简单研究后,发现这就类似于Java语言的package。是为了划分项目的目录结构,即时存在相同的类名,只要拥有各自的命名空间,即可互不干扰的被使用。
在学习过composer的自动加载过程后,我觉得可以描述过程:在new新对象时,触发到我们自定义的__autoload函数,携带类名参数(包含命名空间),接着根据命令空间去映射到一个实际的文件路径。所以定义在前,加载在后,我们在项目中要做的,就把实例然后使用。


接口

在看到这章的时候,我就在想,到底如何更好地使用PHP接口呢?这个问题其实困扰了我好久。先说下我初步了解到的Java,比如Dao、Service,都是先定义一个interface,继而按照其定义规则实现业务。而在如今,我不知道是不是受限于框架、还是受限于业务,接口给我一种可有可无的作用。抽象类我还偶尔有用,为了对一些方法做封装,也不想这个类被实例,觉得还是有点好处。那么对于接口,之前在实现一个项目时,我预定义了一个接口,原意是希望后面的逻辑类都基于此结构,实现一些必要的准则,但是慢慢发现,同时准则是实现了,却都没有去继承。其实这时候我就觉得,或者真的是业务限制了使用,也或者是因为PHP是动态类型语言,在传参时不用定义类型。目前我暂没能抓住面向接口编程的精髓。讲了有力使不上的情况,再来聊聊好用的玩意儿。Monolog日志库内存在许多的handler,虽然handler实现出来的效果有很大不同,有的输出到文本文件、有的输出到邮箱、有的输出到syslog。它们都是通过write()写入,close()写出,记录方法皆为标准的warn、debug等等,如此使用接口就像是理所当然的事情了。

顺便一提,在看到迭代器的时候,发现要实现foreach遍历,得基于Iterator接口。不继承居然实现不了迭代功能。
http://www.laruence.com/2008/10/31/574.html
看了鸟哥文章后,才认识到原因。若类继承了Iterator接口,这个类对象就是ZEND_ITER_OBJECT,否则就是ZEND_ITER_PLAIN_OBJECT。PHP的foreach处理时,会判别类对象,决定是通过相关函数来遍历还是以数组的方式。说真的,这个点讲出来了觉得很正常,刚知晓时简直觉得太神奇了。所以划重点,看底层源码实现是非常有必要的,可惜现在水平还达不到看懂的层次。

所以PHP内置接口,在编译时会根据类型的不同实现不同的处理逻辑!
后续补充,看到PHP核心技术与最佳实践这本书,有一章讲述面向接口编程,看了后希望会有新的体会。


性状

trait,书上的中文翻译写的性状,我怎么感觉名字好土。基于PHP单继承的前提,trait能够更好的封装公用方法、变量,能够更加解耦的将不同业务代码融合在一起。这是PHP5.4.0引入的概念,继续顺便一提,PHP的一些特性、一些用法、一些函数,一定要对版本要求有个印象,别因为测试环境的版本问题,导致发布到生产到出bug。

说起代码封装,有一个DRY原则,该原则鼓励对代码进行抽象,实现不重复的目的;我也听说一句话,存在重复的地方就有优化。
但是凡事不可一概而论,过度封装是否一定完美呢?我个人觉得有水平的封装叫封装,没水平那是提高代码阅读难度。所以开发一定要基于规范,多读优秀源码,提高自己的编码和代码整合能力。


生成器

yield是PHP5.5.0引入的功能,可以不占用大量内存资源,对一些数据或操作进行逐步处理。深层次的我还不懂,基于yield引入了协程概念,这个非常精髓,也很重要,后续继续深入学习。


闭包|匿名函数

可以封装一个没有名称的函数,就不需要每次不停的创建方法了。结合自己的使用聊聊,一段需要被重用的代码,如果在类中重新封装成一个新方法后发现,只在一个方法内多次调用,长此以往,只被一个方法依赖的代码段会越来越多。此时不妨使用闭包,甚至能够被当成参数进行传递。Composer的autoload.php内有一段有意思的代码,通过Closure::bind()方法实现对对象私有属性的赋值。真的简单又很好用。


Zend OPcache

从PHP5.5.0开始,PHP内置了字节码缓存功能,名字就叫Zend OPcache。因为PHP是解释型语言,PHP解释器执行PHP脚本时将代码编译成Zend操作码,最后执行字节码。每次请求都重复以上过程,这和Java等编译语言不同。所以如果能够缓存字节码,节省编译Zend操作码的时间,那么会提高不少效果。这个我没用到,就不聊实际经验了。


内置的HTTP服务器

PHP在编译安装后,可以通过CLI的方式访问,比如运行php index.php,就可以执行PHP脚本。比如未安装nginx或apache等web服务器,启动内置的HTTP服务器后,就可以通过HTTP协议访问。不过这种方式效率和管理上没这么好,所以一般都用apache的mod_php模块或php-fpm对cgi进程进行统一管理。


PSR规范

这一章的主题是标准。非PHP官方出品的行为规范,
PSR-1:基本的代码风格
PSR-2:严厉的代码风格
PSR-3:日志记录器接口
PSR-4:自动加载
具体内容不作说明,若依赖与某个PHP框架开发,其有自定义实现的思想和生态系统,不能与其他PHP框架共享代码,那么我们就容易被束缚在这个框架的生态系统中,也就是说选择的框架限制了创造力、想象力和自由度。
为了让不同的代码,易于阅读、易于使用,这就必须制定标准规范,然而这个标准并非强调,这和PHP的开发实现并没有根本上的影响。基于PSR-1、PSR-2规范,从代码编程的风格上作出改变;通过PSR-3日志规范,让日志更加的自由,不依赖与某个特定库;PSR-4规范的精髓是把命名空间的前缀和文件系统中的目录对应起来,实现自动加载文件。


组件

首先讲讲laravel框架,错误信息的展示使用了Whoops库、日志系统使用了Monolog库等,这两个库使用composer可在任意项目内随意使用。那么为什么要使用组件?书中一段话解释的很好,既然已有各式各样功能齐全的组件,我们为什么还要浪费时间自己去编写呢?

我们不妨思考框架给我们开发过程带来了什么?以TP5粗略为例,
1,重实现了自动加载功能,实际上这个功能composer已经做得足够好
2,系统运行日志、缓存
3,请求路由
4,ORM
5,页面模版渲染
因此,如果想实现简单功能的框架,应该没这么难,做后前端控制器(统一请求入口文件);做好请求数据过滤;实现控制器路由;对系统异常和系统错误进行捕捉;封装数据库操作,若想更方便执行原生sql也成;至于模版,如果仅仅是想要走API请求,可以不添加这个功能。

而且,ORM、路由、错误捕捉、模版,在packagist内可以找到很多成熟组件,不需要我们自己去实现。这给我们从刚开始就基于框架开发的人里说,提供了一个新的思路,在分析业务需求后,可以选择框架、也可以舍弃,使用各个功能的组件来实现更加轻量的代码。


过滤、验证和转义

很早看到过一句话:不要相信任何来自不受自己直接控制的数据源中的数据。
一直谨记,获益匪浅。我们并不知道数据会不会非法截断篡改,也并不知道对接方会发送给我们什么奇葩数据。
往往的经验是,写JS或APP与用户直接挂钩的数据处理时,必须加上限制。和认识的同事对接接口,或与前端通信,有时候可能就觉得对数据的检测没这么重要。实际上我觉得并不是这样,记不清多少次,我提供的文档已明确说明字段信息,有时候字段名错了,甚至传多传少了;用户数据一定为四位数字,而直接传来了四位数字英文组别,前端的理解为后端会检测的……。我们并不能知道他人会不会犯错,所以自己实现对数据的过滤、验证,这会让我们避免不少麻烦,让我们的程序能够更加健壮。


密码

用户密码对我们来说是重中之重的数据,更为认真的考虑,用户的密码,我们也应当无法从数据库中直接解除。
MD5加密是一种目前我了解到使用较多的方式,现在这种方式已不是特别合适,在数据量级如此激增的当下,因为md5对字符串重复处理密文是一致的,会导致越来越多结果被冲撞解出。
最让我震惊的,居然还真的有存在明文密码直接存储数据库的事情。
目前最好的方式自然是使用bcrypt计算用户密码的哈希值了,无法通过密文反推,只能通过明文密码进行验证,因为在计算过程中会自动加盐且会进行不断计算,导致明文重复加密会得到不同的密文,很确定保证密文密码的安全性。
password_hash()
password_verify()
PHP5.5.0以上的版本两个函数即可实现加密与验证,高效又不失简洁。


日期、时间和时区

推荐使用DateTime、DateInterval和DateTimeZone类,来处理日期、时间、时区相关的逻辑。比较之前都使用time()和strtotime()函数处理,推荐类能够对不同的时间格式类型、和日期加减有更巧妙的处理,也许之后慢慢熟悉使用不失为一种选择。
数据库
讲述了PDO的使用。内容不详细说明,聊聊我的感受。一直以来用框架开发时,都使用封装的一些方法,没有接触到与Mysql通信的具体实现,内部实现通信逻辑,还是要了解一下的。
多字节字符串
总结:养成使用mbstring拓展替换默认函数的习惯。因为不同编码方式有的字符占用字节会不相同,比如UTF-8的一个中文占用3个字节,如果使用strlen()函数处理,一个中文会得出长度为3的结果,此时应该使用mb_strlen()函数,能够针对真实字符长度正确处理。

流在我们日常开发中经常会使用,不过对于概念可能并不是这么了解。比如file_get_contents()函数,有时候读取本地文本文件时会使用;有时候以get方式请求接口获取结果也会使用;有时候获取POST发送的json数据也会使用。其实这三个过程中,都存在流概念。

  1. 读取文本文件 file_get_contents(‘1.txt’);默认使用File流方式,所以文件流协议可省略。
  2. get请求 file_get_contents(‘http://xxxx’);使用HTTP协议,进行流的读取。
  3. json数据获取 file_get_contents(‘php://input’);读取input流数据。

所以我们在使用fopen()、file_get_contents()、fclose()等函数时,不能等同于文件操作函数,而是流操作。文件,只是其中实现的一种协议。
对流,我们可以配置上下文(配置项内容),设置流过滤器(对数据进行一层层过滤处理)。
比如要读取一个压缩后的文件,若要常规处理,只能一个个解压后再处理,而如果考虑流过滤器,在代码中添加过滤器,功能是提供解压缩,那么我们就可以按照正常的文本来读取内容。


错误和异常

错误和异常的处理,就不能忽略set_error_handler()、set_exception_handler()、register_shutdown_function()三个函数,这里不重复说明了。
Whoops是一个处理错误机制的现代化组件,很值得一用。


PHP-FPM

讲述了一些配置项的使用,比如pm系列、慢执行日志等。


最长执行时间

PHP脚本默认最长执行时间是30秒,正常来说肯定足够了,但是一些特殊的任务执行完成,要需要远远大于30秒。一般都是使用set_time_limit()来设置,若参数填入0,表示脚本执行无上限。但是如果用户请求,存在某段逻辑要执行很久要怎么办?这个有各种解决方法,书里提供了用exec()函数调用bash的at命令来重新触发脚本的思路。


处理会话

会话即为Session,这样基本上都明白了。默认情况下,会话数据存储方式为文件,Linux会将文件存储到/tmp目录。在一次开发中,就发现了问题,项目做了两台服务器的负载,结果Session的获取不顺利,毕竟两台服务器的文件并不共享。解决并不麻烦,一个是将文件置于NFS下;一个是若使用了nginx做负载,添加hash_ip配置将相同IP的请求仅发送到一台服务器;一个是使用Memcache或Redis来存储会话数据。


缓冲输出

PHP的缓冲内容此篇不作说明。


测试

为什么要测试?测试的目的在于能够确保PHP应用始终能按照我们预期的方式运行。测试也许是我们开发过程的重要一步,基本上都是忽视了,其必须性还是的在使用后才可能有更为准确的了解,所以先去用下PHPUnit试试。


Xdebug

刚开始接触到Xdebug是作为断点调试使用的,这个功能对我阅读源码的学习帮助非常大,能够了解到代码实际运行的逻辑过程。甚至能与服务器建立联系,进行远程断点调试,没想到有个更为厉害的功能,它是一个分析器。使用它分析应用的调用堆栈,能轻易找出瓶颈和性能问题。


分析器

分析器能分析应用的性能,调试性能问题,有很实用的意义,值得我们每一个人掌握。


总结

说点其他的,重新看起了纸质书后发现真的很有感觉,比看电子版要来的有意思的多,可以随意在写写画画自己的思路和随笔,可能也更加让自己坚持着能看完吧。

现在正在看[PHP核心技术与最佳实践]+[Redis实战]~

10-03 13:33