就当前而言,PHP仍旧是网站培植的主流编程措辞之一。
一方面,是得益于它自身的大略性,随意马虎学习且快速上手;另一方面,得益于开源社区贡献的各种精良框架、类库和项目。
这些源代码下载到做事器后,大略配置一下,乃至都不须要二次开拓就能直策应用,非常方便。

但须要把稳的是,别人供应、贡献的开源项目是可以减少我们重复开拓的本钱,并不虞味着我们对原生态的PHP就可以置之不理。
恰好相反,更深入地理解PHP原生态的用法,将能帮助我们从底层、从根本上更透彻地理解和节制别人封装的类、函数、模块和扩展。
也便是说,除了会利用,还不敷矣。
作为专业的开拓人士,我们还应明白为什么会这样,洞明背后奇妙的差异,对中大型项目开拓尤其关键。
关于这部分,后面会逐步说到。

PHP开拓,入门很大略,但要深入和精通很难,须要付出一定的韶光以及精力。
在这一章节,肯定不能罗列PHP这门措辞全部的要点,只是通过分享一些常见的或者有代表性的知识点,希望能引起广大开拓同学对原生态的PHP有更多的关注,加深理解。
做到:不误解,不误用,更不误导。

php非空PHP高等编程回归原生态空与非空 Ruby

4.1 空与非空

很多时候,对付明显的低级PHP语法,我们一眼就能识别。
假设轻微转换一下,这时就须要花点心思才能看破个中的奥妙。
最困难的莫过于,奇妙的用法一旦与繁杂的业务代码、规则逻辑混在一起,散落在上千行代码内时,想要在短韶光内创造问题所在则是个巨大的寻衅。

4.1.1 大略的判空

大家利用最多的PHP函数之一,大概是empty()这个函数了。
而且,大家都知道,什么样的情形下,一个值会判断为空。
摘自官方文档的解释,以下的东西被认为是空的:

"" (空字符串)0 (作为整数的0)0.0 (作为浮点数的0)"0" (作为字符串的0)NULLFALSEarray() (一个空数组)$var; (一个声明了,但是没有值的变量)

非常随意马虎看出,以下代码输出的结果为true。

<?php$var = 0;var_dump(empty($var)); // 结果输出为true

4.1.2 隐晦的判空

但是,结合其他函数一起利用时,并且不再是直策应用empty()函数来判断时,情形就开始变得晦涩了。
一起来看下以下这段有问题的代码。
看你须要多少韶光才能创造里面的BUG?

<?php// 文章内容$text = '软件开拓是根据用户哀求建造出软件系统或者系统中的软件部分的过程。
……'; // 用户输入的关键字$keyword = ''; $pos = strpos($text, $keyword);if ($pos) { // 找到了,是我感兴趣的文章} else { // 未能找到关键字}

这里的场景是,根据用户输入的关键字,匹配某篇文章是不是读者感兴趣的。
如果文章包含关键字就视为是用户感兴趣的,否则便是不感兴趣的。
正常情形下这段代码是可以正常事情的,问题在于,如果恰巧用户输入的关键字刚好涌如今文章开头时,就会发生意想不到的事情。
例如,用户输入关键字“软件”。
由于“软件”这个词涌如今最前面,以是查找到的位置$pos值为0。
末了在判断位置时,0被当作FALSE,即:$pos = !empty($pos) = !empty(0) = !TRUE = FALSE。
实际是找到匹配值,却被误判断为未找到。
由此就涌现了一个BUG,引发了一个故障。

4.1.3 两个引申

说到这里,我们可以引申出两个故意义的谈论。
第一是关于PHP全等判断,第二是关于PHP函数的缺点返回。

PHP全等判断

这点很好理解,从我们开始打仗PHP编程时,就已经知道这一点了。
全等判断是指不仅哀求值相等,而且还哀求类型也一样。
即在不进行隐式类型转换的情形下,待比较的这两个值是否仍旧相等。
普通的相等,PHP默认会进行隐式的转换,利用两个等号 == 表示。
全等判断则用三个等号 === 表示。

针对前面刚才的问题,可以利用全等来改动对是否找到这一逻辑的判断。
即:

