วิธีเข้าถึงคุณสมบัติของวัตถุที่มีชื่อเช่นจำนวนเต็ม?


87

ฉันใช้json_decode()สิ่งที่ชอบ:

$myVar = json_decode($data)

ซึ่งทำให้ฉันได้ผลลัพธ์ดังนี้:

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

ฉันต้องการเข้าถึงค่าสตริงในคีย์ [0] เมื่อฉันพยายามทำสิ่งที่ชอบ:

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

ฉันได้รับข้อผิดพลาดนี้:

ข้อผิดพลาดในการแยกวิเคราะห์: ข้อผิดพลาดทางไวยากรณ์ T_DNUMBER ที่ไม่คาดคิด

ดูเหมือนว่าตัวเลข / จำนวนเต็มทั้งสองจะมีปัญหา



1
@FelixKling: ฉัน CVed ด้วย แต่จริงๆแล้วมันไม่ใช่การหลอกลวง: มันสร้างความแตกต่างถ้าชื่อคุณสมบัติขึ้นต้นด้วยตัวเลขหรือเป็นตัวเลขทั้งหมด !
จอน

@ จอน: อืมน่าสนใจ ... ควรทำแบบทดสอบก่อนที่จะเดา ขอบคุณสำหรับการให้ฉันรู้ว่า!
Felix Kling

คำตอบ:


288

อัปเดตสำหรับ PHP 7.2

PHP 7.2 นำเสนอการเปลี่ยนแปลงพฤติกรรมในการแปลงคีย์ตัวเลขในออบเจ็กต์และอาร์เรย์ซึ่งแก้ไขความไม่สอดคล้องกันนี้โดยเฉพาะและทำให้ตัวอย่างต่อไปนี้ทั้งหมดทำงานตามที่คาด

น้อยเรื่องที่จะสับสน!


คำตอบเดิม (ใช้กับเวอร์ชันก่อนหน้า 7.2.0)

PHP มีหุ้นของตรอกซอกซอยมืดที่คุณจริงๆไม่ต้องการที่จะพบว่าตัวเองอยู่ภายใน คุณสมบัติของวัตถุที่มีชื่อเป็นตัวเลขเป็นหนึ่งในนั้น ...

สิ่งที่พวกเขาไม่เคยบอกคุณ

ข้อเท็จจริง # 1:คุณไม่สามารถเข้าถึงคุณสมบัติที่มีชื่อที่ไม่ใช่ชื่อตัวแปรตามกฎหมายได้อย่างง่ายดาย

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

ข้อเท็จจริง # 2:คุณสามารถเข้าถึงคุณสมบัติดังกล่าวได้ด้วยไวยากรณ์ปีกกา

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

ข้อเท็จจริง # 3:แต่ไม่ใช่ถ้าชื่อคุณสมบัติเป็นตัวเลขทั้งหมด!

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

ตัวอย่างสด

ข้อเท็จจริง # 4:เว้นแต่ว่าวัตถุนั้นไม่ได้มาจากอาร์เรย์ตั้งแต่แรก

$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?

ตัวอย่างสด

ใช้งานง่ายดีคุณไม่เห็นด้วย?

คุณสามารถทำอะไรได้บ้าง

ตัวเลือก # 1: ทำด้วยตนเอง

แนวทางที่เป็นประโยชน์ที่สุดคือเพียงแค่โยนวัตถุที่คุณสนใจกลับเข้าไปในอาร์เรย์ซึ่งจะช่วยให้คุณสามารถเข้าถึงคุณสมบัติ:

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

น่าเสียดายที่การดำเนินการนี้ไม่สามารถทำงานซ้ำได้ ดังนั้นในกรณีของคุณคุณต้องทำสิ่งต่างๆเช่น:

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

ตัวเลือก # 2: ตัวเลือกนิวเคลียร์

อีกทางเลือกหนึ่งคือการเขียนฟังก์ชันที่แปลงวัตถุเป็นอาร์เรย์แบบวนซ้ำ:

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'];

