Skip to content

Narrow property type hints #258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: CI

on: [push, pull_request]

jobs:
tests:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest]
php: [8.0, 8.1]
dependency-versions: [lowest, highest]

name: Tests - P${{ matrix.php }} - ${{ matrix.dependency-versions }} - ${{ matrix.os }}

steps:
- uses: actions/checkout@v2

- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick
coverage: none

- run: |
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"

- uses: ramsey/composer-install@v2
with:
dependency-versions: ${{ matrix.dependency-versions }}

- run: vendor/bin/phpunit

static-analysis:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
php: [8.0, 8.1]
dependency-versions: [lowest, highest]

name: Static Analysis - P${{ matrix.php }} - ${{ matrix.dependency-versions }}

steps:
- uses: actions/checkout@v2

- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick
coverage: none

- uses: ramsey/composer-install@v2
with:
dependency-versions: ${{ matrix.dependency-versions }}

- run: vendor/bin/phpstan
37 changes: 0 additions & 37 deletions .github/workflows/tests.yml

This file was deleted.

5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Return value for invalid enum case when using the `Description` attribute [264](https://github.com/BenSampo/laravel-enum/pull/264)

### Fixed

- Type-hint `Enum::$key` and `Enum::$description` as `string`
- Type-hint `FlaggedEnum::$value` as `int`

## [5.2.0](https://github.com/BenSampo/laravel-enum/compare/v5.1.0...v5.2.0) - 2022-03-11

### Fixed
Expand Down
15 changes: 10 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ Hey, thank you for contributing. Here are some tips to make it easy for you.
1. Think about how the changes you are about to make can be tested, write tests before coding
1. Run tests, make sure they fail
1. Write the actual code to make the tests pass
1. Run checks with `composer all`
1. Open a pull request detailing your changes. Make sure to follow the [template](.github/PULL_REQUEST_TEMPLATE.md)

## Testing

We use **PHPUnit** for automated tests.
We use [PHPUnit](https://phpunit.de) for automated tests.

Have a new feature? You can start off by writing some tests that detail
the behaviour you want to achieve and go from there.
Expand All @@ -34,14 +35,18 @@ composer test

Formatting is automated through [php_codesniffer](https://github.com/squizlabs/PHP_CodeSniffer).

Check the codestyle
Apply automated fixes

```bash
composer check-style
composer fix
```

Apply automated fixes
## Static Analysis

We use [PHPStan](https://phpstan.org) for static analysis.

Run static analysis

```bash
composer fix-style
composer stan
```
23 changes: 16 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,22 @@
],
"homepage": "https://github.com/bensampo/laravel-enum",
"require": {
"php": "^8.0",
"php": "^8",
"composer/composer": "^2.2",
"illuminate/contracts": "^9.0",
"illuminate/support": "^9.0",
"laminas/laminas-code": "^3.4 || ^4.0",
"illuminate/contracts": "^9",
"illuminate/support": "^9",
"laminas/laminas-code": "^3.4 || ^4",
"nikic/php-parser": "^4.13"
},
"require-dev": {
"doctrine/dbal": "^3.3",
"ergebnis/composer-normalize": "^2.24",
"mockery/mockery": "^1.4.4",
"orchestra/testbench": "^7.0",
"nunomaduro/larastan": "^2",
"orchestra/testbench": "^7",
"phpstan/phpstan": "^1.4",
"phpstan/phpstan-mockery": "^1",
"phpstan/phpstan-phpunit": "^1",
"phpunit/phpunit": "^9.5.10",
"squizlabs/php_codesniffer": "^3.6"
},
Expand Down Expand Up @@ -69,8 +72,14 @@
}
},
"scripts": {
"check-style": "phpcs -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests",
"fix-style": "phpcbf -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests",
"all": [
"composer normalize",
"@fix",
"@test",
"@stan"
],
"fix": "phpcbf -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests",
"stan": "phpstan",
"test": "phpunit"
}
}
17 changes: 17 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
includes:
- extension.neon
- vendor/nunomaduro/larastan/extension.neon
- vendor/phpstan/phpstan/conf/bleedingEdge.neon
- vendor/phpstan/phpstan-mockery/extension.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
parameters:
# TODO level up to max
level: 2
paths:
- src
- tests
checkOctaneCompatibility: true
ignoreErrors:
# This is a library, so it should be extendable
- '#Unsafe usage of new static.*#'
6 changes: 3 additions & 3 deletions src/Enum.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ abstract class Enum implements EnumContract, Castable, Arrayable, JsonSerializab
/**
* The key of one of the enum members.
*
* @var mixed
* @var string
*/
public $key;

/**
* The description of one of the enum members.
*
* @var mixed
* @var string
*/
public $description;

/**
* Caches reflections of enum subclasses.
*
* @var array<class-string<static>, ReflectionClass<static>
* @var array<class-string<static>, ReflectionClass<static>>
*/
protected static $reflectionCache = [];

Expand Down
7 changes: 7 additions & 0 deletions src/FlaggedEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