if ($pos !== FALSE) { // 找到了,是我感兴趣的文章} else { // 未能找到关键字}

这一点是大家都知道的,但下面这一点大概大家都知道,但随意马虎忽略。

PHP函数的缺点返回

PHP底层的代码,可以分为两大类。
一类是早期面向过程范式的函数,例如:strpos()、json_decode()、curl_exec()、file_get_contents()等函数。
另一类是后期面向工具范式的类与工具,例如:PDO、Memcached、SoapClient等。
对付前者,当失落败时,例如字符串查找不到、JSON解码失落败、URL抓取超时或者文件不存在时,所调用的函数会返回布尔值FALSE表示失落败。
而对付后者,即若利用的是封装的类,并通过类实例化的工具来操作时,当发生失落败或者非常情形时,则会直接抛出非常。
例如数据库连接失落败、SOAP调用失落败。

这可以说是PHP措辞的老例。
当调用函数并失落败时,返回FALSE,这里须要做好全等判断,避免与正常情形下的数字0、"" (空字符串)或"0" (作为字符串的0)稠浊。
前面提到的关键字位置查找便是个中一例,又如文件内容的读取,若文件存在但读取的内容为空字符串,与文件不存在读取到的结果为FALSE,这两者之间是有着奇妙的差异的。
在进行项目开拓时,一定要把稳严格区分,否则就会失落之毫厘,谬以千里。

此外,如果是函数失落败了,可以通过供应的缺点码查找到对应的缺点信息。
例如,利用curl失落败时,可以利用curl_error()函数查看缺点信息。
例如官网供应的示例:

<?php// 创建 cURL 句柄,指向一个不存在的位置$ch = curl_init('http://404.php.net/');curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);// 笔者注,利用全等判断if(curl_exec($ch) === false){ // 笔者注,获取缺点信息 echo 'Curl error: ' . curl_error($ch);}

4.1.4 为什么系统崩溃了?

再来看一个更为繁芜的真实案例场景。
在一个中型社交游戏系统中,有一个业务场景是须要获取每个用户的道具数量。
干系代码片段如下:

<?php// 用户名$user = 'dogstar';// 缓存KEY$key = 'item_' . $user;// 缓存读取$cache = new Cache\Memcached();$num = $cache->get($key);if ($num <= 0) { // 查询数据库:SELECT COUNT(id) FROM user_items WHERE username = 'dogstar' $model = new Model\Item(); $num = $model->getUserItemTotalNum($user); $cache->set($key, $num, 600);}//更多业务处理……

在上面代码中, 对付用户名为dogstar的游戏玩家,先会从Memcache缓存中获取他的道具总数,如果之前没有缓存,则再从数据库查询该用户的道具总数。
末了写入到缓存,避免下一次重复穿透读取数据库,从而减轻数据库的访问压力。

但该功能上线不久后,创造全体游戏系统就崩溃了,经排查创造是数据库负载过高导致系统无法正常相应。
原来,有很多玩家用户是没有任何道具的,即他们的道具数量为0。
当没有缓存时,通过Memcached读取出来的值是FALSE,经隐式转换后也是0。
此时,就无法区分是用户真的没有道具,还是由于没有缓存须要直接查询数据库。
终极导致了在高并发的情形下,频繁穿透到数据库,进行聚合的查询,拖垮了数据库做事器。

这时的情形,会比以往都要严厉。
首先,这里不仅有前面谈论的空判断,还引入了Memcached缓存和数据库查询。
其次,这是确切发生在线上环境的故障问题,每一秒都在影响游戏玩家的用户体验,每一分钟都对我们的产品造成了丢失,须要在面临重大压力、在最短的韶光内找出缘故原由并修复上线。
末了,上面的代码片段是经由简化提炼的代码,实际情形上,代码可能遍布在你的项目里,更为要命的是,你的项目拥有10万行以上的代码!

对付这种情形,须要提前意识到会发生若何的状况。
改进的办法很大略,一种是沿用前面的全等判断;另一种便是,不要在缓存结果中只保存基本类型的数据,而是保存一个构造体,即保存一个数组到缓存中。
如:

if (empty($data)) { // 查询数据库:SELECT COUNT(id) FROM user_items WHERE username = 'dogstar' $model = new Model\Item(); $num = $model->getUserItemTotalNum($user); // 改进方案:保存一个数组到缓存 $data = array('num' => $num); $cache->set($key, $data, 600);}$num = $data['num'];

4.1.5 小结

作为三大程序掌握构造之一,选择掌握构造是我们平时项目开拓过程中打仗最多的。
这里的条件逻辑判断,又会涉及对空和非空的判断。
如果做出准确无误的判断,就哀求我们对全等判断、各函数失落败时的返回值、隐式类型转换、空值的判断等都要有一个全面的认识和清晰的理解。
这样才不会误解、误用、误判。

关于空值判断,暂且谈论到这里。
下面我们再来看下另一个在PHP开拓中常常用到的数组。