Skip to content

Commit

Permalink
- Update to composer.json for versions.
Browse files Browse the repository at this point in the history
- Documentation update.
- Bump dependencies to jausions/php-typed-collections:^v2.0
  • Loading branch information
jausions committed Oct 31, 2023
1 parent a9b910f commit 7a0a89b
Show file tree
Hide file tree
Showing 15 changed files with 153 additions and 128 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Run tests

on:
push:
pull_request:
types: [opened, synchronize, reopened]
branches:
- master

jobs:
test-php:
name: Test on php ${{ matrix.php-version }} and ${{ matrix.os }}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: false
matrix:
php-version: ["5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2"]
os: [macos-latest, windows-latest, ubuntu-latest]
experimental: [false]
php-extensions: ["json"]
coverage-extension: ["none"]
include:
- { php-version: 'nightly', os: ubuntu-latest, experimental: true }
steps:
- uses: actions/checkout@v3
- name: Use php ${{ matrix.php-version }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: ${{ matrix.coverage-extension }}
extensions: ${{ matrix.php-extensions }}
ini-values: display_errors=on, display_startup_errors=on, error_reporting=-1
- name: Cache module
uses: actions/cache@v3
with:
path: ~/.composer/cache/
key: composer-cache
- name: Install dependencies
run: composer install --no-interaction
- name: Run PHPUnit test
run: vendor/bin/phpunit tests/
109 changes: 57 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Typed Doctrine Collections

Type hinting is evolving but PHP 7 still does not currently provide
Type hinting is evolving but PHP still does not currently provide
a way to define the type of the elements of an array (a.k.a. Generics.)

This library is built on top of Doctrine/Collections to enforce type
Expand All @@ -13,6 +13,7 @@ least, detect them earlier in the development cycle.
For the purpose of this library, the term *type* is used loosely to
refer to built-in PHP types, classes, and even application-domain types.


## Installation

```sh
Expand All @@ -21,41 +22,45 @@ composer require jausions/php-typed-doctrine-collections

In the examples below, the `require 'vendor/autoload.php';` is implied.


## Type Defined by Initial Value

The first element passed to the constructor determines the criteria for
the elements that come after it.

```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
$int_array = new CollectionOf([1, '2']); // Not okay, second element is of different type
$int_array = new CollectionOf([null, 1]); // Not okay, first element has no type
```


## Type Defined by a Sample Value

The element validation is done against the type of a sample value.

```php
<?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
$int_array[] = true; // Not okay - throws an exception

class SomeClass {}

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

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

## Type Defined by a Closure
Expand All @@ -64,36 +69,36 @@ The elements added to the collection can be checked with a closure:

```php
<?php

use Abacus11\Doctrine\Collections\CollectionOf;

// Use the setElementType() method

$positive_int = (new CollectionOf())->setElementType(function ($value) {
if (!is_integer($value)) {
$positive_int = (new CollectionOf())->setElementType(static function ($value) {
if (!is_int($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
$positive_int['bananas'] = -5; // Not okay - throws an exception

// Or directly in the constructor

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

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


## Type Defined by a Class Name

Objects added to the collection can be checked against a class name:
Expand All @@ -110,56 +115,60 @@ 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
$some_a[] = new B(); // Not okay - throws an 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
$some_b[] = new A(); // Not okay - throws an exception
```


## Built-In Library Types

Apart from a closure or a class name, the `setElementType()` method also
accepts the following values:

- `array`
- `boolean`
- `callable`
- `double`
- `integer`
- `number`
- `json`
- `object`
- `resource`
- `string`
- `TypedCollection::OF_ANYTHING`
- `TypedCollection::OF_ARRAYS`
- `TypedCollection::OF_BOOLEANS`
- `TypedCollection::OF_CALLABLES`
- `TypedCollection::OF_CLASSES_AND_INTERFACES`
- `TypedCollection::OF_DOUBLES`
- `TypedCollection::OF_INTEGERS`
- `TypedCollection::OF_JSON`
- `TypedCollection::OF_NUMBERS`
- `TypedCollection::OF_OBJECTS`
- `TypedCollection::OF_PHP_RESOURCES`
- `TypedCollection::OF_STRINGS`

```php
<?php

use Abacus11\Collections\TypedCollection;
use Abacus11\Doctrine\Collections\CollectionOf;

// Use the setElementType() method

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

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

// Or directly in the constructor

$int_array = new CollectionOf('integer');
$int_array = new CollectionOf(TypedCollection::OF_INTEGERS);

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


## Built-In Collections

Several typed collections are predefined:
Expand All @@ -180,38 +189,45 @@ Several typed collections are predefined:
$integers = new \Abacus11\Doctrine\Collections\Integers([1, 2, 3, 0, -1]);
```


## Custom Type Collections

You can easily create collections by extending the base class or by
including the trait into your own implementation of the ArrayAccess
interface.

> Remarks:
> 1. We could have type hinted the `enter()` method with the `Car` class instead
> of the `Vehicle` class.
> 2. I am aware that I mixed the types in the *docBlock* and the signature of
> the `getCars()` method. It is somewhat more legible and may help your IDE.
> However, the benefit may vary depending on your editor / IDE, and it may
> lead to confusion if trying to use some array function that expect a native
> `array` type.
```php
<?php

use Abacus11\Doctrine\Collections\CollectionOf;

class Vehicle
{
}
interface Vehicle {}

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

class Submarine extends Vehicle
class Submarine implements Vehicle
{
public $name;
}

class Cars extends CollectionOf
{
/**
* @param Car[] $cars
*/
/** @param Car[] $cars */
public function __construct(array $cars = []) {
parent::__construct(Car::class, $cars);
// - or -
Expand All @@ -222,9 +238,7 @@ class Cars extends CollectionOf

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

public function __construct()
Expand Down Expand Up @@ -259,14 +273,5 @@ $my_sub->name = 'Nautilus';

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

Remarks:
1. We could have type hinted the `enter()` method with the `Car` class instead
of the `Vehicle` class. This would also have thrown a \TypeError exception.
2. I am aware that I mixed the types in the *docBlock* and the signature of
the `getCars()` method. It is somewhat more legible and may help your IDE.
However, the benefit may vary depending on your editor / IDE, and it may
lead to confusion if trying to use some array function that expect a native
`array` type.
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
],
"require": {
"doctrine/collections": "^v1.5",
"jausions/php-typed-collections": "^v1.0"
"jausions/php-typed-collections": "^v2.0"
},
"require-dev": {
"phpunit/phpunit": "^7.0",
"fzaninotto/faker": "^v1.7"
"fakerphp/faker": "*",
"phpunit/phpunit": "*"
},
"autoload": {
"psr-4": {"Abacus11\\": "src/"}
Expand Down
10 changes: 4 additions & 6 deletions src/Doctrine/Collections/Arrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Abacus11\Doctrine\Collections;

use Abacus11\Collections\TypedCollection;

class Arrays extends CollectionOf
{
/**
Expand All @@ -10,13 +12,9 @@ class Arrays extends CollectionOf
* Each element of the collection is an array
*
* @param array[] $elements
*
* @throws \AssertionError
* @throws \Exception
* @throws \TypeError
*/
public function __construct(array $elements = [])
{
parent::__construct('array', $elements);
parent::__construct(TypedCollection::OF_ARRAYS, $elements);
}
}
}
10 changes: 4 additions & 6 deletions src/Doctrine/Collections/Booleans.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Abacus11\Doctrine\Collections;

use Abacus11\Collections\TypedCollection;

class Booleans extends CollectionOf
{
/**
Expand All @@ -10,13 +12,9 @@ class Booleans extends CollectionOf
* Each element of the collection is a boolean
*
* @param boolean[] $elements
*
* @throws \AssertionError
* @throws \Exception
* @throws \TypeError
*/
public function __construct(array $elements = [])
{
parent::__construct('boolean', $elements);
parent::__construct(TypedCollection::OF_BOOLEANS, $elements);
}
}
}
Loading

0 comments on commit 7a0a89b

Please sign in to comment.