PHP 编程中 10 个最常见的错误

  在foreach循环中,如果我们需要更改迭代的元素或是为了提高效率,运用引用是一个好办法: $arr = array(1,2,3,4);

  在foreach循环中,如果我们需要更改迭代的元素或是为了提高效率,运用引用是一个好办法:

  这里有个问题很多人会迷糊。循环结束后,$value并未销毁,$value其实是数组中最后一个元素的引用,这样在后续对$value的使用中,如果不知道这一点,会引发一些莫名奇妙的错误:)看看下面这段代码:

  我们来分析下。第一个循环过后,$value是数组中最后一个元素的引用。第二个循环开始:

  对于isset函数,变量不存在时会返回false,变量值为null时也会返回false。这种行为很容易把人弄迷糊。。。看下面的代码:

  写这段代码的人本意可能是如果$data[‘keyShouldBeSet’]未设置,则执行对应逻辑。但问题在于即使$data[‘keyShouldBeSet’]已设置,但设置的值为null,还是会执行对应的逻辑,这就不符合代码的本意了。

  如果上面代码的本意仅是检测$_POST[‘active’]是否为真,下面这样实现会更好:

  判断一个变量是否真正被设置(区分未设置和设置值为null),array_key_exists函数或许更好。重构上面的第一个例子,如下:

  另外,结合get_defined_vars函数,我们可以更加可靠的检测变量在当前作用域内是否被设置:

  问题出在哪呢?问题就在于上面的代码混淆了返回值和返回引用。在PHP中,除非你显示的指定返回引用,否则对于数组PHP是值返回,也就是数组的拷贝。因此上面代码对返回数组赋值,实际是对拷贝数组进行赋值,非原数组赋值。

  如果你就是想要改变原数组,也就是要反回数组引用,那应该如何处理呢?办法就是显示指定返回引用即可:

  如果你想的是会和上面一样输出“ Undefined index”错误,那你就错了。代码会正常输出“test”。原因在于PHP对于对象默认就是按引用返回的,而不是按值返回。

  综上所述,我们在使用函数返回值时,要弄清楚是值返回还是引用返回。PHP中对于对象,默认是引用返回,数组和内置基本类型默认均按值返回。这个要与其它语言区别开来(很多语言对于数组是引用传递)。

  像其它语言,比如Java或C#,利用getter或setter来访问或设置类属性是一种更好的方案,当然PHP默认不支持,需要自己实现:

  上面的代码给调用者可以访问或设置数组中的任意值而不用给与数组public访问权限。感觉怎么样:)

  当然上面的代码是没有什么错误的。问题在于我们在迭代过程中$valueRepository-findByValue可能每次都执行了sql查询:

  如果迭代了10000次,那么你就分别执行了10000次sql查询。如果这样的脚本在多线程程序中被调用,那很可能你的系统就挂了。。。

  在编写代码过程中,你应该要清楚什么时候应该执行sql查询,尽可能一次sql查询取出所有数据。

  有一种业务场景,你很可能会犯上述错误。假设一个表单提交了一系列值(假设为IDs),然后为了取出所有ID对应的数据,代码将遍历IDs,分别对每个ID执行sql查询,代码如下所示:

  一次sql查询获取多条记录比每次查询获取一条记录效率肯定要高,但如果你使用的是php中的MySQL扩展,那么一次获取多条记录就很可能会导致内存溢出。

  根据内存使用量来看,貌似一切正常。为了更加确定,试着一次获取100000条记录,结果程序得到如下输出:

  题出在php的mysql模块的工作方式,mysql模块实际上就是libmysqlclient的一个代理。在查询获取多条记录的同时,这些记录会直接

  保存在内存中。由于这块内存不属于php的内存模块所管理,所以我们调用memory_get_peak_usage函数所获得的值并非真实使用内存

  我们可以使用mysqlnd来代替mysql,mysqlnd编译为php自身扩展,其内存使用由php内存管理模块所控制。如果我们用mysqlnd来实现上面的代码,则会更加真实的反应内存使用情况:

  更加糟糕的是,根据php的官方文档,mysql扩展存储查询数据使用的内存是mysqlnd的两倍,因此原来的代码使用的内存是上面显示的两倍左右。

  联系上面提到的错误4可以看出,在实际的编码过程中,要做到一种平衡,才能既满足功能要求,又能保证性能。

  php编程中,在处理非ascii字符时,会遇到一些问题,要很小心的去对待,要不然就会错误遍地。举个简单的例子,strlen($name),如果$name包含非ascii字符,那结果就有些出乎意料。在此给出一些建议,尽量避免此类问题:

  如果你对unicode和utf-8不是很了解,那么你至少应该了解一些基础。推荐阅读这篇文章。

  最好使用mb_*函数来处理字符串,避免使用老的字符串处理函数。这里要确保PHP的“multibyte”扩展已开启。

  PHP中的$_POST并非总是包含表单POST提交过来的数据。假设我们通过jQuery.ajax方法向服务器发送了POST请求:

  为什么是这样的结果呢?我们的json数据 {a: ‘a’, b: ‘b’}哪去了呢?

  不错,上面的代码的确会输出’a’到’z’,但除此之外,还会输出’aa’到’yz’。我们来分析下为什么会是这样的结果。

  在PHP中不存在char数据类型,只有string类型。明白这点,那么对’z’进行递增操作,结果则为’aa’。对于字符串比较大小,学过C的应该都知道,’aa’是小于’z’的。这也就解释了为何会有上面的输出结果。

  虽说忽略编码标准不会导致错误或是bug,但遵循一定的编码标准还是很重要的。

  没有统一的编码标准会使你的项目出现很多问题。最明显的就是你的项目代码不具有一致性。更坏的地方在于,你的代码将更加难以调试、扩展和维护。这也就意味着你的团队效率会降低,包括做一些很多无意义的劳动。

  对于PHP开发者来说,是比较幸运的。因为有PHP编码标准推荐(PSR),由下面5个部分组成:

  PSR最初由PHP社区的几个大的团体所创建并遵循。Zend, Drupal, Symfony, Joomla及其它的平台都为此标准做过贡献并遵循这个标准。即使是PEAR,早些年也想让自己成为一个标准,但现在也加入了PSR阵营。

  某些情况下,使用什么编码标准是无关紧要的,只要你使用一种编码风格并一直坚持使用即可。但是遵循PSR标准不失为一个好办法,除非你有什么特殊的原因要

  自己弄一套。现在越来越多的项目都开始使用PSR,大部分的PHP开发者也在使用PSR,因此使用PSR会让新加入你团队的成员更快的熟悉项目,写代码时

  一些PHP开发人员喜欢用empty函数去对变量或表达式做布尔判断,但在某些情况下会让人很困惑。

  首先我们来看看PHP中的数组Array和数组对象ArrayObject。看上去好像没什么区别,都是一样的。真的这样吗?

  current方法返回数据集时就是这么干的。开发人员很容易就会踩到这个坑。

  在这顺便提一下,因为PHP中会将数值0认为是布尔值false,因此 count函数可以直接用在 if条件语句的条件判断中来判断数组是否为空。另外,count函数对于数组来说复杂度为O(1),因此用 count函数是一个明智的选择。

  再来看一个用 empty函数很危险的例子。当在魔术方法 __get中结合使用 empty函数时,也是很危险的。我们来定义两个类,每个类都有一个 test属性。

  然后我们定义 Magic类,并用 __get魔术方法来访问它的 test属性:

  很不幸的是,如果一个类使用魔法 __get函数来访问类属性的值,没有简单的方法来检查属性值是否为空或是不存在。在类作用域外,你只能检查是否返回 null值,但这并不一定意味着没有设置相应的键,因为键值可以被设置为 null 。

  相比之下,如果我们访问 Regular类的一个不存在的属性,则会得到一个类似下面的Notice消息:

  因此,对于 empty函数,我们要小心的使用,要不然的话就会结果出乎意料,甚至潜在的误导你。

相关阅读