การประกาศโครงสร้างอาร์เรย์ใน PHP

Feb 20 2023
น่าเสียดาย เป็นไปไม่ได้ที่จะกำหนดประเภทอาร์เรย์ที่ชัดเจนเหมือนภาษาโปรแกรมอื่นๆ เช่น Java: หวังว่าเครื่องมือบางอย่างเช่น PHPStan หรือ Psalm จะช่วยให้เราสามารถวิเคราะห์โค้ดแบบคงที่ได้ ซึ่งหมายความว่าเครื่องมือเหล่านี้ไม่ได้ดำเนินการ แต่ตรวจสอบโค้ดที่ไม่สอดคล้องกันตาม ในความคิดเห็น PHP มี 3 วิธีที่แตกต่างกันในการกำหนดประเภทขององค์ประกอบในอาร์เรย์ PHP: นี่เป็นวิธีที่ทันสมัยในการกำหนดรายการขององค์ประกอบจากบางประเภท ปัญหาคือมันจะกลายเป็นความคลุมเครือ และไคลเอนต์ที่ใช้รายการประเภทนี้ไม่สามารถรู้ได้ว่า คีย์เป็นจำนวนเต็ม จำนวนลอย หรือสตริง

น่าเสียดาย เป็นไปไม่ได้ที่จะกำหนดประเภทอาร์เรย์ที่ชัดเจนเหมือนกับภาษาโปรแกรมอื่นๆ เช่น Java:

List<User> users = new ArrayList<>();

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

มี 3 วิธีในการกำหนดประเภทขององค์ประกอบในอาร์เรย์ PHP:

  1. วิธีดั้งเดิม:User[]
  2. รูปร่างรายการ: array<int,mixed>&list<mixed>
  3. รูปร่างวัตถุ:array{0:int, foo:?string, bar:mixed}

นี่เป็นวิธีที่ทันสมัยในการกำหนดรายการองค์ประกอบจากบางประเภท ปัญหาคือมันกลายเป็นเรื่องคลุมเครือและไคลเอนต์ที่ใช้รายการประเภทนี้ไม่สามารถรู้ได้ว่าคีย์เป็นจำนวนเต็ม จำนวนลอย หรือสตริง
นอกจากนี้ ตัววิเคราะห์แบบสแตติกจะไม่ล้มเหลวหากคุณพยายามรับองค์ประกอบด้วยคีย์ด้วยประเภทที่ไม่ถูกต้อง ในสถานการณ์เหล่านี้ ฉันขอแนะนำให้array<int,mixed>ใช้

ในท้ายที่สุด ยิ่งชัดเจนมากเท่าไหร่ก็ยิ่งดีเท่านั้น

/** @var User[] $users */
$users = [ ... ];

# Are keys autoincremental integers, random numbers, maybe strings... ?
$firstUser = $users[ ? ];

# Static analyzers won't complain if you use an incorrect type 

เราใช้รายการเมื่อเรามีอาร์เรย์ขององค์ประกอบประเภทเดียวกัน
ในการทำเช่นนั้น เราใช้ไวยากรณ์ไดมอนด์เพื่อประกาศประเภทของคีย์และค่าarray<int,mixed>: มีทางลัดที่มีประโยชน์มากเมื่อคีย์เป็นจำนวนเต็มแบบเพิ่มอัตโนมัติlist<mixed>:

/** @var array<string,User> $users */
$users = [
    'jesus' => new User('Jesus Valera'),
    'chema' => new User('Chema Valera'),
];

$userJesus = $users['jesus'];
$userChema = $users['chema'];

$users[0]; # ERROR: "Offset 0 does not exist on array<string, User>"

---

/** @var list<User> $users */
$users = [ ... ];

$firstUser = reset($users);
$lastUser = end($users);

เราใช้รูปร่างของวัตถุเมื่ออาร์เรย์ไม่ใช่ชุดของวัตถุ แต่เป็นแผนที่ที่เก็บข้อมูล
ในการทำเช่นนั้น เราใช้ไวยากรณ์วงเล็บปีกกาและ กำหนดชื่อคีย์และประเภท เราสามารถแยกด้วยเครื่องหมายจุลภาคหากมีหลายองค์ประกอบ:array{foo:int, bar:string}

/** @var array{id:int, birthdate: DateTimeImmutable} */
$additionalInfo = [ ... ];

$id = $additionalInfo['id']; # int
$birthdate = $additionalInfo['birthdate']; # DateTimeImmutable

final class User
{
    /** @var list<Comment> */
    public readonly array $comments;

    /** @var array{id:int, birthdate: DateTimeImmutable} */
    public readonly array $additionalInfo;
}

แน่นอน มันเป็นไปได้ที่จะแสดงโครงสร้างอาร์เรย์ที่ซับซ้อนใน PHP ด้วยความคิดเห็น PHP เหล่านี้ ตัวอย่าง:

/**
 * @var list<
 *   array{
 *     uuid?: string,
 *     content: array{name: string, foo: ?stdClass},
 *   }
 * > $array
 */
$array = [
    0 => [
        'uuid' => 'XXXX-XXX-XXX-XXXX',
        'content' => [
            'name' => 'str',
            'foo' => null,
        ],
    ],
    1 => [
        'content' => [
            'name' => 'str',
            'foo' => new stdClass(),
        ],
    ],
    // ...
];

ทรัพยากร:

  • สดุดี | รูปร่างอาร์เรย์
  • PHPStan | ประเภทของ PHPDoc