abstract class FlaggedEnum extends Enum
{
/**
* The value of one of the enum members.
*
* @var int
*/
public $value;

const None = 0;

/**
Expand Down
2 changes: 2 additions & 0 deletions src/Rules/EnumValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ public function passes($attribute, $value)
if (is_subclass_of($this->enumClass, FlaggedEnum::class) && (is_integer($value) || ctype_digit($value))) {
// Unset all possible flag values
foreach ($this->enumClass::getValues() as $enumValue) {
assert(is_int($enumValue), 'Flagged enum values must be int');
$value &= ~$enumValue;
}
// All bits should be unset
return $value === 0;
}

return $this->enumClass::hasValue($value, $this->strict);
}

Expand Down
12 changes: 6 additions & 6 deletions tests/EnumCastTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ class EnumCastTest extends ApplicationTestCase
{
public function test_can_set_model_value_using_enum_instance()
{
$model = app(Example::class);
$model = new Example;
$model->user_type = UserType::Moderator();

$this->assertEquals(UserType::Moderator(), $model->user_type);
}

public function test_can_set_model_value_using_enum_value()
{
$model = app(Example::class);
$model = new Example;
$model->user_type = UserType::Moderator;

$this->assertEquals(UserType::Moderator(), $model->user_type);
Expand All @@ -28,29 +28,29 @@ public function test_cannot_set_model_value_using_invalid_enum_value()
{
$this->expectException(InvalidEnumMemberException::class);

$model = app(Example::class);
$model = new Example;
$model->user_type = 5;
}

public function test_getting_model_value_returns_enum_instance()
{
$model = app(Example::class);
$model = new Example;
$model->user_type = UserType::Moderator;

$this->assertInstanceOf(UserType::class, $model->user_type);
}

public function test_can_get_and_set_null_on_enum_castable()
{
$model = app(Example::class);
$model = new Example;
$model->user_type = null;

$this->assertNull($model->user_type);
}

public function test_that_model_with_enum_can_be_cast_to_array()
{
$model = app(Example::class);
$model = new Example;
$model->user_type = UserType::Moderator();

$this->assertSame(['user_type' => 1], $model->toArray());
Expand Down
1 change: 1 addition & 0 deletions tests/EnumInstanceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public function test_an_exception_is_thrown_when_trying_to_get_enum_instance_by_
{
$this->expectException(InvalidEnumKeyException::class);

// @phpstan-ignore-next-line intentionally wrong
UserType::KeyWhichDoesNotExist();
}

Expand Down
26 changes: 19 additions & 7 deletions tests/EnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,24 +173,36 @@ public function test_enum_as_select_array_with_string_values()

public function test_enum_is_macroable_with_static_methods()
{
Enum::macro('asFlippedArray', function () {
$name = 'asFlippedArray';

Enum::macro($name, function () {
// @phpstan-ignore-next-line self is rebound to Enum
return array_flip(self::asArray());
});

$this->assertTrue(UserType::hasMacro('asFlippedArray'));
$this->assertEquals(UserType::asFlippedArray(), array_flip(UserType::asArray()));
$this->assertTrue(UserType::hasMacro($name));

$reimplementedResult = array_flip(UserType::asArray());
// @phpstan-ignore-next-line TODO make extension recognize macro
$macroResult = UserType::asFlippedArray();
$this->assertEquals($reimplementedResult, $macroResult);
}

public function test_enum_is_macroable_with_instance_methods()
{
Enum::macro('macroGetValue', function () {
$name = 'macroGetValue';
Enum::macro($name, function () {
// @phpstan-ignore-next-line $this is rebound to Enum
return $this->value;
});

$this->assertTrue(UserType::hasMacro('macroGetValue'));
$this->assertTrue(UserType::hasMacro($name));

$user = new UserType(UserType::Administrator);
$this->assertSame(UserType::Administrator, $user->macroGetValue());
$value = UserType::Administrator;
$user = new UserType($value);
// @phpstan-ignore-next-line TODO make extension recognize macro
$valueFromMacro = $user->macroGetValue();
$this->assertSame($value, $valueFromMacro);
}

public function test_enum_get_instances()
Expand Down
1 change: 1 addition & 0 deletions tests/Enums/DescriptionFromAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ final class DescriptionFromAttribute extends Enum
const SuperAdministrator = 3;

#[Description('First description')]
// @phpstan-ignore-next-line intentionally wrong
#[Description('Second description')]
const InvalidCaseWithMultipleDescriptions = 4;

Expand Down
3 changes: 3 additions & 0 deletions tests/Models/Example.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
use BenSampo\Enum\Tests\Enums\UserType;
use Illuminate\Database\Eloquent\Model;

/**
* @property UserType $user_type
*/
class Example extends Model
{
public $timestamps = false;
Expand Down
4 changes: 4 additions & 0 deletions tests/Models/NativeCastModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
use BenSampo\Enum\Tests\Enums\UserTypeCustomCast;
use Illuminate\Database\Eloquent\Model;

/**
* @property UserType $user_type
* @property UserTypeCustomCast $user_type_custom
*/
class NativeCastModel extends Model
{
protected $casts = [
Expand Down
Loading