课程咨询 :13623629309

太原PHP培训 > 达内新闻 > PHP编程中10个最常见的错误(上)
  • PHP编程中10个最常见的错误(上)

    发布:陈鹏个人博客      来源:陈鹏个人博客      时间:2016-10-26

  • PHP编程中10个最常见的错误(上)

    在编程的过程中有错误是在所难免的,我们应该尽量避免他们。太原php培训班带大家一起跳过。

    PHP是一种非常流行的开源服务器端脚本语言,你在万维网看到的大多数网站都是使用php开发的。本篇经将为大家介绍PHP开发中10个最常见的问题,希望能够对朋友有所帮助。

    错误1foreach循环后留下悬挂指针

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

    $arr = array (1, 2, 3, 4);

    foreach ( $arr as & $value ) {

    $value = $value * 2;

    }

    // $arr is now array(2, 4, 6, 8)

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

    $array = [1, 2, 3];

    echo implode( ',' , $array ), "\n" ;

    foreach ( $array as & $value ) {} // by reference

    echo implode( ',' , $array ), "\n" ;

    foreach ( $array as $value ) {} // by value (i.e., copy)

    echo implode( ',' , $array ), "\n" ;

    上面代码的运行结果如下:

    1,2,3

    1,2,3

    1,2,2

    你猜对了吗?为什么是这个结果呢?

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

    第一步:复制$arr[0]$value(注意此时$value$arr[2]的引用),这时数组变成[1,2,1]

    第二步:复制$arr[1]$value,这时数组变成[1,2,2]

    第三步:复制$arr[2]$value,这时数组变成[1,2,2]

    综上,最终结果就是1,2,2

    避免这种错误最好的办法就是在循环后立即用unset函数销毁变量:

    $arr = array (1, 2, 3, 4);

    foreach ( $arr as & $value ) {

    $value = $value * 2;

    }

    unset( $value ); // $value no longer references $arr[3]

    错误2:对isset()函数行为的错误理解

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

    $data = fetchRecordFromStorage( $storage , $identifier );

    if (!isset( $data [ 'keyShouldBeSet' ]) {

    // do something here if 'keyShouldBeSet' is not set

    }

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

    下面是另外一个例子:

    if ( $_POST [ 'active' ]) {

    $postData = extractSomething( $_POST );

    }

    // ...

    if (!isset( $postData )) {

    echo 'post not active' ;

    }

    上面的代码假设$_POST[‘active’]为真,那么$postData应该被设置,因此isset($postData)会返回true。反之,上 面代码假设isset($postData)返回false的唯一途径就是$_POST[‘active’]也返回false

    真是这样吗?当然不是!

    即使$_POST[‘active’]返回true$postData也有可能被设置为null,这时isset($postData)就会返回false。这就不符合代码的本意了。

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

    if ( $_POST [ 'active' ]) {

    $postData = extractSomething( $_POST );

    }

    // ...

    if ( $_POST [ 'active' ]) {

    echo 'post not active' ;

    }

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

    $data = fetchRecordFromStorage( $storage , $identifier );

    if (! array_key_exists ( 'keyShouldBeSet' , $data )) {

    // do this if 'keyShouldBeSet' isn't set

    }

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

    if ( array_key_exists ( 'varShouldBeSet' , get_defined_vars())) {

    // variable $varShouldBeSet exists in current scope

    }

    错误3:混淆返回值和返回引用

    考虑下面的代码:

    class Config

    {

    private $values = [];

    public function getValues() {

    return $this ->values;

    }

    }

    $config = new Config();

    $config ->getValues()[ 'test' ] = 'test' ;

    echo $config ->getValues()[ 'test' ];

    运行上面的代码,将会输出下面的内容:

    PHP Notice: Undefined index: test in /path/to/my/script.php on line 21

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

    // getValues() returns a COPY of the $values array, so this adds a 'test' element

    // to a COPY of the $values array, but not to the $values array itself.

    $config ->getValues()[ 'test' ] = 'test' ;

    // getValues() again returns ANOTHER COPY of the $values array, and THIS copy doesn't

    // contain a 'test' element (which is why we get the "undefined index" message).

    echo $config ->getValues()[ 'test' ];

    下面是一种可能的解决办法,输出拷贝的数组,而不是原数组:

    $vals = $config ->getValues();

    $vals [ 'test' ] = 'test' ;

    echo $vals [ 'test' ];

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

    class Config

    {

    private $values = [];

    // return a REFERENCE to the actual $values array

    public function &getValues() {

    return $this ->values;

    }

    }

    $config = new Config();

    $config ->getValues()[ 'test' ] = 'test' ;

    echo $config ->getValues()[ 'test' ];

    经过改造后,上面代码将会像你期望那样会输出test

    我们再来看一个例子会让你更迷糊的例子:

    class Config

    {

    private $values ;

    // using ArrayObject rather than array

    public function __construct() {

    $this ->values = new ArrayObject();

    }

    public function getValues() {

    return $this ->values;

    }

    }

    $config = new Config();

    $config ->getValues()[ 'test' ] = 'test' ;

    echo $config ->getValues()[ 'test' ];

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

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

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

    class Config

    {

    private $values = [];

    public function setValue( $key , $value ) {

    $this ->values[ $key ] = $value ;

    }

    public function getValue( $key ) {

    return $this ->values[ $key ];

    }

    }

    $config = new Config();

    $config ->setValue( 'testKey' , 'testValue' );

    echo $config ->getValue( 'testKey' ); // echos 'testValue'

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

    错误4:在循环中执行sql查询

    PHP编程中发现类似下面的代码并不少见:

    $models = [];

    foreach ( $inputValues as $inputValue ) {

    $models [] = $valueRepository ->findByValue( $inputValue );

    }

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

    1

    $result = $connection ->query( "SELECT `x`,`y` FROM `values` WHERE `value`=" . $inputValue );

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

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

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

    $data = [];

    foreach ( $ids as $id ) {

    $result = $connection ->query( "SELECT `x`, `y` FROM `values` WHERE `id` = " . $id );

    $data [] = $result ->fetch_row();

    }

    但同样的目的可以在一个sql中更加高效的完成,代码如下:

    $data = [];

    if ( count ( $ids )) {

    $result = $connection ->query( "SELECT `x`, `y` FROM `values` WHERE `id` IN (" . implode( ',' , $ids ));

    while ( $row = $result ->fetch_row()) {

    $data [] = $row ;

    }

    }

    好了,今天就给大家讲这么多吧,喜欢我的内容可以关注或者分享(微信公众平台:tytedu)选择太原达内培训,不再孤军奋战,轻轻松松做IT高薪白领。太原达内培训带领有明确目标的学子迈向成功之路!

上一篇:Php session内部执行流程的再次剖析

下一篇:PHP编程中10个最常见的错误(下)

最新开班日期  |  更多

php高级开发名企定制班(剩2个名额)

php高级开发名企定制班(剩2个名额)

开班日期:12-29

php高级开发周末班(剩5个名额)

php高级开发周末班(剩5个名额)

开班日期:12-29

php高级开发免费试听(剩5个名额)

php高级开发免费试听(剩5个名额)

开班日期:12-29

更多高级开发工程师精品班

更多高级开发工程师精品班

开班日期:12-29

  • 地址:山西省太原市小店区学府街长治路高新国际A座24层
  • 课程培训电话:13623629309     全国服务监督电话:400-827-0010
  • 服务邮箱 ts@tedu.cn
  • 2001-2016 达内国际公司(TARENA INTERNATIONAL,INC.) 版权所有 京ICP证08000853号-56