Làm thế nào để truy cập các thuộc tính đối tượng có tên như số nguyên?


87

Tôi đang sử dụng json_decode()một cái gì đó như:

$myVar = json_decode($data)

Điều này cho tôi kết quả như thế này:

[highlighting] => stdClass Object
        (
            [448364] => stdClass Object
                (
                    [Data] => Array
                        (
                            [0] => Tax amount liability is ....... 

Tôi muốn truy cập giá trị chuỗi trong khóa [0]. Khi tôi cố gắng làm điều gì đó như:

print $myVar->highlighting->448364->Data->0;

Tôi gặp lỗi này:

Lỗi phân tích cú pháp: lỗi cú pháp, T_DNUMBER không mong muốn

Hai chữ số / số nguyên ở đó dường như có vấn đề.



1
@FelixKling: Tôi cũng đã viết CV, nhưng hóa ra nó không phải là dupe: nó tạo ra sự khác biệt nếu tên thuộc tính bắt đầu bằng một số hoặc là tất cả các số !
Jon

@Jon: Mmmh, thú vị ... lẽ ra tôi phải làm một bài kiểm tra trước khi tôi đoán. Cảm ơn vì đã cho tôi biết!
Felix Kling

Câu trả lời:


286

Cập nhật cho PHP 7.2

PHP 7.2 đã giới thiệu một thay đổi hành vi để chuyển đổi các khóa số trong các phôi đối tượng và mảng , giúp khắc phục sự không nhất quán cụ thể này và làm cho tất cả các ví dụ sau hoạt động như mong đợi.

Một điều ít hơn để được nhầm lẫn về!


Câu trả lời gốc (áp dụng cho các phiên bản trước 7.2.0)

PHP có những con hẻm tối tăm mà bạn thực sự không muốn tìm thấy mình bên trong. Thuộc tính đối tượng có tên là số là một trong số chúng ...

Điều họ chưa bao giờ nói với bạn

Sự thật số 1: Bạn không thể dễ dàng truy cập các thuộc tính có tên không phải là tên biến hợp pháp

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->123foo; // error

Sự thật # 2: Bạn có thể truy cập các thuộc tính như vậy bằng cú pháp dấu ngoặc nhọn

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->{'123foo'}; // OK!

Sự thật thứ 3: Nhưng không phải nếu tên tài sản là tất cả các chữ số!

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->{'123foo'}; // OK!
echo $o->{'123'}; // error!

Ví dụ trực tiếp .

Sự thật thứ 4: Chà, trừ khi đối tượng không đến từ một mảng ngay từ đầu.

$a = array('123' => '123');
$o1 = (object)$a;
$o2 = new stdClass;
$o2->{'123'} = '123'; // setting property is OK

echo $o1->{'123'}; // error!
echo $o2->{'123'}; // works... WTF?

Ví dụ trực tiếp .

Khá trực quan, bạn có đồng ý không?

Bạn có thể làm gì

Tùy chọn số 1: làm thủ công

Cách tiếp cận thực tế nhất chỉ đơn giản là truyền đối tượng bạn quan tâm trở lại vào một mảng, điều này sẽ cho phép bạn truy cập các thuộc tính:

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
$a = (array)$o;
echo $o->{'123'}; // error!
echo $a['123']; // OK!

Thật không may, điều này không hoạt động một cách đệ quy. Vì vậy, trong trường hợp của bạn, bạn cần phải làm điều gì đó như:

$highlighting = (array)$myVar->highlighting;
$data = (array)$highlighting['448364']->Data;
$value = $data['0']; // at last!

Tùy chọn số 2: tùy chọn hạt nhân

Một cách tiếp cận thay thế sẽ là viết một hàm chuyển đổi các đối tượng thành mảng một cách đệ quy:

function recursive_cast_to_array($o) {
    $a = (array)$o;
    foreach ($a as &$value) {
        if (is_object($value)) {
            $value = recursive_cast_to_array($value);
        }
    }

    return $a;
}

$arr = recursive_cast_to_array($myVar);
$value = $arr['highlighting']['448364']['Data']['0'];

Tuy nhiên, tôi không tin rằng đây là một lựa chọn tốt hơn trên toàn diện bởi vì nó sẽ không cần thiết truyền sang mảng tất cả các thuộc tính mà bạn không quan tâm cũng như những thuộc tính bạn đang có.

Tùy chọn # 3: Chơi thông minh

Một lựa chọn thay thế cho tùy chọn trước đó là sử dụng các hàm JSON được tích hợp sẵn:

$arr = json_decode(json_encode($myVar), true);
$value = $arr['highlighting']['448364']['Data']['0'];

Các hàm JSON thực hiện một cách hữu ích việc chuyển đổi đệ quy sang mảng mà không cần xác định bất kỳ hàm bên ngoài nào. Tuy nhiên, điều này trông mong muốn, nó có nhược điểm "nuke" của tùy chọn số 2 và thêm vào đó là nhược điểm là nếu có bất kỳ chuỗi nào bên trong đối tượng của bạn, các chuỗi đó phải được mã hóa bằng UTF-8 (đây là một yêu cầu của json_encode).


Nó cũng xảy ra để giải quyết vấn đề của tôi! stackoverflow.com/questions/4643894/…
Bossliaw 10/12/12

Jon, cảm ơn vì đã cứu tôi. Tuy nhiên, vấn đề của tôi lại khác (tôi đoán nó thực sự nằm trong phần "những gì họ chưa bao giờ nói với bạn"). Đối tượng DateTime được truy xuất từ ​​DB có vẻ ổn nhưng nếu tôi truy cập bất kỳ thuộc tính nào của nó, chẳng hạn như ->datehoặc ->timezone, chỉ nullđược trả về. Tôi nhận thấy rằng nếu tôi var_dumped đối tượng trước khi sử dụng các thuộc tính này, các giá trị thích hợp sẽ được trả về. var_dumpSao chép không khắc phục được điều này, vì vậy tôi đoán nó thực sự có liên quan gì đó đến quyền truy cập ... Sau đó, tôi thấy Tùy chọn số 1 và voilá của bạn, truy cập nó dưới dạng một mảng ( $objCastAsArray['date']) hoạt động như một sự quyến rũ.
Armfoot

1
Sự thật # 0 : Việc truyền các mảng thành các đối tượng ngay từ đầu không nên có cảm giác khó chịu. Sự thật # 1 đến Sự kiện # 3: không cần thiết.
Pacerier

4
@Pacerier: Tôi đồng ý rằng nó hơi có vấn đề, nhưng nó hoàn toàn có thể có ý nghĩa trong một số tình huống. Dù sao, vì nó được ghi trong sách hướng dẫn để hoạt động như thế này, nên ý kiến ​​cá nhân của chúng tôi không thực sự quan trọng.
Jon

Một giải pháp thay thế cho Tùy chọn số 3 không yêu cầu UTF-8 sẽ là$o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
OscarJ

10

Chỉ muốn thêm vào lời giải thích hùng hồn của Jon về lý do tại sao điều này không thành công. Tất cả là do khi tạo một mảng, php sẽ chuyển đổi các khóa thành số nguyên - nếu có thể - gây ra vấn đề tra cứu trên các mảng đã được truyền sang các đối tượng, đơn giản là vì khóa số được giữ nguyên. Điều này có vấn đề vì tất cả các tùy chọn truy cập thuộc tính đều mong đợi hoặc chuyển đổi thành chuỗi. Bạn có thể xác nhận điều này bằng cách làm như sau:

$arr = array('123' => 'abc');
$obj = (object) $arr;
$obj->{'123'} = 'abc';
print_r( $obj );

Cái nào sẽ xuất ra:

stdClass Object ( 
  [123] => 'abc', 
  [123] => 'abc'
)

Vì vậy, đối tượng có hai khóa thuộc tính, một khóa số (không thể truy cập) và một dựa trên chuỗi. Đây là lý do tại sao Jon #Fact 4hoạt động, bởi vì bằng cách đặt thuộc tính bằng cách sử dụng dấu ngoặc nhọn có nghĩa là bạn luôn xác định khóa dựa trên chuỗi, thay vì số.

Thực hiện giải pháp của Jon, nhưng ngược lại, bạn có thể tạo một đối tượng từ mảng luôn có các khóa dựa trên chuỗi của mình bằng cách thực hiện như sau:

$obj = json_decode(json_encode($arr));

Từ bây giờ, bạn có thể sử dụng một trong hai cách sau vì truy cập theo cách này luôn chuyển đổi giá trị bên trong dấu ngoặc nhọn thành một chuỗi:

$obj->{123};
$obj->{'123'};

PHP phi logic cũ tốt ...


1

Nếu một đối tượng bắt đầu bằng @như sau:

SimpleXMLElement Object (
    [@attributes] => Array (
        [href] => qwertyuiop.html
        [id] => html21
        [media-type] => application/xhtml+xml
    )
)

Bạn phải sử dụng:

print_r($parent_object->attributes());

bởi vì $parent_object->{'@attributes'}hoặc $parent_object['@attributes']sẽ không hoạt động.


3 năm sau và điều này vẫn còn giúp đỡ mọi người, cảm ơn! Trong khi câu trả lời của bạn khắc phục được sự cố của tôi, nó không có lời giải thích. Có ai có thể giải thích lý do đằng sau điều này?
Arbiter

1

Tôi đã sao chép chức năng này từ mạng. Nếu nó hoạt động như nó nói ("Chức năng chuyển đổi đối tượng stdClass thành mảng đa chiều"), hãy thử như sau:

<?php

    function objectToArray($d) {
        if (is_object($d)) {
            // Gets the properties of the given object
            // with get_object_vars function
            $d = get_object_vars($d);
        }

        if (is_array($d)) {
            /*
            * Return array converted to object
            * Using __FUNCTION__ (Magic constant)
            * for recursive call
            */
            return array_map(__FUNCTION__, $d);
        }
        else {
            // Return array
            return $d;
        }
    }

?>
  • đầu tiên chuyển mảng của bạn vào objectToArrayhàm
  • sau đó lấy giá trị trả về
  • tiếng vang [highlighting][448364][Data][0]

Nguồn: PHP stdClass thành Array và Array thành stdClass


1

Một thay thế cuối cùng cho câu trả lời toàn diện của Jon:

Đơn giản chỉ cần sử dụng json_decode () với tham số thứ hai được đặt thành true .

$array = json_decode($url, true);

Sau đó, nó trả về một mảng kết hợp chứ không phải là một đối tượng nên không cần chuyển đổi sau thực tế.

Điều này có thể không phù hợp với mọi ứng dụng nhưng nó thực sự đã giúp tôi dễ dàng tham chiếu một thuộc tính của đối tượng gốc.

Giải pháp được tìm thấy trong hướng dẫn này - http://nitschinger.at/Handling-JSON-like-a-boss-in-PHP/

Trân trọng


1

Đối với PHP 7

Truy cập thuộc tính Đối tượng có số dưới dạng tên thuộc tính. Hầu hết cần thiết sau khi truyền mảng sang đối tượng.

    $arr = [2,3,7];
    $o = (object) $arr;

    $t = "1";
    $t2 = 1;
    $t3 = (1);

    echo $o->{1};      // 3
    echo $o->{'1'};   // 3
    echo $o->$t;        // 3
    echo $o->$t2;       // 3
    echo $o->$t3;       // 3

    echo $o->1;       // error
    echo $o->(1);      // error

0

Tôi e rằng bạn không được phép đặt tên cho các đối tượng bắt đầu bằng số. Đổi tên cái đầu tiên "448364" bắt đầu bằng một chữ cái.

Cái thứ hai là một mảng, chúng phải được truy cập bằng dấu ngoặc như sau:

print myVar->highlighting->test_448364->Data[0]

thay thế


Tôi không thể thay đổi nó. Đầu ra được trả về từ một ứng dụng mà tôi không có quyền kiểm soát.
avinash shah
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.