Skip to content

Commit

Permalink
implement PHP 8.1 Enum support [closes #585]
Browse files Browse the repository at this point in the history
  • Loading branch information
hrach committed Apr 15, 2023
1 parent dc5570a commit 128f8c4
Show file tree
Hide file tree
Showing 15 changed files with 93 additions and 78 deletions.
2 changes: 0 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"nette/di": "^3.1",
"nette/neon": "~3.0",
"nette/tester": "~2.5",
"marc-mabe/php-enum": "~4.6",
"mockery/mockery": ">=1.5.1",
"phpstan/extension-installer": "1.2.0",
"phpstan/phpstan": "1.10.9",
Expand All @@ -41,7 +40,6 @@
"phpstan/phpstan-strict-rules": "1.4.5",
"nextras/multi-query-parser": "~1.0",
"nextras/orm-phpstan": "~1.0@dev",
"marc-mabe/php-enum-phpstan": "dev-master",
"tracy/tracy": "~2.3"
},
"autoload": {
Expand Down
2 changes: 1 addition & 1 deletion src/Entity/Embeddable/Embeddable.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ private function createPropertyWrapper(PropertyMetadata $metadata): IProperty

if ($wrapper instanceof IEntityAwareProperty) {
if ($this->parentEntity === null) {
throw new InvalidStateException("");
throw new InvalidStateException("Embeddable cannot contain a property having IEntityAwareProperty wrapper because embeddable is instanced before setting/attaching to its entity.");
} else {
$wrapper->onEntityAttach($this->parentEntity);
}
Expand Down
5 changes: 5 additions & 0 deletions src/Entity/Reflection/MetadataParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
namespace Nextras\Orm\Entity\Reflection;


use BackedEnum;
use DateTime;
use Nette\Utils\Reflection;
use Nextras\Orm\Collection\ICollection;
use Nextras\Orm\Entity\Embeddable\EmbeddableContainer;
use Nextras\Orm\Entity\Embeddable\IEmbeddable;
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Entity\IProperty;
use Nextras\Orm\Entity\Wrapper\BackedEnumWrapper;
use Nextras\Orm\Exception\InvalidStateException;
use Nextras\Orm\Exception\NotSupportedException;
use Nextras\Orm\Relationships\HasMany;
Expand Down Expand Up @@ -241,6 +243,9 @@ protected function parseAnnotationTypes(PropertyMetadata $property, string $type
if ($type === DateTime::class || is_subclass_of($type, DateTime::class)) {
throw new NotSupportedException("Type '{$type}' in {$this->currentReflection->name}::\${$property->name} property is not supported anymore. Use \DateTimeImmutable or \Nextras\Dbal\Utils\DateTimeImmutable type.");
}
if (is_subclass_of($type, BackedEnum::class)) {
$property->wrapper = BackedEnumWrapper::class;
}
}
$parsedTypes[$type] = true;
}
Expand Down
50 changes: 50 additions & 0 deletions src/Entity/Wrapper/BackedEnumWrapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php declare(strict_types = 1);

namespace Nextras\Orm\Entity\Wrapper;


use BackedEnum;
use Nextras\Orm\Entity\ImmutableValuePropertyWrapper;
use Nextras\Orm\Exception\NullValueException;
use function array_key_first;
use function assert;
use function is_int;
use function is_string;
use function is_subclass_of;


final class BackedEnumWrapper extends ImmutableValuePropertyWrapper
{
public function setInjectedValue($value): bool
{
if ($value === null && !$this->propertyMetadata->isNullable) {
throw new NullValueException($this->propertyMetadata);
}

return parent::setInjectedValue($value);
}


public function convertToRawValue(mixed $value): mixed
{
if ($value === null) return null;
$type = array_key_first($this->propertyMetadata->types);
assert($value instanceof $type);
assert($value instanceof BackedEnum);
return $value->value;
}


public function convertFromRawValue(mixed $value): ?BackedEnum
{
if ($value === null) {
if ($this->propertyMetadata->isNullable) return null;
throw new NullValueException($this->propertyMetadata);
}

assert(is_int($value) || is_string($value));
$type = array_key_first($this->propertyMetadata->types);
assert(is_subclass_of($type, BackedEnum::class));
return $type::from($value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class CollectionEmbeddablesTest extends DataTestCase
Assert::same(0, $books1->countStored());

$book = $this->orm->books->getByIdChecked(1);
$book->price = new Money(1000, Currency::CZK());
$book->price = new Money(1000, Currency::CZK);
$this->orm->persistAndFlush($book);

$books2 = $this->orm->books->findBy(['price->cents>=' => 1000]);
Expand Down
8 changes: 4 additions & 4 deletions tests/cases/integration/Collection/collection.where.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -98,25 +98,25 @@ class CollectionWhereTest extends DataTestCase

public function testFilterByPropertyWrapper(): void
{
$ean8 = new Ean(EanType::EAN8());
$ean8 = new Ean(EanType::EAN8);
$ean8->code = '123';
$ean8->book = $this->orm->books->getByIdChecked(1);
$this->orm->persist($ean8);

$ean13 = new Ean(EanType::EAN13());
$ean13 = new Ean(EanType::EAN13);
$ean13->code = '456';
$ean13->book = $this->orm->books->getByIdChecked(2);
$this->orm->persistAndFlush($ean13);

Assert::count(2, $this->orm->eans->findAll());

$eans = $this->orm->eans->findBy(['type' => EanType::EAN8()]);
$eans = $this->orm->eans->findBy(['type' => EanType::EAN8]);
Assert::count(1, $eans);
$fetched = $eans->fetch();
Assert::notNull($fetched);
Assert::equal('123', $fetched->code);

$eans = $this->orm->eans->findBy(['type' => EanType::EAN13()]);
$eans = $this->orm->eans->findBy(['type' => EanType::EAN13]);
Assert::count(1, $eans);
$fetched = $eans->fetch();
Assert::notNull($fetched);
Expand Down
14 changes: 7 additions & 7 deletions tests/cases/integration/Entity/entity.embeddable.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ class EntityEmbeddableTest extends DataTestCase
public function testBasic(): void
{
$book = $this->orm->books->getByIdChecked(1);
$book->price = new Money(1000, Currency::CZK());
$book->price = new Money(1000, Currency::CZK);
Assert::same(1000, $book->price->cents);
Assert::same(Currency::CZK(), $book->price->currency);
Assert::same(Currency::CZK, $book->price->currency);

$this->orm->persistAndFlush($book);
$this->orm->clear();
Expand All @@ -36,7 +36,7 @@ class EntityEmbeddableTest extends DataTestCase

Assert::notNull($book->price);
Assert::same(1000, $book->price->cents);
Assert::same(Currency::CZK(), $book->price->currency);
Assert::same(Currency::CZK, $book->price->currency);

$book->price = null;
$this->orm->persistAndFlush($book);
Expand All @@ -50,8 +50,8 @@ class EntityEmbeddableTest extends DataTestCase
public function testMultiple(): void
{
$book = $this->orm->books->getByIdChecked(1);
$book->price = new Money(1000, Currency::CZK());
$book->origPrice = new Money(330, Currency::EUR());
$book->price = new Money(1000, Currency::CZK);
$book->origPrice = new Money(330, Currency::EUR);

$this->orm->persistAndFlush($book);
$this->orm->clear();
Expand All @@ -73,7 +73,7 @@ class EntityEmbeddableTest extends DataTestCase
Assert::throws(function (): void {
$book = new Book();
// @phpstan-ignore-next-line
$book->price = (object) ['price' => 100, 'currency' => Currency::CZK()];
$book->price = (object) ['price' => 100, 'currency' => Currency::CZK];
}, InvalidArgumentException::class);
}

Expand All @@ -82,7 +82,7 @@ class EntityEmbeddableTest extends DataTestCase
{
$book = $this->orm->books->getByIdChecked(1);

$book->price = new Money(1000, Currency::CZK());
$book->price = new Money(1000, Currency::CZK);
Assert::same(1000, $book->price->cents);

$book->price = null;
Expand Down
6 changes: 6 additions & 0 deletions tests/cases/integration/Entity/entity.nullValidation.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use Nextras\Orm\Exception\InvalidArgumentException;
use Nextras\Orm\Exception\InvalidStateException;
use Nextras\Orm\Exception\NullValueException;
use NextrasTests\Orm\Book;
use NextrasTests\Orm\Ean;
use NextrasTests\Orm\TestCase;
use Tester\Assert;

Expand All @@ -32,6 +33,11 @@ class EntityNullValidationTest extends TestCase
$book->author = null; // @phpstan-ignore-line
}, NullValueException::class, 'Property NextrasTests\Orm\Book::$author is not nullable.');

Assert::throws(function (): void {
$ean = new Ean();
$ean->type = null; // @phpstan-ignore-line
}, NullValueException::class, 'Property NextrasTests\Orm\Ean::$type is not nullable.');

$book = new Book();
$book->translator = null;
}
Expand Down
4 changes: 2 additions & 2 deletions tests/cases/unit/Collection/FetchPairsHelperTest.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,11 @@ class FetchPairsHelperTest extends TestCase
$data = new ArrayIterator([
$this->e(
Book::class,
['price' => new Money(100, Currency::CZK())]
['price' => new Money(100, Currency::CZK)]
),
$this->e(
Book::class,
['price' => new Money(200, Currency::CZK())]
['price' => new Money(200, Currency::CZK)]
),
]);
Assert::same(
Expand Down
8 changes: 4 additions & 4 deletions tests/db/array-data.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
$book1->translator = $author1;
$book1->publisher = $publisher1;
$book1->publishedAt = new \DateTimeImmutable('2021-12-14 21:10:04');
$book1->price = new Money(50, Currency::CZK());
$book1->price = new Money(50, Currency::CZK);
$book1->tags->set([$tag1, $tag2]);
$orm->books->persist($book1);

Expand All @@ -53,7 +53,7 @@
$book2->author = $author1;
$book2->publisher = $publisher2;
$book2->publishedAt = new \DateTimeImmutable('2021-12-14 21:10:02');
$book2->price = new Money(150, Currency::CZK());
$book2->price = new Money(150, Currency::CZK);
$book2->tags->set([$tag2, $tag3]);
$orm->books->persist($book2);

Expand All @@ -63,7 +63,7 @@
$book3->translator = $author2;
$book3->publisher = $publisher3;
$book3->publishedAt = new \DateTimeImmutable('2021-12-14 21:10:03');
$book3->price = new Money(20, Currency::CZK());
$book3->price = new Money(20, Currency::CZK);
$book3->tags->set([$tag3]);
$orm->books->persist($book3);

Expand All @@ -74,7 +74,7 @@
$book4->publisher = $publisher1;
$book4->nextPart = $book3;
$book4->publishedAt = new \DateTimeImmutable('2021-12-14 21:10:01');
$book4->price = new Money(220, Currency::CZK());
$book4->price = new Money(220, Currency::CZK);
$orm->books->persist($book4);

$tagFollower1 = new TagFollower();
Expand Down
13 changes: 5 additions & 8 deletions tests/inc/Currency.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@
namespace NextrasTests\Orm;


use MabeEnum\Enum;


class Currency extends Enum
enum Currency: string
{
const CZK = 'CZK';
const EUR = 'EUR';
const GBP = 'GBP';
const USD = 'USD';
case CZK = 'CZK';
case EUR = 'EUR';
case GBP = 'GBP';
case USD = 'USD';
}
2 changes: 1 addition & 1 deletion tests/inc/Money.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

/**
* @property-read int $cents
* @property-read Currency $currency {wrapper TestEnumPropertyWrapper}
* @property-read Currency $currency
*/
class Money extends Embeddable
{
Expand Down
38 changes: 0 additions & 38 deletions tests/inc/TestEnumPropertyWrapper.php

This file was deleted.

6 changes: 3 additions & 3 deletions tests/inc/model/ean/Ean.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
* @property int|null $id {primary}
* @property string $code
* @property Book $book {1:1 Book::$ean}
* @property EanType $type {wrapper TestEnumPropertyWrapper}
* @property EanType $type
*/
class Ean extends Entity
{
public function __construct(EanType $type = null)
public function __construct(EanType $type = EanType::EAN8)
{
parent::__construct();
$this->type = $type ?? EanType::EAN8();
$this->type = $type;
}
}
11 changes: 4 additions & 7 deletions tests/inc/model/ean/EanType.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@
namespace NextrasTests\Orm;


use MabeEnum\Enum;


class EanType extends Enum
enum EanType: int
{
const EAN13 = 1;
const EAN8 = 2;
const CODE39 = 3;
case EAN13 = 1;
case EAN8 = 2;
case CODE39 = 3;
}

0 comments on commit 128f8c4

Please sign in to comment.