Kodokon kodokon.com

การสร้าง JSON API ใน PHP

สร้าง JSON API ที่รัดกุมด้วยการเชี่ยวชาญเรื่องเฮดเดอร์ การอ่านเนื้อหาดิบ (raw body) และความหมายที่ละเอียดอ่อนของรหัสสถานะ

9 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

JSON API คือสัญญาข้อตกลง ทุกการตอบสนองจะประกาศชนิดของมัน (Content-Type: application/json) พร้อมรหัสสถานะที่ถูกต้องและโครงสร้างที่คงที่ PHP ไม่ส่งสิ่งเหล่านี้เลยตามค่าเริ่มต้น มันประกาศเป็น text/html บล็อกแรกที่จะสร้างคือ ตัวช่วยตอบสนอง (response helper) เพียงตัวเดียว ที่มีชนิดเป็น never (PHP 8.1 ขึ้นไป) ชนิดที่คืนค่านี้รับประกันทั้งต่อเอนจินและต่อเครื่องมือวิเคราะห์แบบสถิตว่าฟังก์ชันจะไม่มีวันคืนการควบคุมกลับมา โค้ดใด ๆ ที่เขียนหลังการเรียกจึงพิสูจน์ได้ว่าไปไม่ถึง

PHP
<?php
declare(strict_types=1);

function jsonResponse(
    mixed $data,
    int $status = 200
): never {
    http_response_code($status);
    header('Content-Type: application/json');
    echo json_encode(
        $data,
        JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE
    );
    exit;
}

jsonResponse(['status' => 'ok']);
จุดออกเพียงจุดเดียว สถานะ เฮดเดอร์ และเนื้อหาสอดคล้องกันเสมอ

ความละเอียดอ่อนข้อที่สอง ซึ่งมักถูกค้นพบเมื่อสายเกินไป $_POST จะ ว่างเปล่า เมื่อเจอกับเนื้อหาแบบ JSON PHP จะเติมมันให้เฉพาะสำหรับ application/x-www-form-urlencoded และ multipart/form-data เท่านั้น เนื้อหาดิบถูกอ่านจากสตรีม php://input ให้ถอดรหัสมันด้วย JSON_THROW_ON_ERROR หากไม่มีแฟล็กนั้น json_decode จะคืนค่า null แบบเงียบ ๆ เมื่อเกิดข้อผิดพลาด ซึ่งแยกไม่ออกจากเนื้อหาที่มีคำว่า null อยู่จริง ๆ และด้วยชนิด never คำสั่ง switch ด้านล่างจึงไม่ต้องมี break เลยแม้แต่น้อย

PHP
<?php
declare(strict_types=1);

switch ($_SERVER['REQUEST_METHOD']) {
    case 'GET':
        jsonResponse(['items' => []]);
    case 'POST':
        $raw = (string) file_get_contents(
            'php://input'
        );
        try {
            $payload = json_decode(
                $raw,
                true,
                512,
                JSON_THROW_ON_ERROR
            );
        } catch (JsonException) {
            jsonResponse(
                ['error' => 'Malformed JSON'],
                400
            );
        }
        header('Location: /items/42');
        jsonResponse(['created' => $payload], 201);
    default:
        header('Allow: GET, POST');
        jsonResponse(
            ['error' => 'Method not allowed'],
            405
        );
}
สมมติว่ามี jsonResponse นิยามไว้ด้านบน ส่วน catch ที่ไม่มีตัวแปรเป็นของ PHP 8

จงเลือกรหัสอย่างแม่นยำ เพราะไคลเอนต์ของคุณจะเขียนโปรแกรมโดยอิงกับรหัสเหล่านั้น ไม่ใช่กับข้อความของคุณ 200 อ่านสำเร็จ, 201 การสร้าง (พร้อมเฮดเดอร์ Location ที่ชี้ไปยังทรัพยากร), 204 สำเร็จโดยไม่มีเนื้อหา, 400 JSON ผิดรูปแบบ, 404 ไม่พบทรัพยากร, 405 เมท็อดที่ไม่รองรับ (พร้อมเฮดเดอร์ Allow ที่ระบุเมท็อดที่อนุญาต) และ 422 สำหรับ JSON ที่ถูกต้องตามไวยากรณ์แต่ผิดในเชิงความหมาย ตรวจสอบทั้งหมดด้วย curl -i ซึ่งจะแสดงเฮดเดอร์ที่ได้รับ

BASH
curl -i -X POST http://localhost:8000/items \
  -H 'Content-Type: application/json' \
  -d '{"name":"Widget"}'
ตัวเลือก -i แสดงสถานะและเฮดเดอร์ ซึ่งเป็นการทดสอบสัญญาข้อตกลงที่แท้จริงเพียงอย่างเดียว

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. API ของคุณได้รับ POST ที่มีเนื้อหาแบบ JSON ทำไม $_POST ถึงว่างเปล่า?
    • PHP เติม $_POST ให้เฉพาะเนื้อหาแบบ form-urlencoded และ multipart เท่านั้น เนื้อหาแบบ JSON ต้องอ่านจาก php://input
    • JSON เกินขีดจำกัด post_max_size ที่เป็นค่าเริ่มต้น
    • คุณต้องเรียก json_decode บน $_POST ก่อนเพื่อเติมมัน
  2. คุณควรคืนรหัสสถานะใดหลังจากสร้างสำเร็จผ่าน POST?
    • 200
    • 201
    • 204
    • 302
  3. ชนิดที่คืนค่าแบบ never รับประกันอะไร?
    • ฟังก์ชันคืนค่า null เสมอ
    • ฟังก์ชันไม่มีวันคืนการควบคุมกลับมา มันจบด้วย exit หรือข้อยกเว้น และเอนจินตรวจสอบสิ่งนี้
    • ฟังก์ชันไม่สามารถถูกโอเวอร์ไรด์โดยคลาสลูกได้