Kodokon kodokon.com

คุณภาพ: PHPUnit, PHPStan และมาตรฐาน PSR

ติดเครื่องมือให้โค้ดของคุณอย่างมืออาชีพ ด้วยการทดสอบ PHPUnit, การวิเคราะห์แบบสถิตของ PHPStan ที่ระดับ 9 และมาตรฐาน PSR

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

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

โค้ดระดับผู้เชี่ยวชาญนั้นสังเกตได้จากตาข่ายนิรภัยของมันมากกว่าลูกเล่นอันชาญฉลาด มีสามเสาหลักที่เสริมกัน ได้แก่ การทดสอบ (test) (PHPUnit) ที่ตรวจสอบพฤติกรรมด้วยการรันโค้ด, การวิเคราะห์แบบสถิต (static analysis) (PHPStan) ที่พิสูจน์คุณสมบัติต่าง ๆ โดยไม่ต้องรันอะไรเลย และ มาตรฐานที่ใช้ร่วมกัน (PSR) ที่ทำให้ระบบนิเวศทำงานร่วมกันได้ ทุกอย่างติดตั้งด้วย Composer ในฐานะการพึ่งพาสำหรับการพัฒนา (development dependency)

BASH
composer require --dev phpunit/phpunit
composer require --dev phpstan/phpstan
./vendor/bin/phpunit --testdox tests
./vendor/bin/phpstan analyse src --level=9
ตัวเลือก --testdox แสดงการทดสอบเป็นข้อกำหนด (specification) ที่อ่านได้เข้าใจง่าย

ตั้งแต่ PHPUnit 10 เป็นต้นมา เมทาดาทาถูกเขียนเป็นแอตทริบิวต์ของ PHP #[DataProvider] แทนที่คำอธิบายประกอบแบบ docblock @dataProvider ตัวจ่ายข้อมูล (data provider) ต้องเป็น public และ static เพราะ PHPUnit อ่านมันตั้งแต่ก่อนสร้างอินสแตนซ์ของคลาสทดสอบเสียอีก ชุดข้อมูลแต่ละชุดมีคีย์ที่บรรยายความหมาย ซึ่งจะแสดงตามที่เป็นเมื่อเคสนั้นล้มเหลว การล้มเหลวของ accents จึงถูกระบุตำแหน่งได้ทันที

PHP
<?php
declare(strict_types=1);

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

final class SlugifierTest extends TestCase
{
    #[DataProvider('provideTitles')]
    public function testSlugify(
        string $input,
        string $expected
    ): void {
        $this->assertSame($expected, slugify($input));
    }

    public static function provideTitles(): array
    {
        return [
            'simple' => [
                'Hello World', 'hello-world'
            ],
            'accents' => [
                'Déjà vu', 'deja-vu'
            ],
        ];
    }
}
ทดสอบฟังก์ชัน slugify ที่นิยามไว้ที่อื่นในโปรเจกต์ของคุณ

PHPStan ไล่ระดับความเข้มงวดของมันเป็นระดับ ตั้งแต่ 0 ถึง 9 (ในเวอร์ชันล่าสุดมีระดับ 10 อยู่ด้วย) ที่ระดับ 9 ค่า mixed ใด ๆ จะใช้ไม่ได้จนกว่าจะถูกจำกัดให้แคบลงด้วยการตรวจสอบอย่างชัดเจนเสียก่อน พลังที่แท้จริงของมันมาจาก ชนิดใน docblock ที่เอนจิน PHP มองข้ามแต่ตัววิเคราะห์ตรวจสอบ ได้แก่ รูปทรงอาร์เรย์ array{email: string}, ลิสต์ list<int>, และเจเนอริก พวกมันบันทึกและพิสูจน์ไปในเวลาเดียวกัน

PHP
<?php
declare(strict_types=1);

function findEmail(?array $user): string
{
    return $user['email'];
}

/**
 * @param array{email: string}|null $user
 */
function findEmailSafe(?array $user): string
{
    return $user === null
        ? 'n/a'
        : $user['email'];
}
ระดับ 9 ฟังก์ชันแรกถูกปฏิเสธ (อาจเข้าถึงบน null, คีย์และชนิดที่ไม่รู้จัก) ส่วนฟังก์ชันที่สองผ่าน

มาตรฐาน PSR (PHP Standards Recommendations) คือแบบแผนที่กำหนดโดย PHP-FIG PSR-1 และ PSR-12 ทำให้สไตล์เป็นมาตรฐาน ทั้งการเยื้อง วงเล็บปีกกา ลำดับการนำเข้า (import) ซึ่งถูกใช้โดยอัตโนมัติผ่าน php-cs-fixer หรือ phpcs PSR-4 นิยามการโหลดอัตโนมัติ (autoloading) คำนำหน้าเนมสเปซจับคู่กับไดเรกทอรีหนึ่ง ซึ่งทำให้ Composer ค้นหาคลาสใด ๆ ได้โดยไม่ต้องใช้ require ด้วยมือ สุดท้าย มาตรฐาน PSR ที่เป็นอินเทอร์เฟซ ได้แก่ PSR-3 สำหรับตัวบันทึก (logger) และ PSR-7 สำหรับข้อความ HTTP ช่วยให้คุณสลับไลบรารีหนึ่งไปเป็นอีกตัวได้โดยไม่ต้องแตะโค้ดที่ใช้งานมัน

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

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

  1. ตั้งแต่ PHPUnit 10 เป็นต้นมา ข้อจำกัดใดใช้กับเมท็อดตัวจ่ายข้อมูลที่ถูกอ้างอิงโดย #[DataProvider]?
    • มันต้องเป็น public และ static เพราะ PHPUnit อ่านมันก่อนสร้างอินสแตนซ์ของคลาสทดสอบ
    • มันต้องคืนเจเนอเรเตอร์ ไม่ใช่อาร์เรย์เด็ดขาด
    • มันต้องมีชื่อเดียวกับการทดสอบ โดยเติมท้ายด้วย Provider
  2. PHPStan ระดับ 9 กำหนดอะไรบังคับ?
    • ห้ามใช้ docblock โดยให้ใช้เฉพาะชนิดเนทีฟเท่านั้น
    • การจัดการ mixed อย่างเข้มงวด คุณไม่สามารถใช้ค่าเช่นนั้นได้โดยไม่จำกัดให้แคบลงก่อน
    • ความครอบคลุมของการทดสอบ 100% บนโค้ดที่วิเคราะห์
  3. มาตรฐาน PSR-4 นิยามอะไร?
    • รูปแบบของข้อความบันทึก (log) ของแอปพลิเคชัน
    • การจับคู่ระหว่างคำนำหน้าเนมสเปซกับไดเรกทอรี ที่ตัวโหลดอัตโนมัติของ Composer ใช้
    • สไตล์การเยื้องและการวางตำแหน่งวงเล็บปีกกา