อย่างไรก็ตามฉันไม่มั่นใจว่านี่เป็นตัวเลือกที่ดีกว่าในกระดานเพราะมันจะถูกส่งไปยังอาร์เรย์ของคุณสมบัติทั้งหมดที่คุณไม่สนใจรวมทั้งคุณสมบัติที่คุณเป็นอยู่โดยไม่จำเป็น

ตัวเลือก # 3: เล่นอย่างชาญฉลาด

อีกทางเลือกหนึ่งของตัวเลือกก่อนหน้านี้คือการใช้ฟังก์ชัน JSON ในตัว:

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

ฟังก์ชัน JSON ทำการแปลงแบบวนซ้ำเป็นอาร์เรย์โดยไม่จำเป็นต้องกำหนดฟังก์ชันภายนอกใด ๆ อย่างไรก็ตามรูปลักษณ์นี้เป็นที่ต้องการ แต่ก็มีข้อเสีย "nuke" ของตัวเลือก # 2 และนอกจากนี้ข้อเสียคือหากมีสตริงใด ๆ ในวัตถุของคุณสตริงเหล่านั้นจะต้องเข้ารหัสเป็น UTF-8 (นี่เป็นข้อกำหนดของjson_encode)


มันเกิดขึ้นเพื่อแก้ปัญหาของฉันด้วย! stackoverflow.com/questions/4643894/…
Bossliaw

จอนขอบคุณที่ช่วยฉัน แม้ว่าปัญหาของฉันจะแตกต่างออกไป (ฉันเดาว่ามันอยู่ในส่วนของ "สิ่งที่พวกเขาไม่เคยบอกคุณ") วัตถุ DateTime เรียกข้อมูลจาก DB ดูเหมือนตกลง แต่ถ้าฉันเข้าถึงใด ๆ ของคุณสมบัติของมันเช่น->dateหรือ->timezoneเพียงnullจะถูกส่งกลับ ฉันสังเกตว่าถ้าฉัน var_dumped วัตถุก่อนที่จะใช้คุณสมบัติเหล่านี้ค่าที่เหมาะสมจะถูกส่งกลับ การโคลนไม่ได้แก้ไขปัญหานี้ดังนั้นฉันเดาว่ามันมีส่วนเกี่ยวข้องกับการเข้าถึงที่var_dumpทำ ... จากนั้นฉันเห็นตัวเลือก # 1 และvoiláของคุณการเข้าถึงเป็นอาร์เรย์ ( $objCastAsArray['date']) ทำงานได้อย่างมีเสน่ห์
Armfoot

1
ข้อเท็จจริง # 0 : การหล่ออาร์เรย์ลงในวัตถุไม่ควรทำให้รู้สึกเหม็นตั้งแต่แรก ข้อเท็จจริง # 1 ถึงข้อเท็จจริง # 3: ไม่จำเป็น
Pacerier

4
@Pacerier: ฉันยอมรับว่ามันค่อนข้างน่าสงสัย แต่มันก็สมเหตุสมผลในบางสถานการณ์ อย่างไรก็ตามเนื่องจากมีการบันทึกไว้ในคู่มือเพื่อให้ทำงานเช่นนี้ความคิดเห็นส่วนตัวของเราจึงไม่สำคัญ
จอน

อีกทางเลือกหนึ่งสำหรับตัวเลือก # 3 ที่ไม่ต้องใช้ UTF-8 คือ$o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
OscarJ

10

แค่อยากจะเพิ่มคำอธิบายที่คมคายของจอนว่าทำไมถึงล้มเหลว ทั้งหมดเป็นเพราะเมื่อสร้างอาร์เรย์ php จะแปลงคีย์เป็นจำนวนเต็ม - หากทำได้ซึ่งทำให้เกิดปัญหาการค้นหาอาร์เรย์ที่ถูกส่งไปยังอ็อบเจ็กต์เนื่องจากคีย์ตัวเลขถูกเก็บรักษาไว้ นี่เป็นปัญหาเนื่องจากตัวเลือกการเข้าถึงคุณสมบัติทั้งหมดคาดหวังหรือแปลงเป็นสตริง คุณสามารถยืนยันได้โดยดำเนินการดังต่อไปนี้:

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

ซึ่งจะส่งออก:

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

