jausions/php-typed-doctrine-collections

类型化的 Doctrine 集合(具有类型约束的伪数组)

v1.0.0 2018-05-03 12:31 UTC

This package is not auto-updated.

Last update: 2024-09-29 23:17:04 UTC


README

类型提示正在发展,但 PHP 7 目前仍然没有提供定义数组(即泛型)元素类型的方法。

这个库建立在 Doctrine/Collections 之上,以对添加到集合中的元素进行类型检查。我们也可以称之为强类型数组。

目标是利用类型提示来帮助防止错误,或者至少在开发周期中更早地检测到它们。

对于这个库来说,“类型”一词是广义的,指代内置的 PHP 类型、类,甚至是应用程序域类型。

安装

composer require jausions/php-typed-doctrine-collections

以下示例中隐含了 require 'vendor/autoload.php';

通过初始值定义类型

传递给构造函数的第一个元素决定了后续元素的准则。

<?php
use Abacus11\Doctrine\Collections\CollectionOf;

$int_array = new CollectionOf([1, 2]);      // Okay
$int_array = new CollectionOf([1, '2']);    // Not okay - throws \TypeError
$int_array = new CollectionOf([null, 1]);   // Not okay - throws \InvalidArgumentException

通过样本值定义类型

元素验证是针对样本值的类型进行的。

<?php
use Abacus11\Doctrine\Collections\CollectionOf;

$sample = 1;
$int_array = (new CollectionOf())->setElementTypeLike($sample);

$int_array[] = 2;              // Okay
$int_array[] = true;           // Not okay - throws \TypeError exception

class SomeClass {}

$sample = new SomeClass();
$some = (new CollectionOf())->setElementTypeLike($sample);

$some[] = new SomeClass();     // Okay
$some[] = new stdClass();      // Not okay - throws \TypeError exception

通过闭包定义类型

可以通过闭包检查添加到集合中的元素。

<?php
use Abacus11\Doctrine\Collections\CollectionOf;

// Use the setElementType() method

$positive_int = (new CollectionOf())->setElementType(function ($value) {
    if (!is_integer($value)) {
        return false;
    }
    return ($value >= 0);
});

$positive_int['apples'] = 0;      // Okay
$positive_int['oranges'] = 10;    // Okay
$positive_int['bananas'] = -5;    // Not okay - throws \TypeError exception

// Or directly in the constructor

$negative_int = new CollectionOf(
    function ($value) {
        if (!is_integer($value)) {
            return false;
        }
        return ($value <= 0);
    }
);

$negative_int[] = -50;            // Okay
$negative_int[] = 5;              // Not okay - throws \TypeError exception

通过类名定义类型

添加到集合中的对象可以与类名进行校验。

<?php

use Abacus11\Doctrine\Collections\CollectionOf;

class A {}

class B {}

class AA extends A {}

// Use the setElementType() method

$some_a = (new CollectionOf())->setElementType(A::class);

$some_a[] = new A();    // Okay
$some_a[] = new AA();   // Okay
$some_a[] = new B();    // Not okay - throws \TypeError exception

// Or directly in the constructor

$some_b = new CollectionOf(B::class);

$some_b[] = new B();    // Okay
$some_b[] = new A();    // Not okay - throws \TypeError exception

内置库类型

除了闭包或类名之外,setElementType() 方法还接受以下值

  • 数组
  • 布尔值
  • 可调用
  • 双精度浮点数
  • 整数
  • 数字
  • JSON
  • 对象
  • 资源
  • 字符串
<?php
use Abacus11\Doctrine\Collections\CollectionOf;

// Use the setElementType() method

$int_array = (new CollectionOf())->setElementType('integer');

$int_array[] = 1;      // Okay
$int_array[] = '1';    // Not okay - throws \TypeError exception

// Or directly in the constructor

$int_array = new CollectionOf('integer');

$int_array[] = 20;     // Okay
$int_array[] = true;   // Not okay - throws \TypeError exception

内置集合

预定义了几个类型化的集合

  • \Abacus11\Doctrine\Collections\Arrays
  • \Abacus11\Doctrine\Collections\Booleans
  • \Abacus11\Doctrine\Collections\Callables
  • \Abacus11\Doctrine\Collections\Doubles
  • \Abacus11\Doctrine\Collections\Integers
  • \Abacus11\Doctrine\Collections\Numbers
  • \Abacus11\Doctrine\Collections\JSONs
  • \Abacus11\Doctrine\Collections\Objects
  • \Abacus11\Doctrine\Collections\Resources
  • \Abacus11\Doctrine\Collections\Strings
<?php
$integers = new \Abacus11\Doctrine\Collections\Integers([1, 2, 3, 0, -1]);

自定义类型集合

您可以通过扩展基本类或将特质包含到您自己实现的 ArrayAccess 接口中来轻松创建集合。

<?php
use Abacus11\Doctrine\Collections\CollectionOf;

class Vehicle
{
}

class Car extends Vehicle
{
    public $make;
    public $model;
    public $color;
    public $license_plate_number;
}

class Submarine extends Vehicle
{
    public $name;
}

class Cars extends CollectionOf
{
    /**
     * @param Car[] $cars
     */
    public function __construct(array $cars = []) {
        parent::__construct(Car::class, $cars);
        // - or -
        //$this->setElementType(Car::class);
        //parent::__construct($cars);
    }
}

class Parking
{
    /**
     * @var Cars
     */
    protected $lot;

    public function __construct()
    {
        $this->lot = new Cars([]);
    }

    public function enter(Vehicle $car)
    {
        $this->lot[] = $car;
    }

    /**
     * @return Car[] The collection of cars
     */
    public function getCars(): Cars
    {
        return $this->lot;
    }

    //...
}

$my_car = new Car();
$my_car->model = 'T';
$my_car->make = 'Ford';
$my_car->color = 'Black';
$my_car->license_plate_number = 'MI-01234';

$my_sub = new Submarine();
$my_sub->name = 'Nautilus';

$parking = new Parking();
$parking->enter($my_car);   // Okay
$parking->enter($my_sub);   // Not okay - throws \TypeError exception

备注

  1. 我们本可以用 Car 类而不是 Vehicle 类来提示 enter() 方法。这也会抛出一个 \TypeError 异常。
  2. 我注意到我在 docBlockgetCars() 方法的签名中混合了类型。这可能会更容易阅读,并且可能有助于您的 IDE。然而,这种好处可能因编辑器/IDE 而异,并且如果尝试使用某些期望原生 array 类型的数组函数,可能会导致混淆。