PHP - usort如何不修改数组中对象的内容?

7

我正在使用带有用户比较函数的usort来对对象数组进行排序。在对这些对象数组运行usort后,我发现一些对象的值随着它们在数组中的位置改变而改变。我错过了什么?我不认为我的用户比较函数中有任何副作用。usort是否以某种方式解构/重构对象?

以下是我正在使用的用户比较函数:

private function SortArrayOfSegments($segments){
    foreach($segments as $segment){
        echo '<pre>';
        var_dump($segment);  
    }
    usort($segments, "AirObject::CompareSegments");
    foreach($segments as $segment){
        var_dump($segment);
        echo '</pre>';
     }
    return $segments;
}

public static function CompareSegments($a, $b){
    $interval = date_diff(date_create($a->StartDateTime->GetString()),
                date_create($b->StartDateTime->GetString()));
    if($interval->invert == 1){
        return 1;   
    }else if($interval->y == 0 && $interval->m == 0  && $interval->d == 0 
    && $interval->i == 0 && $interval->s == 0 && $interval->h == 0){
        return 0;   
    }else if($interval->invert == 0){
        return -1;  
    }  
}

The objects I'm using look like this :

object(AirSegment)#14 (12) {
      ["StartDateTime"]=>
      object(VDateTime)#27 (4) {
        ["date"]=>
        string(10) "2010-12-07"
        ["time"]=>
        string(8) "09:23:21"
        ["timezone"]=>
        string(0) ""
        ["utc_offset"]=>
        string(0) ""
      }
      ["EndDateTime"]=>
      object(VDateTime)#23 (4) {
        ["date"]=>
        string(10) "2010-12-07"
        ["time"]=>
        string(8) "13:23:21"
        ["timezone"]=>
        string(0) ""
        ["utc_offset"]=>
        string(0) ""
      }
      ["start_airport_code"]=>
      string(3) "SFO"
      ["start_city_name"]=>
      string(13) "San Francisco"
      ["end_airport_code"]=>
      string(3) "STL"
      ["end_city_name"]=>
      string(8) "St Louis"
      ["operating_airline"]=>
      string(15) "United Airlines"
      ["operating_flight_number"]=>
      string(3) "335"
      ["duration"]=>
      float(NAN)
      ["required_properties":protected]=>
      array(9) {
        ["StartDateTime"]=>
        bool(false)
        ["EndDateTime"]=>
        bool(false)
        ["start_airport_code"]=>
        bool(false)
        ["end_airport_code"]=>
        bool(false)
        ["operating_airline"]=>
        bool(false)
        ["operating_flight_number"]=>
        bool(false)
        ["start_city_name"]=>
        bool(false)
        ["end_city_name"]=>
        bool(false)
        ["service_class"]=>
        bool(true)
      }
      ["service_class"]=>
      string(5) "Coach"
      ["trip_id"]=>
      string(4) "1621"
    }

正在变化的属性是持续时间属性。在运行usort之前,每个对象都有一个有效的浮点值。在usort之后,其中两个值为NaN。

解决方案:

date_diff具有副作用 - 至少在我的PHP版本中是这样。删除它完全可以解决这些问题。

public static function CompareSegments($a, $b){
    $adate = new DateTime($a->StartDateTime->GetString());
    $bdate = new DateTime($b->StartDateTime->GetString());
    $lessThan = $adate < $bdate;
    $equal = $adate == $bdate;
    $greaterThan = $adate > $bdate;

    if($lessThan){
        return -1;  
    }else if($equal){
        return 0;   
    }else{
        return 1;   
    }
}

1
如果您使用旧的对象回调语法:usort($segments, array('AirObject', 'CompareSegments');,我认为它仍然会出现损坏的情况? - Orbling
现在它可以工作了,我将其更改为那种语法...虽然很奇怪。如果我在SortArrayOfSegments函数内部使用var_dump,则会显示损坏,如果我在该函数调用之外使用var_dump,则一切都是格式良好的...奇怪。 - Macy Abbey
你使用的是哪个PHP版本? - ts.
5.3.0,这个版本或之前的版本是否已知存在漏洞?虽然它现在似乎可以工作,但我很沮丧,因为我无法找到一个解释为什么属性在函数内部是损坏的,但在返回时却不是。 - Macy Abbey
1
如果您在比较器中转储整个被排序的对象,那么您所看到的“损坏”可能实际上是正在处理未定义状态的中间过程。 - cabbey
我认为这可能是发生的事情,谢谢cabbey。 - Macy Abbey
2个回答

1

就我所知,我没有看到任何东西会修改数组元素本身的内容。

usort() 的工作方式是确定元素的新顺序,然后将新元素添加到保存原始值的数组中,然后删除原始元素,因此严格来说,您传递给函数的数组中的任何元素都不会出现在输出中,只有它们的副本(虽然作为同一数组变量的一部分)。

我不认为 usort 会像您问的那样破坏和重建对象,但它确实对数组本身做了类似的事情。据我所知,除非您在比较函数中明确地这样做,否则不应更改数组元素的值。

在您的 CompareSegments 方法中,您调用了一个 StartDateTime->GetString() 方法。可能是 GetString() 方法修改了您的数据吗?


很不幸,它没有副作用。 - Macy Abbey
尝试直接调用CompareSegments,在usort之外通过引用传递两个数组元素(就像它们在usort中一样),并检查它们是否也会被破坏。当然,确保使用至少一个在usort中被破坏的元素。 - AgentConundrum
没有副作用或损坏。 - Macy Abbey

0

在应用usort方法或任何方法之前,对象看起来是什么样子?如果不知道对象的原始状态,很难得出结论,但NaN是传递给usort的函数产生的错误结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接