ดังนั้นออบเจ็กต์จึงมีคีย์คุณสมบัติสองคีย์ตัวเลขหนึ่งตัว (ซึ่งไม่สามารถเข้าถึงได้) และหนึ่งสตริงตาม นี่คือเหตุผลว่าทำไมจอนถึงใช้#Fact 4งานได้เนื่องจากการตั้งค่าคุณสมบัติโดยใช้วงเล็บปีกกาหมายความว่าคุณจะกำหนดคีย์แบบสตริงแทนที่จะเป็นตัวเลข

การใช้วิธีแก้ปัญหาของ Jon แต่เปิดไว้ที่ส่วนหัวคุณสามารถสร้างวัตถุจากอาร์เรย์ของคุณที่มีคีย์ตามสตริงโดยทำสิ่งต่อไปนี้:

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

จากนี้ไปคุณสามารถใช้อย่างใดอย่างหนึ่งต่อไปนี้เนื่องจากการเข้าถึงในลักษณะนี้จะแปลงค่าในวงเล็บปีกกาเป็นสตริง:

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

PHP ที่ไร้เหตุผลเก่า ๆ ...


1

หากวัตถุขึ้นต้นด้วย@like:

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

คุณต้องใช้:

print_r($parent_object->attributes());

เพราะ$parent_object->{'@attributes'}หรือ$parent_object['@attributes']ไม่ทำงาน


3 ปีต่อมาและสิ่งนี้ยังคงช่วยเหลือผู้คนอยู่ขอบคุณ! แม้ว่าคำตอบของคุณจะแก้ไขปัญหาของฉันได้ แต่ก็ไม่มีคำอธิบาย มีใครสามารถอธิบายเหตุผลเบื้องหลังเรื่องนี้ได้บ้าง?
ตุลาการ

1

ฉันได้คัดลอกฟังก์ชันนี้จากเน็ต หากทำงานตามที่ระบุไว้ ("ฟังก์ชันในการแปลงวัตถุ stdClass เป็นอาร์เรย์หลายมิติ") ให้ลองทำดังต่อไปนี้:

<?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;
        }
    }

?>
  • ก่อนอื่นให้ส่งอาร์เรย์ของคุณไปที่objectToArrayฟังก์ชัน
  • จากนั้นรับค่าส่งคืน
  • ก้อง [highlighting][448364][Data][0]

ที่มา: PHP stdClass ถึง Array และ Array ไปยัง stdClass


1

ทางเลือกสุดท้ายสำหรับคำตอบที่ครอบคลุมของ Jon:

เพียงแค่ใช้ json_decode () กับชุดพารามิเตอร์ที่สองที่จะเป็นจริง

$array = json_decode($url, true);

สิ่งนี้จะส่งกลับอาร์เรย์ที่เชื่อมโยงแทนที่จะเป็นวัตถุดังนั้นไม่จำเป็นต้องแปลงหลังจากข้อเท็จจริง

สิ่งนี้อาจไม่เหมาะกับทุกแอปพลิเคชัน แต่ช่วยให้ฉันสามารถอ้างอิงคุณสมบัติของวัตถุดั้งเดิมได้อย่างง่ายดาย

พบวิธีแก้ปัญหาในบทช่วยสอนนี้ - http://nitschinger.at/Handling-JSON-like-a-boss-in-PHP/

ความนับถือ


1

สำหรับ PHP 7

การเข้าถึงคุณสมบัติของวัตถุที่มีตัวเลขเป็นชื่อคุณสมบัติ ส่วนใหญ่จำเป็นหลังจากส่งอาร์เรย์ไปยังวัตถุ

    $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

ฉันเกรงว่าคุณจะไม่ได้รับอนุญาตให้ตั้งชื่อวัตถุที่ขึ้นต้นด้วยตัวเลข เปลี่ยนชื่อคนแรกเป็น "448364" โดยเริ่มต้นด้วยตัวอักษร

อันที่สองคืออาร์เรย์ซึ่งจะเข้าถึงได้ด้วยวงเล็บดังนี้:

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

แทน


ฉันไม่สามารถเปลี่ยนแปลงได้ ผลลัพธ์จะถูกส่งกลับจากแอปพลิเคชันที่ฉันไม่สามารถควบคุมได้
avinash shah
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.