Hướng dẫn phpstan union type - loại công đoàn phpstan

Thực đơn

Ngày 27 tháng 11 năm 2017 · 6 phút Đọc

Một trong những tính năng dẫn đầu của Phpstan 0.9 là sự ra đời của các loại giao điểm. Vì đây là một tính năng rất hữu ích giúp chúng tôi hiểu mã tốt hơn nhiều, nhưng thuật ngữ này phần lớn chưa được biết đến và bí ẩn đối với cộng đồng PHP, tôi quyết định viết và so sánh hai loại hợp chất này.

Các loại công đoàn

  • Được viết là Foo|Bar. Cú pháp chung được sử dụng trong PHPDOCs trong nhiều năm.
  • Nó có nghĩa là loại biến là foo hoặc thanh.
  • Khi một hàm chấp nhận Foo|Bar như một đối số, điều đó có nghĩa là chỉ các thuộc tính và phương thức có sẵn trên cả FOO và BAR mới có thể được truy cập một cách an toàn trên biến. Nếu FOO có $loremProperty, bạn chỉ có thể truy cập nếu thanh có cùng một thuộc tính.
  • Nó rất nguy hiểm khi chuyển giá trị của loại Foo|Bar cho một tham số hàm trong đó chỉ cho phép FOO, bởi vì nếu nó thanh trong thời gian chạy, hàm được gọi không chấp nhận nó.
  • Nó rất tốt để chuyển giá trị của loại foo cho tham số hàm của loại Foo|Bar, bởi vì FOO là một trong những tùy chọn được phép hơn.

Cách phổ biến nhất để sử dụng các loại công đoàn là trong các đối số chức năng chấp nhận nhiều loại khác nhau. Chúng có thể được so sánh với ví dụ hoặc một nhà điều hành so sánh để cắt giảm các khả năng khi làm việc với chúng:

/**
* @param Foo|Bar $object
*/

public function doSomethingUseful($object)
{
if ($object instanceof Foo) {
// now we can be sure that $object is just Foo in this branch
} elseif ($object instanceof Bar) {
// dtto for Bar
}
}

Đừng nhầm lẫn các loại liên minh với cách các nhà phát triển thường đánh dấu các loại vật phẩm trong một bộ sưu tập (mà thường là một trình lặp):

/**
* @param Collection|Foo[] $object
*/

public function doSomethingUseful($object)
{
foreach ($object as $foo) {
// $foo is Foo here
}
}

Các loại giao điểm

  • Được viết là

    /**
    * @param Collection|Foo[] $object
    */

    public function doSomethingUseful($object)
    {
    foreach ($object as $foo) {
    // $foo is Foo here
    }
    }
    0. Đây là một cú pháp rất hiếm nhìn thấy trong tự nhiên và nó thậm chí có thể không được IDE của bạn hỗ trợ. Đó là vì khi mọi người viết Foo|Bar, đôi khi họ có nghĩa là một loại công đoàn và đôi khi là một loại giao nhau. Hãy để hy vọng rằng điều này thay đổi trong những tháng và năm tiếp theo (không chỉ) nhờ Phpstan. Sự khác biệt giữa họ giúp mã dễ hiểu hơn và cũng thân thiện hơn với các máy phân tích tĩnh có lợi cho mọi người.

  • Nó có nghĩa là loại biến là foo và thanh cùng một lúc.

  • Khi một hàm chấp nhận

    /**
    * @param Collection|Foo[] $object
    */

    public function doSomethingUseful($object)
    {
    foreach ($object as $foo) {
    // $foo is Foo here
    }
    }
    0 làm đối số, điều đó có nghĩa là các thuộc tính và phương thức từ một trong hai loại có thể được truy cập một cách an toàn. Bạn có thể truy cập các thuộc tính và các phương thức gọi từ foo ngay cả khi chúng không ở trên thanh.

  • Nó rất tốt để chuyển giá trị của loại

    /**
    * @param Collection|Foo[] $object
    */

    public function doSomethingUseful($object)
    {
    foreach ($object as $foo) {
    // $foo is Foo here
    }
    }
    0 cho một tham số của loại foo.

  • Nó rất nguy hiểm khi chuyển giá trị của loại foo cho một tham số loại

    /**
    * @param Collection|Foo[] $object
    */

    public function doSomethingUseful($object)
    {
    foreach ($object as $foo) {
    // $foo is Foo here
    }
    }
    0, bởi vì ai đó có thể truy cập vào một thuộc tính hoặc gọi một phương thức từ thanh.

  • Nó rất tốt để chuyển giá trị của loại

    /**
    * @param Collection|Foo[] $object
    */

    public function doSomethingUseful($object)
    {
    foreach ($object as $foo) {
    // $foo is Foo here
    }
    }
    5 cho một tham số loại
    /**
    * @param Collection|Foo[] $object
    */

    public function doSomethingUseful($object)
    {
    foreach ($object as $foo) {
    // $foo is Foo here
    }
    }
    0, loại Baz chỉ đơn giản là bị vứt đi trong trường hợp này.

Vấn đề là bạn đã tạo ra các loại giao điểm trong mã của bạn và thậm chí không nhận ra nó! Xem xét mã này:

public function doSomethingUseful(Foo $object)
{
if ($object instanceof BarInterface) {
// $foo is Foo&BarInterface here!
}
}

Trường hợp sử dụng khác cho chúng là các đối tượng giả trong các khung kiểm tra đơn vị, thường là PHPUNIT. Hãy suy nghĩ về nó: Bạn tạo một đối tượng và nó có thể gọi các phương thức trên đó từ cả hai phương thức cấu hình cụ thể và mô phỏng!

class Foo
{
public function doFooStuff()
{
}
}

class SomeTest extends \PHPUnit\Framework\TestCase
{
public function testSomething()
{
$mockedFoo = $this>createMock(Foo::class);
// $mockedFoo is Foo&PHPUnit_Framework_MockObject_MockObject

// we can call mock-configuration methods:
$mockedFoo->method('doFooStuff')
->will($this->returnValue('fooResult'));

// and also methods from Foo itself:
$mockedFoo->doFooStuff();
}
}

Bạn cũng có thể tận dụng các loại giao nhau nếu bạn không muốn buộc mã của mình với một lớp cụ thể, nhưng muốn đánh máy nhiều giao diện cùng một lúc. Ví dụ: nếu bạn muốn lặp lại một cách an toàn trên một đối tượng và đồng thời chuyển nó đến

/**
* @param Collection|Foo[] $object
*/

public function doSomethingUseful($object)
{
foreach ($object as $foo) {
// $foo is Foo here
}
}
7, bạn có thể làm điều đó như thế này:

/**
* @param \Traversable&\Countable $object
*/

public function doSomethingUseful($object)
{
echo sprintf(
'We are going to iterate over %d values!',
count($object)
);
foreach ($object as $foo) {

}
}

Điều này thực sự tốt đẹp vì nó hỗ trợ thiết kế cơ sở mã của bạn với các giao diện nhỏ và đơn giản.


Bạn có thích Phpstan và sử dụng nó mỗi ngày? Xem xét hỗ trợ phát triển hơn nữa của PHPSTAN trên các nhà tài trợ GitHub. Tôi thực sự đánh giá cao nó!Consider supporting further development of PHPStan on GitHub Sponsors. I’d really appreciate it!