From 5a05513e2da0536709843a584dd91bc22ba73ec4 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Thu, 10 Nov 2022 13:54:12 -0500 Subject: [PATCH 01/30] [feature] require php8+ --- .github/workflows/ci.yml | 20 ++++++++------------ composer.json | 10 ++-------- docker/Dockerfile | 2 +- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 268cc23c3..3cfb64f62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php: [7.2, 7.4, 8.0, 8.1] + php: [8.0, 8.1] symfony: [4.4.*, 5.4.*, 6.0.*, 6.1.*] deps: [highest] use-orm: [1] @@ -23,15 +23,11 @@ jobs: - {use-orm: 0, use-odm: 0} # tested directly in a test case - {use-orm: 0, use-dama: 1} # cannot happen # conflicts - - {php: 7.2, symfony: 6.0.*} - - {php: 7.2, symfony: 6.1.*} - - {php: 7.4, symfony: 6.0.*} - - {php: 7.4, symfony: 6.1.*} - {php: 8.0, symfony: 6.1.*} include: - - {php: 7.2, symfony: 4.4.*, use-orm: 1, use-odm: 0, use-dama: 0, deps: lowest} - - {php: 7.2, symfony: 4.4.*, use-orm: 1, use-odm: 1, use-dama: 0, deps: lowest} - - {php: 7.2, symfony: 4.4.*, use-orm: 0, use-odm: 1, use-dama: 0, deps: lowest} + - {php: 8.0, symfony: 4.4.*, use-orm: 1, use-odm: 0, use-dama: 0, deps: lowest} + - {php: 8.0, symfony: 4.4.*, use-orm: 1, use-odm: 1, use-dama: 0, deps: lowest} + - {php: 8.0, symfony: 4.4.*, use-orm: 0, use-odm: 1, use-dama: 0, deps: lowest} - {php: 8.1, symfony: 6.1.*, use-orm: 1, use-odm: 0, use-dama: 0, deps: highest} - {php: 8.1, symfony: 6.1.*, use-orm: 1, use-odm: 1, use-dama: 0, deps: highest} - {php: 8.1, symfony: 6.1.*, use-orm: 1, use-odm: 0, use-dama: 1, deps: highest} @@ -155,7 +151,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.0 coverage: none - name: Install dependencies @@ -176,7 +172,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@2.7.0 with: - php-version: 7.2 + php-version: 8.0 coverage: none - name: Install dependencies @@ -200,7 +196,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@2.7.0 with: - php-version: 7.2 + php-version: 8.0 coverage: none - name: Install dependencies @@ -212,7 +208,7 @@ jobs: run: composer bin psalm install - name: Run static analysis - run: bin/tools/psalm/vendor/vimeo/psalm/psalm --output-format=github --php-version=7.2 + run: bin/tools/psalm/vendor/vimeo/psalm/psalm --output-format=github docker-stack: name: CI with docker stack diff --git a/composer.json b/composer.json index 9c327082e..10efaa0d4 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ } ], "require": { - "php": ">=7.2.12", + "php": ">=8.0", "doctrine/persistence": "^1.3.3|^2.0|^3.0", "fakerphp/faker": "^1.5", "symfony/deprecation-contracts": "^2.2|^3.0", @@ -60,11 +60,5 @@ } }, "minimum-stability": "dev", - "prefer-stable": true, - "scripts": { - "auto-scripts": { - "cache:clear": "symfony-cmd", - "assets:install %PUBLIC_DIR%": "symfony-cmd" - } - } + "prefer-stable": true } diff --git a/docker/Dockerfile b/docker/Dockerfile index 8d95ebd1b..a44206f07 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM php:7.2-cli +FROM php:8.0-cli COPY --from=composer:2.3 /usr/bin/composer /usr/bin/composer From 8423b7576d1108a7f4a93c15daa3146801526721 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Thu, 10 Nov 2022 13:56:01 -0500 Subject: [PATCH 02/30] [chore] adjust `.symfony.bundle.yaml` for new branch (#325) --- .symfony.bundle.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.symfony.bundle.yaml b/.symfony.bundle.yaml index 10f9fcfec..8ab8c0051 100644 --- a/.symfony.bundle.yaml +++ b/.symfony.bundle.yaml @@ -1,6 +1,3 @@ -branches: ['master'] -maintained_branches: ['master'] -current_branch: "master" -dev_branch: "master" -doc_dir: 'docs/' -dev_branch_alias: '1.x' +branches: ["1.x", "1.23.x"] +maintained_branches: ["1.x", "1.23.x"] +doc_dir: "docs/" From 51f1bc0c1b8016d4a1b711ca2e265f0255ef25e0 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Fri, 11 Nov 2022 14:42:31 +0100 Subject: [PATCH 03/30] [refactor] modernize code with rector --- psalm.xml | 7 ++ src/AnonymousFactory.php | 12 ++- .../DependencyInjection/GlobalStatePass.php | 5 +- src/Bundle/Maker/MakeFactory.php | 18 ++-- src/Bundle/Maker/MakeStory.php | 3 +- src/Bundle/Resources/skeleton/Factory.tpl.php | 2 +- src/ChainManagerRegistry.php | 33 ++++---- src/Configuration.php | 69 ++++++--------- src/Factory.php | 67 ++++++--------- src/FactoryCollection.php | 39 ++++----- src/Instantiator.php | 27 ++---- src/ModelFactory.php | 22 ++--- src/ModelFactoryManager.php | 7 +- src/Proxy.php | 35 +++----- src/RepositoryAssertions.php | 5 +- src/RepositoryProxy.php | 25 +++--- src/Story.php | 8 +- src/StoryManager.php | 15 ++-- src/Test/AbstractSchemaResetter.php | 2 +- src/Test/DatabaseResetter.php | 3 +- src/Test/Factories.php | 3 +- src/Test/GlobalStateRegistry.php | 26 +++--- src/Test/LazyManagerRegistry.php | 12 +++ src/Test/ODMSchemaResetter.php | 16 ++-- src/Test/ORMDatabaseResetter.php | 19 ++--- src/Test/ResetDatabase.php | 2 +- src/Test/TestState.php | 8 +- tests/Fixtures/Document/Category.php | 2 +- tests/Fixtures/Document/Comment.php | 2 +- tests/Fixtures/Document/Post.php | 4 +- tests/Fixtures/Document/User.php | 4 +- tests/Fixtures/Entity/Address.php | 3 +- tests/Fixtures/Entity/Cascade/Brand.php | 6 +- tests/Fixtures/Entity/Cascade/Category.php | 6 +- tests/Fixtures/Entity/Cascade/Image.php | 4 +- tests/Fixtures/Entity/Cascade/Product.php | 18 ++-- tests/Fixtures/Entity/Cascade/Review.php | 6 +- tests/Fixtures/Entity/Cascade/Tag.php | 6 +- tests/Fixtures/Entity/Cascade/Variant.php | 8 +- tests/Fixtures/Entity/Category.php | 22 ++--- tests/Fixtures/Entity/Comment.php | 18 ++-- tests/Fixtures/Entity/Contact.php | 2 +- tests/Fixtures/Entity/Post.php | 83 ++++++++++--------- tests/Fixtures/Entity/SpecificPost.php | 3 +- tests/Fixtures/Entity/Tag.php | 22 ++--- tests/Fixtures/Entity/User.php | 13 +-- .../Factories/CategoryServiceFactory.php | 6 +- tests/Fixtures/Factories/ODM/PostFactory.php | 22 ++--- tests/Fixtures/Factories/PostFactory.php | 6 +- tests/Fixtures/Kernel.php | 9 +- .../Migrations/Version20220925092226.php | 5 ++ .../Migrations/Version20220925092634.php | 7 +- .../Migrations/Version20221010154036.php | 7 +- tests/Fixtures/Service.php | 2 +- tests/Fixtures/Stories/ServiceStory.php | 6 +- tests/Functional/AnonymousFactoryTest.php | 5 +- .../Bundle/Maker/MakeFactoryTest.php | 4 +- .../Functional/Bundle/Maker/MakeStoryTest.php | 10 ++- tests/Functional/FactoryTest.php | 19 ++--- tests/Functional/GlobalStateTest.php | 2 - tests/Functional/ModelFactoryTest.php | 20 ++--- tests/Functional/ORMDatabaseResetterTest.php | 1 + tests/Functional/ORMRepositoryProxyTest.php | 4 +- tests/Functional/ProxyTest.php | 10 +-- tests/Functional/RepositoryProxyTest.php | 38 ++++++--- tests/Functional/StoryTest.php | 2 +- .../ZenstruckFoundryExtensionTest.php | 3 + tests/Unit/FactoryTest.php | 42 ++++------ tests/Unit/InstantiatorTest.php | 37 +++++---- tests/Unit/ProxyTest.php | 32 +++---- tests/Unit/RepositoryProxyTest.php | 8 +- 71 files changed, 492 insertions(+), 537 deletions(-) diff --git a/psalm.xml b/psalm.xml index 0c094f680..e6c7761a7 100644 --- a/psalm.xml +++ b/psalm.xml @@ -31,8 +31,15 @@ + + + + + + + diff --git a/src/AnonymousFactory.php b/src/AnonymousFactory.php index de255628e..bd12f2496 100644 --- a/src/AnonymousFactory.php +++ b/src/AnonymousFactory.php @@ -80,13 +80,15 @@ public function randomOrCreate(array $attributes = []): Proxy { try { return $this->repository()->random($attributes); - } catch (\RuntimeException $e) { + } catch (\RuntimeException) { return $this->create($attributes); } } /** * @see RepositoryProxy::randomSet() + * + * @return object[] */ public function randomSet(int $number, array $attributes = []): array { @@ -95,6 +97,8 @@ public function randomSet(int $number, array $attributes = []): array /** * @see RepositoryProxy::randomRange() + * + * @return object[] */ public function randomRange(int $min, int $max, array $attributes = []): array { @@ -109,7 +113,7 @@ public function count(): int return $this->repository()->count(); } - public function getIterator(): \Traversable + public function getIterator(): \ArrayIterator { return new \ArrayIterator($this->all()); } @@ -124,6 +128,8 @@ public function truncate(): void /** * @see RepositoryProxy::findAll() + * + * @return object[] */ public function all(): array { @@ -146,6 +152,8 @@ public function find($criteria): Proxy /** * @see RepositoryProxy::findBy() + * + * @return object[] */ public function findBy(array $attributes): array { diff --git a/src/Bundle/DependencyInjection/GlobalStatePass.php b/src/Bundle/DependencyInjection/GlobalStatePass.php index a47a7a696..b46841c67 100644 --- a/src/Bundle/DependencyInjection/GlobalStatePass.php +++ b/src/Bundle/DependencyInjection/GlobalStatePass.php @@ -51,7 +51,7 @@ private function isStoryAsService(ContainerBuilder $container, string $globalSta $globalStateItemDefinition = $container->getDefinition($globalStateItem); - return \count($globalStateItemDefinition->getTag('foundry.story')) > 0; + return [] !== $globalStateItemDefinition->getTag('foundry.story'); } private function isInvokableService(ContainerBuilder $container, string $globalStateItem): bool @@ -74,6 +74,9 @@ private function isStandaloneStory(ContainerBuilder $container, string $globalSt return \class_exists($globalStateItem) && \is_a($globalStateItem, Story::class, true); } + /** + * @return mixed[] + */ private function getBundleConfiguration(ContainerBuilder $container): array { return (new Processor())->processConfiguration(new Configuration(), $container->getExtensionConfig('zenstruck_foundry')); diff --git a/src/Bundle/Maker/MakeFactory.php b/src/Bundle/Maker/MakeFactory.php index 9967ff410..ebaa15fa3 100644 --- a/src/Bundle/Maker/MakeFactory.php +++ b/src/Bundle/Maker/MakeFactory.php @@ -47,19 +47,13 @@ final class MakeFactory extends AbstractMaker 'TIME_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', ]; - /** @var ManagerRegistry */ - private $managerRegistry; - /** @var string[] */ - private $entitiesWithFactories; + private array $entitiesWithFactories = []; - public function __construct(ManagerRegistry $managerRegistry, \Traversable $factories) + public function __construct(private ManagerRegistry $managerRegistry, \Traversable $factories) { - $this->managerRegistry = $managerRegistry; $this->entitiesWithFactories = \array_map( - static function(ModelFactory $factory) { - return $factory::getEntityClass(); - }, + static fn(ModelFactory $factory): string => $factory::getEntityClass(), \iterator_to_array($factories) ); } @@ -127,7 +121,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen /** * Generates a single entity factory. */ - private function generateFactory(string $class, InputInterface $input, ConsoleStyle $io, Generator $generator) + private function generateFactory(string $class, InputInterface $input, ConsoleStyle $io, Generator $generator): void { if (!\class_exists($class)) { $class = $generator->createClassNameDetails($class, 'Entity\\')->getFullName(); @@ -181,6 +175,9 @@ private function generateFactory(string $class, InputInterface $input, ConsoleSt ]); } + /** + * @return class-string[] + */ private function entityChoices(): array { $choices = []; @@ -190,6 +187,7 @@ private function entityChoices(): array if ($metadata->getReflectionClass()->isAbstract()) { continue; } + if (!\in_array($metadata->getName(), $this->entitiesWithFactories, true)) { $choices[] = $metadata->getName(); } diff --git a/src/Bundle/Maker/MakeStory.php b/src/Bundle/Maker/MakeStory.php index a332a635a..79a661063 100644 --- a/src/Bundle/Maker/MakeStory.php +++ b/src/Bundle/Maker/MakeStory.php @@ -7,7 +7,6 @@ use Symfony\Bundle\MakerBundle\Generator; use Symfony\Bundle\MakerBundle\InputConfiguration; use Symfony\Bundle\MakerBundle\Maker\AbstractMaker; -use Symfony\Bundle\MakerBundle\Validator; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -51,7 +50,7 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma } $argument = $command->getDefinition()->getArgument('name'); - $value = $io->ask($argument->getDescription(), null, [Validator::class, 'notBlank']); + $value = $io->ask($argument->getDescription(), null, static fn(?string $value = null): string => \Symfony\Bundle\MakerBundle\Validator::notBlank($value)); $input->setArgument($argument->getName(), $value); } diff --git a/src/Bundle/Resources/skeleton/Factory.tpl.php b/src/Bundle/Resources/skeleton/Factory.tpl.php index 4cc230b3e..4b24d8a95 100644 --- a/src/Bundle/Resources/skeleton/Factory.tpl.php +++ b/src/Bundle/Resources/skeleton/Factory.tpl.php @@ -54,7 +54,7 @@ protected function initialize(): self { // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization return $this - // ->afterInstantiate(function(getShortName() ?> $getShortName()) ?>): void {}) + // ->afterInstantiate(function(getShortName() ?> $getShortName()) ?>): void {}) ; } diff --git a/src/ChainManagerRegistry.php b/src/ChainManagerRegistry.php index fbd4c4acd..1eae9fc52 100644 --- a/src/ChainManagerRegistry.php +++ b/src/ChainManagerRegistry.php @@ -12,13 +12,9 @@ */ final class ChainManagerRegistry implements ManagerRegistry { - /** @var list */ - private $managerRegistries; - /** @param list $managerRegistries */ - public function __construct(array $managerRegistries) + public function __construct(private array $managerRegistries) { - $this->managerRegistries = $managerRegistries; } public function getRepository($persistentObject, $persistentManagerName = null): ObjectRepository @@ -28,7 +24,7 @@ public function getRepository($persistentObject, $persistentManagerName = null): if ($repository = $managerRegistry->getRepository($persistentObject, $persistentManagerName)) { return $repository; } - } catch (MappingException $exception) { + } catch (MappingException) { // the class is not managed by the current manager } } @@ -47,58 +43,59 @@ public function getManagerForClass($class): ?ObjectManager return null; } + /** + * @return array + */ public function getManagers(): array { return \array_reduce( $this->managerRegistries, - static function(array $carry, ManagerRegistry $managerRegistry) { - return \array_merge($carry, \array_values($managerRegistry->getManagers())); - }, + static fn(array $carry, ManagerRegistry $managerRegistry): array => \array_merge($carry, \array_values($managerRegistry->getManagers())), [] ); } - public function getDefaultConnectionName(): string + public function getDefaultConnectionName(): void { throw new \BadMethodCallException('Not available in '.self::class); } - public function getConnection($name = null): object + public function getConnection($name = null): void { throw new \BadMethodCallException('Not available in '.self::class); } - public function getConnections(): array + public function getConnections(): void { throw new \BadMethodCallException('Not available in '.self::class); } - public function getConnectionNames(): array + public function getConnectionNames(): void { throw new \BadMethodCallException('Not available in '.self::class); } - public function getDefaultManagerName(): string + public function getDefaultManagerName(): void { throw new \BadMethodCallException('Not available in '.self::class); } - public function getManager($name = null): ObjectManager + public function getManager($name = null): void { throw new \BadMethodCallException('Not available in '.self::class); } - public function resetManager($name = null): ObjectManager + public function resetManager($name = null): void { throw new \BadMethodCallException('Not available in '.self::class); } - public function getAliasNamespace($alias): string + public function getAliasNamespace($alias): void { throw new \BadMethodCallException('Not available in '.self::class); } - public function getManagerNames(): array + public function getManagerNames(): void { throw new \BadMethodCallException('Not available in '.self::class); } diff --git a/src/Configuration.php b/src/Configuration.php index ae0bbaf88..75648335c 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -14,53 +14,34 @@ */ final class Configuration { - /** @var ManagerRegistry|null */ - private $managerRegistry; + private ?ManagerRegistry $managerRegistry = null; - /** @var StoryManager */ - private $stories; + private StoryManager $stories; - /** @var ModelFactoryManager */ - private $factories; + private ModelFactoryManager $factories; - /** @var Faker\Generator */ - private $faker; + private \Faker\Generator $faker; /** @var callable */ private $instantiator; - /** @var bool|null */ - private $defaultProxyAutoRefresh; + private ?bool $defaultProxyAutoRefresh = null; - /** @var bool */ - private $flushEnabled = true; + private bool $flushEnabled = true; - /** @var bool */ - private $databaseResetEnabled = true; + private bool $databaseResetEnabled = true; - /** @var list */ - private $ormConnectionsToReset; - - /** @var list */ - private $ormObjectManagersToReset; - - /** @var string */ - private $ormResetMode; - - /** @var list */ - private $odmObjectManagersToReset; - - public function __construct(array $ormConnectionsToReset, array $ormObjectManagersToReset, string $ormResetMode, array $odmObjectManagersToReset) + /** + * @param string[] $ormConnectionsToReset + * @param string[] $ormObjectManagersToReset + * @param string[] $odmObjectManagersToReset + */ + public function __construct(private array $ormConnectionsToReset, private array $ormObjectManagersToReset, private string $ormResetMode, private array $odmObjectManagersToReset) { $this->stories = new StoryManager([]); $this->factories = new ModelFactoryManager([]); $this->faker = Faker\Factory::create(); $this->instantiator = new Instantiator(); - - $this->ormConnectionsToReset = $ormConnectionsToReset; - $this->ormObjectManagersToReset = $ormObjectManagersToReset; - $this->ormResetMode = $ormResetMode; - $this->odmObjectManagersToReset = $odmObjectManagersToReset; } public function stories(): StoryManager @@ -178,33 +159,28 @@ public function delayFlush(callable $callback): void } /** - * @param object|string $objectOrClass - * * @psalm-suppress InvalidReturnType * @psalm-suppress InvalidReturnStatement * @template TObject of object * @psalm-param Proxy|TObject|class-string $objectOrClass * @psalm-return RepositoryProxy */ - public function repositoryFor($objectOrClass): RepositoryProxy + public function repositoryFor(object|string $objectOrClass): RepositoryProxy { if ($objectOrClass instanceof Proxy) { $objectOrClass = $objectOrClass->object(); } if (!\is_string($objectOrClass)) { - $objectOrClass = \get_class($objectOrClass); + $objectOrClass = $objectOrClass::class; } return new RepositoryProxy($this->managerRegistry()->getRepository($objectOrClass)); } - /** - * @param object|string $objectOrClass - */ - public function objectManagerFor($objectOrClass): ObjectManager + public function objectManagerFor(object|string $objectOrClass): ObjectManager { - $class = \is_string($objectOrClass) ? $objectOrClass : \get_class($objectOrClass); + $class = \is_string($objectOrClass) ? $objectOrClass : $objectOrClass::class; if (!$objectManager = $this->managerRegistry()->getManagerForClass($class)) { throw new \RuntimeException(\sprintf('No object manager registered for "%s".', $class)); @@ -219,11 +195,17 @@ public function hasManagerRegistry(): bool return null !== $this->managerRegistry; } + /** + * @return string[] + */ public function getOrmConnectionsToReset(): array { return $this->ormConnectionsToReset; } + /** + * @return string[] + */ public function getOrmObjectManagersToReset(): array { return $this->ormObjectManagersToReset; @@ -234,12 +216,15 @@ public function getOrmResetMode(): string return $this->ormResetMode; } + /** + * @return string[] + */ public function getOdmObjectManagersToReset(): array { return $this->odmObjectManagersToReset; } - private function managerRegistry(): ManagerRegistry + private function managerRegistry(): ?ManagerRegistry { if (!$this->hasManagerRegistry()) { /** @psalm-suppress MissingDependency */ diff --git a/src/Factory.php b/src/Factory.php index 79d8a3b98..b8d34fd9f 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -17,42 +17,36 @@ class Factory { private const NULL_VALUE = '__null_value'; - /** @var Configuration|null */ - private static $configuration; + private static ?Configuration $configuration = null; /** - * @var string - * @psalm-var class-string + * @var class-string */ - private $class; + private string $class; /** @var callable|null */ private $instantiator; - /** @var bool */ - private $persist = true; + private bool $persist = true; - /** @var bool */ - private $cascadePersist = false; + private bool $cascadePersist = false; /** @var array */ - private $attributeSet = []; + private array $attributeSet = []; /** @var callable[] */ - private $beforeInstantiate = []; + private array $beforeInstantiate = []; /** @var callable[] */ - private $afterInstantiate = []; + private array $afterInstantiate = []; /** @var callable[] */ - private $afterPersist = []; + private array $afterPersist = []; /** - * @param array|callable $defaultAttributes - * - * @psalm-param class-string $class + * @param class-string $class */ - public function __construct(string $class, $defaultAttributes = []) + public function __construct(string $class, array|callable $defaultAttributes = []) { if (self::class === static::class) { trigger_deprecation('zenstruck/foundry', '1.9', 'Instantiating "%s" directly is deprecated and this class will be abstract in 2.0, use "%s" instead.', self::class, AnonymousFactory::class); @@ -85,7 +79,7 @@ final public function create($attributes = []): Proxy $attributeSet = \array_merge($this->attributeSet, [$attributes]); // normalize each attribute set and collapse - $attributes = \array_merge(...\array_map([$this, 'normalizeAttributes'], $attributeSet)); + $attributes = \array_merge(...\array_map(fn(callable|array $attributes): array => $this::normalizeAttributes($attributes), $attributeSet)); foreach ($this->beforeInstantiate as $callback) { $attributes = $callback($attributes); @@ -102,8 +96,10 @@ final public function create($attributes = []): Proxy if (self::NULL_VALUE === $normalizedAttribute) { $normalizedAttribute = null; } + $mappedAttributes[$name] = $normalizedAttribute; } + $attributes = $mappedAttributes; // instantiate the object with the users instantiator or if not set, the default instantiator @@ -115,13 +111,13 @@ final public function create($attributes = []): Proxy $proxy = new Proxy($object); - if (!$this->isPersisting() || true === $this->cascadePersist) { + if (!$this->isPersisting() || $this->cascadePersist) { return $proxy; } return $proxy ->save() - ->withoutAutoRefresh(function(Proxy $proxy) use ($attributes) { + ->withoutAutoRefresh(function(Proxy $proxy) use ($attributes): void { if (!$this->afterPersist) { return; } @@ -154,7 +150,7 @@ final public function many(int $min, ?int $max = null): FactoryCollection * * @return FactoryCollection */ - final public function sequence($sequence): FactoryCollection + final public function sequence(iterable|callable $sequence): FactoryCollection { if (\is_callable($sequence)) { $sequence = $sequence(); @@ -302,20 +298,15 @@ final protected function class(): string return $this->class; } - /** - * @param array|callable $attributes - */ - private static function normalizeAttributes($attributes): array + private static function normalizeAttributes(array|callable $attributes): array { return \is_callable($attributes) ? $attributes() : $attributes; } /** - * @param mixed $value - * * @return mixed */ - private function normalizeAttribute($value, ?string $name = null) + private function normalizeAttribute(mixed $value, ?string $name = null) { if ($value instanceof Proxy) { return $value->isPersisted() ? $value->refresh()->object() : $value->object(); @@ -329,14 +320,10 @@ private function normalizeAttribute($value, ?string $name = null) // possible OneToMany/ManyToMany relationship return \array_filter( \array_map( - function($value) use ($name) { - return $this->normalizeAttribute($value, $name); - }, + fn($value) => $this->normalizeAttribute($value, $name), $value ), - function($value) { - return self::NULL_VALUE !== $value; - } + static fn($value): bool => self::NULL_VALUE !== $value ); } @@ -361,7 +348,7 @@ function($value) { $cascadePersist = $this->hasCascadePersist($value, $relationshipField); if ($this->isPersisting() && null !== $relationshipField && false === $cascadePersist) { - $this->afterPersist[] = static function(Proxy $proxy) use ($value, $relationshipField, $isCollection) { + $this->afterPersist[] = static function(Proxy $proxy) use ($value, $relationshipField, $isCollection): void { $value->create([$relationshipField => $isCollection ? [$proxy] : $proxy]); $proxy->refresh(); }; @@ -381,7 +368,7 @@ private static function normalizeObject(object $object): object { try { return Proxy::createFromPersisted($object)->refresh()->object(); - } catch (\RuntimeException $e) { + } catch (\RuntimeException) { return $object; } } @@ -393,7 +380,7 @@ private function normalizeCollection(?string $relationName, FactoryCollection $c $cascadePersist = $this->hasCascadePersist($collection->factory(), $field); if ($field && false === $cascadePersist) { - $this->afterPersist[] = static function(Proxy $proxy) use ($collection, $field) { + $this->afterPersist[] = static function(Proxy $proxy) use ($collection, $field): void { $collection->create([$field => $proxy]); $proxy->refresh(); }; @@ -404,7 +391,7 @@ private function normalizeCollection(?string $relationName, FactoryCollection $c } return \array_map( - function(self $factory) { + function(self $factory): self { $factory->cascadePersist = $this->cascadePersist; return $factory; @@ -467,7 +454,7 @@ private function inverseRelationshipField(?string $relationName, self $factory, try { // Check mappedBy side ($factory is the owner of the relation) $relationClassMetadata = self::configuration()->objectManagerFor($relationClass)->getClassMetadata($relationClass); - } catch (\RuntimeException $e) { + } catch (\RuntimeException) { // relation not managed - could be embeddable return null; } @@ -559,7 +546,7 @@ private function isPersisting(): bool try { $classMetadata = self::configuration()->objectManagerFor($this->class)->getClassMetadata($this->class); - } catch (\RuntimeException $e) { + } catch (\RuntimeException) { // entity not managed (perhaps Embeddable) return false; } diff --git a/src/FactoryCollection.php b/src/FactoryCollection.php index 30f148d06..358212622 100644 --- a/src/FactoryCollection.php +++ b/src/FactoryCollection.php @@ -9,27 +9,21 @@ */ final class FactoryCollection implements \IteratorAggregate { - /** @var Factory */ - private $factory; + private ?int $min; - /** @var int */ - private $min; - - /** @var int */ - private $max; - - /** @var iterable>|null */ - private $sequence; + private ?int $max; /** - * @param int|null $max If set, when created, the collection will be a random size between $min and $max - * @param iterable> $sequence + * @param int|null $max If set, when created, the collection will be a random size between $min and $max + * @param iterable> $sequence|null $sequence * * @psalm-param Factory $factory * - * @deprecated using directly FactoryCollection's constructor is deprecated. It will be private in v2. Use named constructors instead. + * @param Factory $factory + * + *@deprecated using directly FactoryCollection's constructor is deprecated. It will be private in v2. Use named constructors instead. */ - public function __construct(Factory $factory, ?int $min = null, ?int $max = null, ?iterable $sequence = null, bool $calledInternally = false) + public function __construct(private Factory $factory, ?int $min = null, ?int $max = null, private ?iterable $sequence = null, bool $calledInternally = false) { if ($max && $min > $max) { throw new \InvalidArgumentException('Min must be less than max.'); @@ -39,10 +33,8 @@ public function __construct(Factory $factory, ?int $min = null, ?int $max = null trigger_deprecation('zenstruck/foundry', '1.22.0', "using directly FactoryCollection's constructor is deprecated. It will be private in v2. Use named constructors instead."); } - $this->factory = $factory; $this->min = $min; $this->max = $max ?? $min; - $this->sequence = $sequence; } public static function set(Factory $factory, int $count): self @@ -64,14 +56,12 @@ public static function sequence(Factory $factory, iterable $sequence): self } /** - * @param array|callable $attributes - * * @return list> * * @psalm-suppress InvalidReturnType * @psalm-return list> */ - public function create($attributes = []): array + public function create(array|callable $attributes = []): array { $objects = []; foreach ($this->all() as $i => $factory) { @@ -93,9 +83,7 @@ public function all(): array if (!$this->sequence) { /** @psalm-suppress TooManyArguments */ return \array_map( - function() { - return clone $this->factory; - }, + fn(): Factory => clone $this->factory, \array_fill(0, \random_int($this->min, $this->max), null) ); } @@ -113,14 +101,17 @@ public function factory(): Factory return $this->factory; } - public function getIterator(): \Traversable + public function getIterator(): \ArrayIterator { return new \ArrayIterator($this->all()); } + /** + * @return \Iterator + */ public function asDataProvider(): iterable { - foreach ($this as $i => $factory) { + foreach ($this as $factory) { yield [$factory]; } } diff --git a/src/Instantiator.php b/src/Instantiator.php index 0e8a5731f..f8fdad994 100644 --- a/src/Instantiator.php +++ b/src/Instantiator.php @@ -11,23 +11,18 @@ */ final class Instantiator { - /** @var PropertyAccessor|null */ - private static $propertyAccessor; + private static ?PropertyAccessor $propertyAccessor = null; - /** @var bool */ - private $withoutConstructor = false; + private bool $withoutConstructor = false; - /** @var bool */ - private $allowExtraAttributes = false; + private bool $allowExtraAttributes = false; - /** @var array */ - private $extraAttributes = []; + private array $extraAttributes = []; - /** @var bool */ - private $alwaysForceProperties = false; + private bool $alwaysForceProperties = false; /** @var string[] */ - private $forceProperties = []; + private array $forceProperties = []; public function __invoke(array $attributes, string $class): object { @@ -124,11 +119,9 @@ public function alwaysForceProperties(array $properties = []): self } /** - * @param mixed $value - * * @throws \InvalidArgumentException if property does not exist for $object */ - public static function forceSet(object $object, string $property, $value): void + public static function forceSet(object $object, string $property, mixed $value): void { self::accessibleProperty($object, $property)->setValue($object, $value); } @@ -172,7 +165,7 @@ private static function reflectionProperty(\ReflectionClass $class, string $name { try { return $class->getProperty($name); - } catch (\ReflectionException $e) { + } catch (\ReflectionException) { if ($class = $class->getParentClass()) { return self::reflectionProperty($class, $name); } @@ -238,9 +231,7 @@ private static function snake(string $string): string /** * @see https://github.com/symfony/symfony/blob/a73523b065221b6b93cd45bf1cc7c59e7eb2dcdf/src/Symfony/Component/String/AbstractUnicodeString.php#L369 */ - $string = \preg_replace_callback('/\b./u', static function(array $m): string { - return \mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'); - }, $string, 1); + $string = \preg_replace_callback('/\b./u', static fn(array $m): string => \mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'), $string, 1); return \mb_strtolower(\preg_replace(['/(\p{Lu}+)(\p{Lu}\p{Ll})/u', '/([\p{Ll}0-9])(\p{Lu})/u'], '\1_\2', $string), 'UTF-8'); } diff --git a/src/ModelFactory.php b/src/ModelFactory.php index 7278456ad..285d0aff7 100644 --- a/src/ModelFactory.php +++ b/src/ModelFactory.php @@ -30,10 +30,8 @@ public static function __callStatic(string $name, array $arguments) /** * @param array|callable|string $defaultAttributes If string, assumes state * @param string ...$states Optionally pass default states (these must be methods on your ObjectFactory with no arguments) - * - * @return static */ - final public static function new($defaultAttributes = [], string ...$states): self + final public static function new(array|callable|string $defaultAttributes = [], string ...$states): static { if (\is_string($defaultAttributes)) { $states = \array_merge([$defaultAttributes], $states); @@ -47,7 +45,7 @@ final public static function new($defaultAttributes = [], string ...$states): se } $factory = $factory - ->withAttributes([$factory, 'getDefaults']) + ->withAttributes(static fn(): array => $factory->getDefaults()) ->withAttributes($defaultAttributes) ->initialize() ; @@ -82,7 +80,7 @@ final public static function createOne(array $attributes = []): Proxy * @return list> * @psalm-return list> */ - final public static function createSequence($sequence): array + final public static function createSequence(iterable|callable $sequence): array { return static::new()->sequence($sequence)->create(); } @@ -158,7 +156,7 @@ final public static function randomOrCreate(array $attributes = []): Proxy { try { return static::repository()->random($attributes); - } catch (\RuntimeException $e) { + } catch (\RuntimeException) { return static::new()->create($attributes); } } @@ -267,23 +265,19 @@ abstract protected static function getClass(): string; /** * Override to add default instantiator and default afterInstantiate/afterPersist events. - * - * @return static */ protected function initialize() { return $this; } - /** - * @param array|callable $attributes - * - * @return static - */ - final protected function addState($attributes = []): self + final protected function addState(array|callable $attributes = []): static { return $this->withAttributes($attributes); } + /** + * @return mixed[] + */ abstract protected function getDefaults(): array; } diff --git a/src/ModelFactoryManager.php b/src/ModelFactoryManager.php index d091b2351..d7427b8b3 100644 --- a/src/ModelFactoryManager.php +++ b/src/ModelFactoryManager.php @@ -9,20 +9,17 @@ */ final class ModelFactoryManager { - private $factories; - /** * @param ModelFactory[] $factories */ - public function __construct(iterable $factories) + public function __construct(private iterable $factories) { - $this->factories = $factories; } public function create(string $class): ModelFactory { foreach ($this->factories as $factory) { - if ($class === \get_class($factory)) { + if ($class === $factory::class) { return $factory; } } diff --git a/src/Proxy.php b/src/Proxy.php index 691c1b4f2..555dff08f 100644 --- a/src/Proxy.php +++ b/src/Proxy.php @@ -15,35 +15,28 @@ * * @author Kevin Bond */ -final class Proxy +final class Proxy implements \Stringable { /** - * @var object - * @psalm-var TProxiedObject - */ - private $object; - - /** - * @var string * @psalm-var class-string */ - private $class; + private string $class; - /** @var bool */ - private $autoRefresh; + private bool $autoRefresh; - /** @var bool */ - private $persisted = false; + private bool $persisted = false; /** * @internal * * @psalm-param TProxiedObject $object */ - public function __construct(object $object) + public function __construct(/** + * @psalm-var TProxiedObject + */ + private object $object) { - $this->object = $object; - $this->class = \get_class($object); + $this->class = $object::class; $this->autoRefresh = Factory::configuration()->defaultProxyAutoRefresh(); } @@ -153,7 +146,8 @@ public function remove(): self { $this->objectManager()->remove($this->object); $this->objectManager()->flush(); - $this->autoRefresh = $this->persisted = false; + $this->autoRefresh = false; + $this->persisted = false; return $this; } @@ -179,10 +173,7 @@ public function refresh(): self return $this; } - /** - * @param mixed $value - */ - public function forceSet(string $property, $value): self + public function forceSet(string $property, mixed $value): self { return $this->forceSetAll([$property => $value]); } @@ -272,7 +263,7 @@ public function executeCallback(callable $callback, ...$arguments): void Parameter::union( Parameter::untyped($this), Parameter::typed(self::class, $this), - Parameter::typed($this->class, Parameter::factory(function() { return $this->object(); })) + Parameter::typed($this->class, Parameter::factory(fn(): object => $this->object())) )->optional(), ...$arguments ); diff --git a/src/RepositoryAssertions.php b/src/RepositoryAssertions.php index 9fb8d985b..84a8fc6b4 100644 --- a/src/RepositoryAssertions.php +++ b/src/RepositoryAssertions.php @@ -9,11 +9,8 @@ */ final class RepositoryAssertions { - private $repository; - - public function __construct(RepositoryProxy $repository) + public function __construct(private RepositoryProxy $repository) { - $this->repository = $repository; } public function empty(string $message = 'Expected {entity} repository to be empty but it has {actual} items.'): self diff --git a/src/RepositoryProxy.php b/src/RepositoryProxy.php index 290f97c4e..7a3ecc04d 100644 --- a/src/RepositoryProxy.php +++ b/src/RepositoryProxy.php @@ -15,12 +15,11 @@ */ final class RepositoryProxy implements ObjectRepository, \IteratorAggregate, \Countable { - /** @var ObjectRepository|EntityRepository */ - private $repository; - - public function __construct(ObjectRepository $repository) + /** + * @param ObjectRepository|EntityRepository $repository + */ + public function __construct(private ObjectRepository $repository) { - $this->repository = $repository; } public function __call(string $method, array $arguments) @@ -289,7 +288,10 @@ public function find($criteria) } if (!\is_array($criteria)) { - return $this->proxyResult($this->repository->find($criteria)); + /** @var TProxiedObject|null $result */ + $result = $this->repository->find($criteria); + + return $this->proxyResult($result); } return $this->findOneBy($criteria); @@ -326,10 +328,11 @@ public function findOneBy(array $criteria, ?array $orderBy = null): ?Proxy $wrappedParams = (new \ReflectionClass($this->repository))->getMethod('findOneBy')->getParameters(); if (!isset($wrappedParams[1]) || 'orderBy' !== $wrappedParams[1]->getName() || !($type = $wrappedParams[1]->getType()) instanceof \ReflectionNamedType || 'array' !== $type->getName()) { - throw new \RuntimeException(\sprintf('Wrapped repository\'s (%s) findOneBy method does not have an $orderBy parameter.', \get_class($this->repository))); + throw new \RuntimeException(\sprintf('Wrapped repository\'s (%s) findOneBy method does not have an $orderBy parameter.', $this->repository::class)); } } + /** @var TProxiedObject|null $result */ $result = $this->repository->findOneBy(self::normalizeCriteria($criteria), $orderBy); if (null === $result) { return null; @@ -347,8 +350,6 @@ public function getClassName(): string } /** - * @param mixed $result - * * @return Proxy|Proxy[]|object|object[]|mixed * * @psalm-suppress InvalidReturnStatement @@ -357,7 +358,7 @@ public function getClassName(): string * @psalm-param TResult|list $result * @psalm-return ($result is array ? list> : Proxy) */ - private function proxyResult($result) + private function proxyResult(mixed $result) { if (\is_a($result, $this->getClassName())) { return Proxy::createFromPersisted($result); @@ -373,9 +374,7 @@ private function proxyResult($result) private static function normalizeCriteria(array $criteria): array { return \array_map( - function($value) { - return $value instanceof Proxy ? $value->object() : $value; - }, + fn($value) => $value instanceof Proxy ? $value->object() : $value, $criteria ); } diff --git a/src/Story.php b/src/Story.php index 877689d68..06c005913 100644 --- a/src/Story.php +++ b/src/Story.php @@ -8,10 +8,10 @@ abstract class Story { /** @var array */ - private $objects = []; + private array $objects = []; /** @var array */ - private $pools = []; + private array $pools = []; final public function __call(string $method, array $arguments) { @@ -91,10 +91,8 @@ final public static function getRandomRange(string $pool, int $min, int $max): a /** * @param object|Proxy|Factory $object - * - * @return static */ - final public function add(string $name, object $object): self + final public function add(string $name, object $object): static { trigger_deprecation('zenstruck\foundry', '1.17.0', 'Using Story::add() is deprecated, use Story::addState().'); diff --git a/src/StoryManager.php b/src/StoryManager.php index 92d162e99..a1911b72e 100644 --- a/src/StoryManager.php +++ b/src/StoryManager.php @@ -10,20 +10,16 @@ final class StoryManager { /** @var array */ - private static $globalInstances = []; + private static array $globalInstances = []; /** @var array */ - private static $instances = []; - - /** @var Story[] */ - private $stories; + private static array $instances = []; /** * @param Story[] $stories */ - public function __construct(iterable $stories) + public function __construct(private iterable $stories) { - $this->stories = $stories; } public function load(string $class): Story @@ -55,13 +51,14 @@ public static function reset(): void public static function globalReset(): void { - self::$globalInstances = self::$instances = []; + self::$globalInstances = []; + self::$instances = []; } private function getOrCreateStory(string $class): Story { foreach ($this->stories as $story) { - if ($class === \get_class($story)) { + if ($class === $story::class) { return $story; } } diff --git a/src/Test/AbstractSchemaResetter.php b/src/Test/AbstractSchemaResetter.php index 4df4ace92..45f7b451f 100644 --- a/src/Test/AbstractSchemaResetter.php +++ b/src/Test/AbstractSchemaResetter.php @@ -32,7 +32,7 @@ protected function runCommand(Application $application, string $command, array $ protected static function validateObjectsToReset(string $objectsType, array $availableObjectsToReset, array $candidateObjectsToReset): void { if ($invalidObjectsToReset = \array_diff($candidateObjectsToReset, $availableObjectsToReset)) { - throw new \InvalidArgumentException(\sprintf('Cannot reset %s schema: invalid value "%s" given as %s. Available values are "%s"', ORMDatabaseResetter::class === static::class ? 'ORM' : 'ODM', \json_encode($invalidObjectsToReset), $objectsType, \json_encode($availableObjectsToReset))); + throw new \InvalidArgumentException(\sprintf('Cannot reset %s schema: invalid value "%s" given as %s. Available values are "%s"', ORMDatabaseResetter::class === static::class ? 'ORM' : 'ODM', \json_encode($invalidObjectsToReset, \JSON_THROW_ON_ERROR), $objectsType, \json_encode($availableObjectsToReset, \JSON_THROW_ON_ERROR))); } } } diff --git a/src/Test/DatabaseResetter.php b/src/Test/DatabaseResetter.php index 0b3854a80..85e6cb804 100644 --- a/src/Test/DatabaseResetter.php +++ b/src/Test/DatabaseResetter.php @@ -16,8 +16,7 @@ */ final class DatabaseResetter { - /** @var bool */ - private static $hasBeenReset = false; + private static bool $hasBeenReset = false; public static function hasBeenReset(): bool { diff --git a/src/Test/Factories.php b/src/Test/Factories.php index ed12edec7..8c6b59473 100644 --- a/src/Test/Factories.php +++ b/src/Test/Factories.php @@ -3,6 +3,7 @@ namespace Zenstruck\Foundry\Test; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Zenstruck\Foundry\ChainManagerRegistry; use Zenstruck\Foundry\Factory; /** @@ -29,7 +30,7 @@ public static function _setUpFactories(): void TestState::bootFromContainer($kernel->getContainer()); Factory::configuration()->setManagerRegistry( - new LazyManagerRegistry(static function() { + new LazyManagerRegistry(static function(): ChainManagerRegistry { if (!static::$booted) { static::bootKernel(); } diff --git a/src/Test/GlobalStateRegistry.php b/src/Test/GlobalStateRegistry.php index 6dfa7f17f..ae736c01f 100644 --- a/src/Test/GlobalStateRegistry.php +++ b/src/Test/GlobalStateRegistry.php @@ -12,13 +12,13 @@ final class GlobalStateRegistry { /** @var list */ - private $storiesAsService = []; + private array $storiesAsService = []; /** @var list */ - private $invokableServices = []; + private array $invokableServices = []; /** @var list> */ - private $standaloneStories = []; + private array $standaloneStories = []; public function addStoryAsService(Story $storyAsService): void { @@ -43,20 +43,20 @@ public function addStandaloneStory(string $storyClass): void */ public function getGlobalStates(): array { - return \array_merge( - \array_map( - static function(Story $story) { - return static function() use ($story) {$story->build(); }; + return [ + ...\array_map( + static fn(Story $story): \Closure => static function() use ($story): void { + $story->build(); }, $this->storiesAsService ), - $this->invokableServices, - \array_map( - static function(string $storyClassName) { - return static function() use ($storyClassName) {$storyClassName::load(); }; + ...$this->invokableServices, + ...\array_map( + static fn(string $storyClassName): \Closure => static function() use ($storyClassName): void { + $storyClassName::load(); }, $this->standaloneStories - ) - ); + ), + ]; } } diff --git a/src/Test/LazyManagerRegistry.php b/src/Test/LazyManagerRegistry.php index 28371bc6b..9eb952569 100644 --- a/src/Test/LazyManagerRegistry.php +++ b/src/Test/LazyManagerRegistry.php @@ -30,11 +30,17 @@ public function getConnection($name = null): object return $this->inner()->getConnection($name); } + /** + * @return array + */ public function getConnections(): array { return $this->inner()->getConnections(); } + /** + * @return array + */ public function getConnectionNames(): array { return $this->inner()->getConnectionNames(); @@ -50,6 +56,9 @@ public function getManager($name = null): ObjectManager return $this->inner()->getManager($name); } + /** + * @return array + */ public function getManagers(): array { return $this->inner()->getManagers(); @@ -71,6 +80,9 @@ public function getAliasNamespace($alias): string throw new \BadMethodCallException('Method removed in doctrine/persistence v3.'); } + /** + * @return array + */ public function getManagerNames(): array { return $this->inner()->getManagerNames(); diff --git a/src/Test/ODMSchemaResetter.php b/src/Test/ODMSchemaResetter.php index e19b8d885..9188daebf 100644 --- a/src/Test/ODMSchemaResetter.php +++ b/src/Test/ODMSchemaResetter.php @@ -10,18 +10,14 @@ */ final class ODMSchemaResetter extends AbstractSchemaResetter { - /** @var Application */ - private $application; - /** @var ManagerRegistry */ - private $registry; /** @var list */ - private $objectManagersToReset; + private array $objectManagersToReset = []; - public function __construct(Application $application, ManagerRegistry $registry, array $objectManagersToReset) + /** + * @param string[] $objectManagersToReset + */ + public function __construct(private Application $application, private ManagerRegistry $registry, array $objectManagersToReset) { - $this->application = $application; - $this->registry = $registry; - self::validateObjectsToReset('object manager', \array_keys($registry->getManagerNames()), $objectManagersToReset); $this->objectManagersToReset = $objectManagersToReset; } @@ -37,7 +33,7 @@ public function resetSchema(): void '--dm' => $manager, ] ); - } catch (\Exception $e) { + } catch (\Exception) { } $this->runCommand( diff --git a/src/Test/ORMDatabaseResetter.php b/src/Test/ORMDatabaseResetter.php index 55d83583d..fb9d5266e 100644 --- a/src/Test/ORMDatabaseResetter.php +++ b/src/Test/ORMDatabaseResetter.php @@ -11,35 +11,26 @@ final class ORMDatabaseResetter extends AbstractSchemaResetter { public const RESET_MODE_SCHEMA = 'schema'; + public const RESET_MODE_MIGRATE = 'migrate'; - /** @var Application */ - private $application; - /** @var ManagerRegistry */ - private $registry; /** @var list */ - private $connectionsToReset; + private array $connectionsToReset = []; + /** @var list */ - private $objectManagersToReset; - /** @var string */ - private $resetMode; + private array $objectManagersToReset = []; /** * @param list $connectionsToReset * @param list $objectManagersToReset */ - public function __construct(Application $application, ManagerRegistry $registry, array $connectionsToReset, array $objectManagersToReset, string $resetMode) + public function __construct(private Application $application, private ManagerRegistry $registry, array $connectionsToReset, array $objectManagersToReset, private string $resetMode) { - $this->application = $application; - $this->registry = $registry; - self::validateObjectsToReset('connection', \array_keys($registry->getConnectionNames()), $connectionsToReset); $this->connectionsToReset = $connectionsToReset; self::validateObjectsToReset('object manager', \array_keys($registry->getManagerNames()), $objectManagersToReset); $this->objectManagersToReset = $objectManagersToReset; - - $this->resetMode = $resetMode; } public function resetDatabase(): void diff --git a/src/Test/ResetDatabase.php b/src/Test/ResetDatabase.php index 358539223..3af6f834e 100644 --- a/src/Test/ResetDatabase.php +++ b/src/Test/ResetDatabase.php @@ -36,7 +36,7 @@ public static function _resetDatabase(): void try { $kernel->getBundle('ZenstruckFoundryBundle'); - } catch (\InvalidArgumentException $exception) { + } catch (\InvalidArgumentException) { trigger_deprecation('zenstruck\foundry', '1.23', 'Usage of ResetDatabase trait without Foundry bundle is deprecated and will create an error in 2.0.'); } diff --git a/src/Test/TestState.php b/src/Test/TestState.php index 6c44285a8..3fb2a8835 100644 --- a/src/Test/TestState.php +++ b/src/Test/TestState.php @@ -20,14 +20,12 @@ final class TestState /** @var callable|null */ private static $instantiator; - /** @var Faker\Generator|null */ - private static $faker; + private static ?\Faker\Generator $faker = null; - /** @var bool|null */ - private static $defaultProxyAutoRefresh; + private static ?bool $defaultProxyAutoRefresh = null; /** @var callable[] */ - private static $globalStates = []; + private static array $globalStates = []; /** * @deprecated Use TestState::configure() diff --git a/tests/Fixtures/Document/Category.php b/tests/Fixtures/Document/Category.php index 44f3a467f..b35ac9fe8 100644 --- a/tests/Fixtures/Document/Category.php +++ b/tests/Fixtures/Document/Category.php @@ -34,7 +34,7 @@ public function getName(): ?string return $this->name; } - public function setName($name): void + public function setName(string $name): void { $this->name = $name; } diff --git a/tests/Fixtures/Document/Comment.php b/tests/Fixtures/Document/Comment.php index 7e64530dd..801958e35 100644 --- a/tests/Fixtures/Document/Comment.php +++ b/tests/Fixtures/Document/Comment.php @@ -55,7 +55,7 @@ public function setBody(string $body): self return $this; } - public function getCreatedAt(): ?\DateTimeInterface + public function getCreatedAt(): \DateTimeInterface { return $this->createdAt; } diff --git a/tests/Fixtures/Document/Post.php b/tests/Fixtures/Document/Post.php index d022f5716..5bd1e5a19 100644 --- a/tests/Fixtures/Document/Post.php +++ b/tests/Fixtures/Document/Post.php @@ -9,7 +9,7 @@ /** * @MongoDB\Document(collection="post") */ -class Post +class Post implements \Stringable { /** * @MongoDB\Id @@ -115,7 +115,7 @@ public function isPublished(): bool return null !== $this->publishedAt; } - public function setPublishedAt(\DateTime $timestamp) + public function setPublishedAt(\DateTime $timestamp): void { $this->publishedAt = $timestamp; } diff --git a/tests/Fixtures/Document/User.php b/tests/Fixtures/Document/User.php index 158a00855..ea78a6aa4 100644 --- a/tests/Fixtures/Document/User.php +++ b/tests/Fixtures/Document/User.php @@ -12,14 +12,14 @@ class User /** * @MongoDB\Field(type="string") */ - private $name; + private string $name; public function __construct(string $name) { $this->name = $name; } - public function getName(): ?string + public function getName(): string { return $this->name; } diff --git a/tests/Fixtures/Entity/Address.php b/tests/Fixtures/Entity/Address.php index b3df4739b..e011ce02a 100644 --- a/tests/Fixtures/Entity/Address.php +++ b/tests/Fixtures/Entity/Address.php @@ -10,6 +10,7 @@ final class Address { /** + * @var mixed|null * @ORM\Column(type="string", nullable=true) */ private $value; @@ -19,7 +20,7 @@ public function getValue() return $this->value; } - public function setValue($value) + public function setValue($value): void { $this->value = $value; } diff --git a/tests/Fixtures/Entity/Cascade/Brand.php b/tests/Fixtures/Entity/Cascade/Brand.php index d9cc6fc24..20fe7abff 100644 --- a/tests/Fixtures/Entity/Cascade/Brand.php +++ b/tests/Fixtures/Entity/Cascade/Brand.php @@ -17,17 +17,17 @@ class Brand * @ORM\GeneratedValue * @ORM\Column(type="integer") */ - private $id; + private ?int $id = null; /** * @ORM\Column(type="string", length=255) */ - private $name; + private ?string $name = null; /** * @ORM\OneToMany(targetEntity=Product::class, mappedBy="brand") */ - private $products; + private Collection $products; public function __construct() { diff --git a/tests/Fixtures/Entity/Cascade/Category.php b/tests/Fixtures/Entity/Cascade/Category.php index 3d5983714..e2ccc966f 100644 --- a/tests/Fixtures/Entity/Cascade/Category.php +++ b/tests/Fixtures/Entity/Cascade/Category.php @@ -17,17 +17,17 @@ class Category * @ORM\GeneratedValue * @ORM\Column(type="integer") */ - private $id; + private ?int $id = null; /** * @ORM\Column(type="string", length=255) */ - private $name; + private ?string $name = null; /** * @ORM\ManyToMany(targetEntity=Product::class, inversedBy="categories") */ - private $products; + private Collection $products; public function __construct() { diff --git a/tests/Fixtures/Entity/Cascade/Image.php b/tests/Fixtures/Entity/Cascade/Image.php index 7319c474a..698efff55 100644 --- a/tests/Fixtures/Entity/Cascade/Image.php +++ b/tests/Fixtures/Entity/Cascade/Image.php @@ -15,12 +15,12 @@ class Image * @ORM\GeneratedValue * @ORM\Column(type="integer") */ - private $id; + private ?int $id = null; /** * @ORM\Column(type="string", length=255) */ - private $path; + private ?string $path = null; public function getId(): ?int { diff --git a/tests/Fixtures/Entity/Cascade/Product.php b/tests/Fixtures/Entity/Cascade/Product.php index 7c4dd52dc..1c9da057c 100644 --- a/tests/Fixtures/Entity/Cascade/Product.php +++ b/tests/Fixtures/Entity/Cascade/Product.php @@ -17,37 +17,37 @@ class Product * @ORM\GeneratedValue * @ORM\Column(type="integer") */ - private $id; + private ?int $id = null; /** * @ORM\Column(type="string", length=255) */ - private $name; + private ?string $name = null; /** * @ORM\ManyToOne(targetEntity=Brand::class, inversedBy="products", cascade={"persist"}) */ - private $brand; + private ?Brand $brand = null; /** * @ORM\OneToMany(targetEntity=Variant::class, mappedBy="product", cascade={"persist"}) */ - private $variants; + private Collection $variants; /** * @ORM\ManyToMany(targetEntity=Category::class, mappedBy="products", cascade={"persist"}) */ - private $categories; + private Collection $categories; /** * @ORM\ManyToMany(targetEntity=Tag::class, inversedBy="products", cascade={"persist"}) */ - private $tags; + private Collection $tags; /** * @ORM\OneToOne(targetEntity=Review::class, mappedBy="product", cascade={"persist"}) */ - private $review; + private ?Review $review = null; public function __construct() { @@ -71,7 +71,7 @@ public function setName(?string $name): void $this->name = $name; } - public function getBrand(): Brand + public function getBrand(): ?Brand { return $this->brand; } @@ -81,7 +81,7 @@ public function setBrand(Brand $brand): void $this->brand = $brand; } - public function getReview(): Review + public function getReview(): ?Review { return $this->review; } diff --git a/tests/Fixtures/Entity/Cascade/Review.php b/tests/Fixtures/Entity/Cascade/Review.php index d0df7bbe4..b5b3108bf 100644 --- a/tests/Fixtures/Entity/Cascade/Review.php +++ b/tests/Fixtures/Entity/Cascade/Review.php @@ -15,17 +15,17 @@ class Review * @ORM\GeneratedValue * @ORM\Column(type="integer") */ - private $id; + private ?int $id = null; /** * @ORM\OneToOne(targetEntity=Product::class, inversedBy="review") */ - private $product; + private ?Product $product = null; /** * @ORM\Column(name="ranking", type="integer") */ - private $rank; + private ?int $rank = null; public function getId(): ?int { diff --git a/tests/Fixtures/Entity/Cascade/Tag.php b/tests/Fixtures/Entity/Cascade/Tag.php index fbd57d980..c821e366c 100644 --- a/tests/Fixtures/Entity/Cascade/Tag.php +++ b/tests/Fixtures/Entity/Cascade/Tag.php @@ -17,17 +17,17 @@ class Tag * @ORM\GeneratedValue * @ORM\Column(type="integer") */ - private $id; + private ?int $id = null; /** * @ORM\Column(type="string", length=255) */ - private $name; + private ?string $name = null; /** * @ORM\ManyToMany(targetEntity=Product::class, mappedBy="tags") */ - private $products; + private Collection $products; public function __construct() { diff --git a/tests/Fixtures/Entity/Cascade/Variant.php b/tests/Fixtures/Entity/Cascade/Variant.php index 668e9ea7d..3b0597c88 100644 --- a/tests/Fixtures/Entity/Cascade/Variant.php +++ b/tests/Fixtures/Entity/Cascade/Variant.php @@ -15,22 +15,22 @@ class Variant * @ORM\GeneratedValue * @ORM\Column(type="integer") */ - private $id; + private ?int $id = null; /** * @ORM\Column(type="string", length=255) */ - private $name; + private ?string $name = null; /** * @ORM\ManyToOne(targetEntity=Product::class, inversedBy="variants") */ - private $product; + private ?Product $product = null; /** * @ORM\OneToOne(targetEntity=Image::class, cascade={"persist"}) */ - private $image; + private ?Image $image = null; public function getId(): ?int { diff --git a/tests/Fixtures/Entity/Category.php b/tests/Fixtures/Entity/Category.php index 7017b0e10..284fec619 100644 --- a/tests/Fixtures/Entity/Category.php +++ b/tests/Fixtures/Entity/Category.php @@ -3,6 +3,7 @@ namespace Zenstruck\Foundry\Tests\Fixtures\Entity; use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; /** @@ -19,19 +20,20 @@ class Category private $id; /** + * @var mixed|null * @ORM\Column(type="string", length=255) */ - private $name; + private ?string $name = null; /** * @ORM\OneToMany(targetEntity=Post::class, mappedBy="category") */ - private $posts; + private Collection $posts; /** * @ORM\OneToMany(targetEntity=Post::class, mappedBy="secondaryCategory") */ - private $secondaryPosts; + private Collection $secondaryPosts; public function __construct() { @@ -49,17 +51,17 @@ public function getName(): ?string return $this->name; } - public function setName($name) + public function setName(?string $name): void { $this->name = $name; } - public function getPosts() + public function getPosts(): Collection { return $this->posts; } - public function addPost(Post $post) + public function addPost(Post $post): void { if (!$this->posts->contains($post)) { $this->posts[] = $post; @@ -67,7 +69,7 @@ public function addPost(Post $post) } } - public function removePost(Post $post) + public function removePost(Post $post): void { if ($this->posts->contains($post)) { $this->posts->removeElement($post); @@ -78,12 +80,12 @@ public function removePost(Post $post) } } - public function getSecondaryPosts() + public function getSecondaryPosts(): Collection { return $this->posts; } - public function addSecondaryPost(Post $secondaryPost) + public function addSecondaryPost(Post $secondaryPost): void { if (!$this->secondaryPosts->contains($secondaryPost)) { $this->secondaryPosts[] = $secondaryPost; @@ -91,7 +93,7 @@ public function addSecondaryPost(Post $secondaryPost) } } - public function removeSecondaryPost(Post $secondaryPost) + public function removeSecondaryPost(Post $secondaryPost): void { if ($this->secondaryPosts->contains($secondaryPost)) { $this->secondaryPosts->removeElement($secondaryPost); diff --git a/tests/Fixtures/Entity/Comment.php b/tests/Fixtures/Entity/Comment.php index 85e6f82f0..941838018 100644 --- a/tests/Fixtures/Entity/Comment.php +++ b/tests/Fixtures/Entity/Comment.php @@ -15,34 +15,34 @@ class Comment * @ORM\GeneratedValue * @ORM\Column(type="integer") */ - private $id; + private ?int $id = null; /** * @ORM\ManyToOne(targetEntity=User::class, inversedBy="comments") * @ORM\JoinColumn(nullable=false, onDelete="CASCADE") */ - private $user; + private User $user; /** * @ORM\Column(type="text") */ - private $body; + private string $body; /** * @ORM\Column(type="datetime") */ - private $createdAt; + private \DateTimeInterface $createdAt; /** * @ORM\ManyToOne(targetEntity=Post::class, inversedBy="comments") * @ORM\JoinColumn(nullable=false, onDelete="CASCADE") */ - private $post; + private ?Post $post = null; /** * @ORM\Column(type="boolean") */ - private $approved = false; + private bool $approved = false; public function __construct(User $user, string $body) { @@ -68,7 +68,7 @@ public function setUser(?User $user): self return $this; } - public function getBody(): ?string + public function getBody(): string { return $this->body; } @@ -80,7 +80,7 @@ public function setBody(string $body): self return $this; } - public function getCreatedAt(): ?\DateTimeInterface + public function getCreatedAt(): \DateTime { return $this->createdAt; } @@ -104,7 +104,7 @@ public function setPost(?Post $post): self return $this; } - public function getApproved(): ?bool + public function getApproved(): bool { return $this->approved; } diff --git a/tests/Fixtures/Entity/Contact.php b/tests/Fixtures/Entity/Contact.php index 2da1f9ee7..654f124eb 100644 --- a/tests/Fixtures/Entity/Contact.php +++ b/tests/Fixtures/Entity/Contact.php @@ -25,7 +25,7 @@ class Contact /** * @ORM\Embedded("Address") */ - private $address; + private Address $address; public function __construct($name) { diff --git a/tests/Fixtures/Entity/Post.php b/tests/Fixtures/Entity/Post.php index 2583cc50a..a1f319f7b 100644 --- a/tests/Fixtures/Entity/Post.php +++ b/tests/Fixtures/Entity/Post.php @@ -3,6 +3,7 @@ namespace Zenstruck\Foundry\Tests\Fixtures\Entity; use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; /** @@ -12,7 +13,7 @@ * @ORM\DiscriminatorColumn(name="type") * @ORM\DiscriminatorMap({"simple": Post::class, "specific": SpecificPost::class}) */ -class Post +class Post implements \Stringable { /** * @ORM\Id @@ -24,90 +25,90 @@ class Post /** * @ORM\Column(type="string", length=255) */ - private $title; + private string $title; /** * @ORM\Column(type="text") */ - private $body; + private string $body; /** * @ORM\Column(type="string", length=255, nullable=true) */ - private $shortDescription; + private ?string $shortDescription; /** * @ORM\Column(type="integer") */ - private $viewCount = 0; + private int $viewCount = 0; /** * @ORM\Column(type="datetime") */ - private $createdAt; + private \DateTime $createdAt; /** * @ORM\Column(type="datetime", nullable=true) */ - private $publishedAt; + private ?\DateTime $publishedAt = null; /** * @ORM\ManyToOne(targetEntity=Category::class, inversedBy="posts") * @ORM\JoinColumn */ - private $category; + private ?Category $category = null; /** * @ORM\ManyToOne(targetEntity=Category::class, inversedBy="secondaryPosts") * @ORM\JoinColumn(name="secondary_category_id") */ - private $secondaryCategory; + private ?Category $secondaryCategory = null; /** * @ORM\ManyToMany(targetEntity=Tag::class, inversedBy="posts") */ - private $tags; + private Collection $tags; /** * @ORM\ManyToMany(targetEntity=Tag::class, inversedBy="secondaryPosts") * @ORM\JoinTable(name="post_tag_secondary") */ - private $secondaryTags; + private Collection $secondaryTags; /** * @ORM\OneToMany(targetEntity=Comment::class, mappedBy="post") */ - private $comments; + private Collection $comments; /** * @ORM\OneToOne(targetEntity=Post::class, inversedBy="mostRelevantRelatedToPost") */ - private $mostRelevantRelatedPost; + private ?Post $mostRelevantRelatedPost = null; /** * @ORM\OneToOne(targetEntity=Post::class, mappedBy="mostRelevantRelatedPost") */ - private $mostRelevantRelatedToPost; + private ?Post $mostRelevantRelatedToPost = null; /** * @ORM\OneToOne(targetEntity=Post::class, inversedBy="lessRelevantRelatedToPost") */ - private $lessRelevantRelatedPost; + private ?Post $lessRelevantRelatedPost = null; /** * @ORM\OneToOne(targetEntity=Post::class, mappedBy="lessRelevantRelatedPost") */ - private $lessRelevantRelatedToPost; + private ?Post $lessRelevantRelatedToPost = null; /** * @ORM\ManyToMany(targetEntity=Post::class, inversedBy="relatedToPosts") */ - private $relatedPosts; + private Collection $relatedPosts; /** * @ORM\ManyToMany(targetEntity=Post::class, mappedBy="relatedPosts") */ - private $relatedToPosts; + private Collection $relatedToPosts; public function __construct(string $title, string $body, ?string $shortDescription = null) { @@ -132,7 +133,7 @@ public function getId() return $this->id; } - public function getTitle(): ?string + public function getTitle(): string { return $this->title; } @@ -142,7 +143,7 @@ public function setTitle(string $title): void $this->title = $title; } - public function getBody(): ?string + public function getBody(): string { return $this->body; } @@ -167,7 +168,7 @@ public function increaseViewCount(int $amount = 1): void $this->viewCount += $amount; } - public function getCreatedAt(): ?\DateTime + public function getCreatedAt(): \DateTime { return $this->createdAt; } @@ -177,7 +178,7 @@ public function getCategory(): ?Category return $this->category; } - public function setCategory(?Category $category) + public function setCategory(?Category $category): void { $this->category = $category; } @@ -187,7 +188,7 @@ public function getSecondaryCategory(): ?Category return $this->secondaryCategory; } - public function setSecondaryCategory(?Category $secondaryCategory) + public function setSecondaryCategory(?Category $secondaryCategory): void { $this->secondaryCategory = $secondaryCategory; } @@ -197,36 +198,36 @@ public function isPublished(): bool return null !== $this->publishedAt; } - public function setPublishedAt(\DateTime $timestamp) + public function setPublishedAt(\DateTime $timestamp): void { $this->publishedAt = $timestamp; } - public function getRelatedPosts() + public function getRelatedPosts(): Collection { return $this->relatedPosts; } - public function addRelatedPost(self $relatedPost) + public function addRelatedPost(self $relatedPost): void { if (!$this->relatedPosts->contains($relatedPost)) { $this->relatedPosts[] = $relatedPost; } } - public function removeRelatedPost(self $relatedPost) + public function removeRelatedPost(self $relatedPost): void { if ($this->relatedPosts->contains($relatedPost)) { $this->relatedPosts->removeElement($relatedPost); } } - public function getRelatedToPosts() + public function getRelatedToPosts(): Collection { return $this->relatedToPosts; } - public function addRelatedToPost(self $relatedToPost) + public function addRelatedToPost(self $relatedToPost): void { if (!$this->relatedToPosts->contains($relatedToPost)) { $this->relatedToPosts[] = $relatedToPost; @@ -234,7 +235,7 @@ public function addRelatedToPost(self $relatedToPost) } } - public function removeRelatedToPost(self $relatedToPost) + public function removeRelatedToPost(self $relatedToPost): void { if ($this->relatedToPosts->contains($relatedToPost)) { $this->relatedToPosts->removeElement($relatedToPost); @@ -242,45 +243,45 @@ public function removeRelatedToPost(self $relatedToPost) } } - public function getTags() + public function getTags(): Collection { return $this->tags; } - public function addTag(Tag $tag) + public function addTag(Tag $tag): void { if (!$this->tags->contains($tag)) { $this->tags[] = $tag; } } - public function removeTag(Tag $tag) + public function removeTag(Tag $tag): void { if ($this->tags->contains($tag)) { $this->tags->removeElement($tag); } } - public function getSecondaryTags() + public function getSecondaryTags(): Collection { return $this->secondaryTags; } - public function addSecondaryTag(Tag $secondaryTag) + public function addSecondaryTag(Tag $secondaryTag): void { if (!$this->secondaryTags->contains($secondaryTag)) { $this->secondaryTags[] = $secondaryTag; } } - public function removeSecondaryTag(Tag $tag) + public function removeSecondaryTag(Tag $tag): void { if ($this->tags->contains($tag)) { $this->tags->removeElement($tag); } } - public function getComments() + public function getComments(): Collection { return $this->comments; } @@ -313,7 +314,7 @@ public function getMostRelevantRelatedPost(): ?self return $this->mostRelevantRelatedPost; } - public function setMostRelevantRelatedPost(?self $mostRelevantRelatedPost) + public function setMostRelevantRelatedPost(?self $mostRelevantRelatedPost): void { $this->mostRelevantRelatedPost = $mostRelevantRelatedPost; } @@ -323,7 +324,7 @@ public function getMostRelevantRelatedToPost(): ?self return $this->mostRelevantRelatedToPost; } - public function setMostRelevantRelatedToPost(?self $mostRelevantRelatedToPost) + public function setMostRelevantRelatedToPost(?self $mostRelevantRelatedToPost): void { $this->mostRelevantRelatedToPost = $mostRelevantRelatedToPost; } @@ -333,7 +334,7 @@ public function getLessRelevantRelatedPost(): ?self return $this->lessRelevantRelatedPost; } - public function setLessRelevantRelatedPost(?self $lessRelevantRelatedPost) + public function setLessRelevantRelatedPost(?self $lessRelevantRelatedPost): void { $this->lessRelevantRelatedPost = $lessRelevantRelatedPost; } @@ -343,7 +344,7 @@ public function getLessRelevantRelatedToPost(): ?self return $this->lessRelevantRelatedToPost; } - public function setLessRelevantRelatedToPost(?self $lessRelevantRelatedToPost) + public function setLessRelevantRelatedToPost(?self $lessRelevantRelatedToPost): void { $this->lessRelevantRelatedToPost = $lessRelevantRelatedToPost; } diff --git a/tests/Fixtures/Entity/SpecificPost.php b/tests/Fixtures/Entity/SpecificPost.php index c17f5ef44..fe72348fe 100644 --- a/tests/Fixtures/Entity/SpecificPost.php +++ b/tests/Fixtures/Entity/SpecificPost.php @@ -10,6 +10,7 @@ class SpecificPost extends Post { /** + * @var mixed|null * @ORM\Column(type="string", length=255, nullable=true) */ private $specificProperty; @@ -19,7 +20,7 @@ public function getSpecificProperty() return $this->specificProperty; } - public function setSpecificProperty($specificProperty) + public function setSpecificProperty($specificProperty): static { $this->specificProperty = $specificProperty; diff --git a/tests/Fixtures/Entity/Tag.php b/tests/Fixtures/Entity/Tag.php index 7031d18ea..32ee2b4a9 100644 --- a/tests/Fixtures/Entity/Tag.php +++ b/tests/Fixtures/Entity/Tag.php @@ -3,6 +3,7 @@ namespace Zenstruck\Foundry\Tests\Fixtures\Entity; use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; /** @@ -19,19 +20,20 @@ class Tag private $id; /** + * @var mixed|null * @ORM\Column(type="string", length=255) */ - private $name; + private ?string $name = null; /** * @ORM\ManyToMany(targetEntity=Post::class, mappedBy="tags") */ - private $posts; + private Collection $posts; /** * @ORM\ManyToMany(targetEntity=Post::class, mappedBy="secondaryTags") */ - private $secondaryPosts; + private Collection $secondaryPosts; public function __construct() { @@ -44,17 +46,17 @@ public function getName(): ?string return $this->name; } - public function setName($name) + public function setName($name): void { $this->name = $name; } - public function getPosts() + public function getPosts(): Collection { return $this->posts; } - public function addPost(Post $post) + public function addPost(Post $post): void { if (!$this->posts->contains($post)) { $this->posts[] = $post; @@ -62,7 +64,7 @@ public function addPost(Post $post) } } - public function removePost(Post $post) + public function removePost(Post $post): void { if ($this->posts->contains($post)) { $this->posts->removeElement($post); @@ -70,12 +72,12 @@ public function removePost(Post $post) } } - public function getSecondaryPosts() + public function getSecondaryPosts(): Collection { return $this->secondaryPosts; } - public function addSecondaryPost(Post $secondaryPost) + public function addSecondaryPost(Post $secondaryPost): void { if (!$this->secondaryPosts->contains($secondaryPost)) { $this->secondaryPosts[] = $secondaryPost; @@ -83,7 +85,7 @@ public function addSecondaryPost(Post $secondaryPost) } } - public function removeSecondaryPost(Post $secondaryPost) + public function removeSecondaryPost(Post $secondaryPost): void { if ($this->secondaryPosts->contains($secondaryPost)) { $this->secondaryPosts->removeElement($secondaryPost); diff --git a/tests/Fixtures/Entity/User.php b/tests/Fixtures/Entity/User.php index 312881c12..0d26999b3 100644 --- a/tests/Fixtures/Entity/User.php +++ b/tests/Fixtures/Entity/User.php @@ -3,6 +3,7 @@ namespace Zenstruck\Foundry\Tests\Fixtures\Entity; use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; /** @@ -16,17 +17,17 @@ class User * @ORM\GeneratedValue * @ORM\Column(type="integer") */ - private $id; + private ?int $id = null; /** * @ORM\Column(type="string") */ - private $name; + private ?string $name = null; /** * @ORM\OneToMany(targetEntity=Comment::class, mappedBy="user", orphanRemoval=true) */ - private $comments; + private Collection $comments; public function __construct() { @@ -50,12 +51,12 @@ public function setName(string $name): self return $this; } - public function getComments() + public function getComments(): Collection { return $this->comments; } - public function addComment(Comment $comment) + public function addComment(Comment $comment): void { if (!$this->comments->contains($comment)) { $this->comments[] = $comment; @@ -63,7 +64,7 @@ public function addComment(Comment $comment) } } - public function removeComment(Comment $comment) + public function removeComment(Comment $comment): void { if ($this->comments->contains($comment)) { $this->comments->removeElement($comment); diff --git a/tests/Fixtures/Factories/CategoryServiceFactory.php b/tests/Fixtures/Factories/CategoryServiceFactory.php index 4ef96f8ef..2a39bebc7 100644 --- a/tests/Fixtures/Factories/CategoryServiceFactory.php +++ b/tests/Fixtures/Factories/CategoryServiceFactory.php @@ -11,13 +11,9 @@ */ final class CategoryServiceFactory extends ModelFactory { - private $service; - - public function __construct(Service $service) + public function __construct(private Service $service) { parent::__construct(); - - $this->service = $service; } protected static function getClass(): string diff --git a/tests/Fixtures/Factories/ODM/PostFactory.php b/tests/Fixtures/Factories/ODM/PostFactory.php index bdbe94e61..71c9606ae 100644 --- a/tests/Fixtures/Factories/ODM/PostFactory.php +++ b/tests/Fixtures/Factories/ODM/PostFactory.php @@ -10,23 +10,19 @@ class PostFactory extends ModelFactory { - public function published(): self + public function published(): static { - return $this->addState(function() { - return ['published_at' => self::faker()->dateTime()]; - }); + return $this->addState(static fn(): array => ['published_at' => self::faker()->dateTime()]); } - public function withComments(): self + public function withComments(): static { - return $this->addState(function() { - return [ - 'comments' => new ArrayCollection([ - new Comment(new User('user'), 'body'), - new Comment(new User('user'), 'body'), - ]), - ]; - }); + return $this->addState(static fn(): array => [ + 'comments' => new ArrayCollection([ + new Comment(new User('user'), 'body'), + new Comment(new User('user'), 'body'), + ]), + ]); } protected static function getClass(): string diff --git a/tests/Fixtures/Factories/PostFactory.php b/tests/Fixtures/Factories/PostFactory.php index df5579fe3..cd5cd2b85 100644 --- a/tests/Fixtures/Factories/PostFactory.php +++ b/tests/Fixtures/Factories/PostFactory.php @@ -10,11 +10,9 @@ */ class PostFactory extends ModelFactory { - public function published(): self + public function published(): static { - return $this->addState(function() { - return ['published_at' => self::faker()->dateTime()]; - }); + return $this->addState(static fn(): array => ['published_at' => self::faker()->dateTime()]); } protected static function getClass(): string diff --git a/tests/Fixtures/Kernel.php b/tests/Fixtures/Kernel.php index 6a83203d7..ac1cbc9aa 100644 --- a/tests/Fixtures/Kernel.php +++ b/tests/Fixtures/Kernel.php @@ -30,10 +30,9 @@ class Kernel extends BaseKernel { use MicroKernelTrait; - /** @var bool */ - private $enableDoctrine = true; - /** @var string */ - private $ormResetMode = ORMDatabaseResetter::RESET_MODE_SCHEMA; + private bool $enableDoctrine = true; + + private string $ormResetMode = ORMDatabaseResetter::RESET_MODE_SCHEMA; public static function create( bool $enableDoctrine = true, @@ -78,7 +77,7 @@ public function getCacheDir(): string { return \sprintf( "{$this->getProjectDir()}/var/cache/test/%s", - \md5(\json_encode([$this->enableDoctrine, $this->ormResetMode])) + \md5(\json_encode([$this->enableDoctrine, $this->ormResetMode], \JSON_THROW_ON_ERROR)) ); } diff --git a/tests/Fixtures/Migrations/Version20220925092226.php b/tests/Fixtures/Migrations/Version20220925092226.php index c49c37447..5c3f50bd0 100644 --- a/tests/Fixtures/Migrations/Version20220925092226.php +++ b/tests/Fixtures/Migrations/Version20220925092226.php @@ -54,4 +54,9 @@ public function down(Schema $schema): void $this->addSql('DROP INDEX IDX_885DBAFAEA0D7566 ON posts'); $this->addSql('ALTER TABLE posts DROP secondary_category_id'); } + + public function isTransactional(): bool + { + return false; + } } diff --git a/tests/Fixtures/Migrations/Version20220925092634.php b/tests/Fixtures/Migrations/Version20220925092634.php index 29d26c4ba..216f2dc8b 100644 --- a/tests/Fixtures/Migrations/Version20220925092634.php +++ b/tests/Fixtures/Migrations/Version20220925092634.php @@ -23,7 +23,7 @@ public function up(Schema $schema): void $this->addSql('CREATE TABLE post_post (post_source INT NOT NULL, post_target INT NOT NULL, INDEX IDX_93DF0B866FA89B16 (post_source), INDEX IDX_93DF0B86764DCB99 (post_target), PRIMARY KEY(post_source, post_target)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); $this->addSql('ALTER TABLE post_post ADD CONSTRAINT FK_93DF0B866FA89B16 FOREIGN KEY (post_source) REFERENCES posts (id) ON DELETE CASCADE'); $this->addSql('ALTER TABLE post_post ADD CONSTRAINT FK_93DF0B86764DCB99 FOREIGN KEY (post_target) REFERENCES posts (id) ON DELETE CASCADE'); - $this->addSql('ALTER TABLE posts ADD type VARCHAR(255) NOT NULL DEFAULT \'simple\', ADD specificProperty VARCHAR(255) DEFAULT NULL'); + $this->addSql("ALTER TABLE posts ADD type VARCHAR(255) NOT NULL DEFAULT 'simple', ADD specificProperty VARCHAR(255) DEFAULT NULL"); } public function down(Schema $schema): void @@ -34,4 +34,9 @@ public function down(Schema $schema): void $this->addSql('DROP TABLE post_post'); $this->addSql('ALTER TABLE posts DROP type, DROP specificProperty'); } + + public function isTransactional(): bool + { + return false; + } } diff --git a/tests/Fixtures/Migrations/Version20221010154036.php b/tests/Fixtures/Migrations/Version20221010154036.php index 5826a3148..95a7dc20a 100644 --- a/tests/Fixtures/Migrations/Version20221010154036.php +++ b/tests/Fixtures/Migrations/Version20221010154036.php @@ -27,7 +27,12 @@ public function up(Schema $schema): void public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE posts CHANGE type type VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT \'simple\' NOT NULL COLLATE `utf8mb4_unicode_ci`'); + $this->addSql("ALTER TABLE posts CHANGE type type VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT 'simple' NOT NULL COLLATE `utf8mb4_unicode_ci`"); $this->addSql('ALTER TABLE posts RENAME INDEX idx_885dbafa20dbe482 TO IDX_985DBAFAD126F52'); } + + public function isTransactional(): bool + { + return false; + } } diff --git a/tests/Fixtures/Service.php b/tests/Fixtures/Service.php index 9add3cec2..2162d0d22 100644 --- a/tests/Fixtures/Service.php +++ b/tests/Fixtures/Service.php @@ -7,5 +7,5 @@ */ final class Service { - public $name = 'From Service'; + public string $name = 'From Service'; } diff --git a/tests/Fixtures/Stories/ServiceStory.php b/tests/Fixtures/Stories/ServiceStory.php index f511f9432..0cbfefad1 100644 --- a/tests/Fixtures/Stories/ServiceStory.php +++ b/tests/Fixtures/Stories/ServiceStory.php @@ -11,12 +11,8 @@ */ final class ServiceStory extends Story { - /** @var Service */ - private $service; - - public function __construct(Service $service) + public function __construct(private Service $service) { - $this->service = $service; } public function build(): void diff --git a/tests/Functional/AnonymousFactoryTest.php b/tests/Functional/AnonymousFactoryTest.php index 29a9aae82..466a47a06 100644 --- a/tests/Functional/AnonymousFactoryTest.php +++ b/tests/Functional/AnonymousFactoryTest.php @@ -85,7 +85,7 @@ public function can_find_random_set_of_objects(): void $objects = $factory->randomSet(3); $this->assertCount(3, $objects); - $this->assertCount(3, \array_unique(\array_map(static function($category) { return $category->getId(); }, $objects))); + $this->assertCount(3, \array_unique(\array_map(static fn($category) => $category->getId(), $objects))); } /** @@ -153,7 +153,7 @@ public function first_and_last_return_the_correct_object(): void { $factory = AnonymousFactory::new($this->categoryClass()); $categoryA = $factory->create(['name' => '3']); - $categoryB = $factory->create(['name' => '2']); + $factory->create(['name' => '2']); $categoryC = $factory->create(['name' => '1']); $this->assertSame($categoryA->getId(), $factory->first()->getId()); @@ -233,6 +233,7 @@ public function can_find_entity(): void $factory->create(['name' => 'first']); $factory->create(['name' => 'second']); + $category = $factory->create(['name' => 'third']); $this->assertSame('second', $factory->find(['name' => 'second'])->getName()); diff --git a/tests/Functional/Bundle/Maker/MakeFactoryTest.php b/tests/Functional/Bundle/Maker/MakeFactoryTest.php index 84d4d14be..48f1e3fc4 100644 --- a/tests/Functional/Bundle/Maker/MakeFactoryTest.php +++ b/tests/Functional/Bundle/Maker/MakeFactoryTest.php @@ -109,6 +109,7 @@ public function can_create_factory_interactively(): void $tester->setInputs([Tag::class]); $tester->execute([]); + $output = $tester->getDisplay(); $this->assertStringNotContainsString(Category::class, $output); @@ -271,6 +272,7 @@ public function can_create_factory_in_test_dir_interactively(): void $tester->setInputs([Tag::class]); $tester->execute(['--test' => true]); + $output = $tester->getDisplay(); $this->assertFileExists(self::tempFile('tests/Factory/TagFactory.php')); @@ -458,7 +460,7 @@ public function can_create_factory_with_all_interactively(): void try { $tester->execute([]); - } catch (RuntimeCommandException $e) { + } catch (RuntimeCommandException) { // todo find a better solution // because we have fixtures with the same name, the maker will fail when creating the duplicate } diff --git a/tests/Functional/Bundle/Maker/MakeStoryTest.php b/tests/Functional/Bundle/Maker/MakeStoryTest.php index 980ed5c97..97901422c 100644 --- a/tests/Functional/Bundle/Maker/MakeStoryTest.php +++ b/tests/Functional/Bundle/Maker/MakeStoryTest.php @@ -14,7 +14,7 @@ final class MakeStoryTest extends MakerTestCase * @test * @dataProvider storyNameProvider */ - public function can_create_story($name): void + public function can_create_story(string $name): void { $tester = new CommandTester((new Application(self::bootKernel()))->find('make:story')); @@ -47,7 +47,7 @@ public function build(): void * @test * @dataProvider storyNameProvider */ - public function can_create_story_interactively($name): void + public function can_create_story_interactively(string $name): void { $tester = new CommandTester((new Application(self::bootKernel()))->find('make:story')); @@ -55,6 +55,7 @@ public function can_create_story_interactively($name): void $tester->setInputs([$name]); $tester->execute([]); + $output = $tester->getDisplay(); $this->assertFileExists(self::tempFile('src/Story/FooBarStory.php')); @@ -83,7 +84,7 @@ public function build(): void * @test * @dataProvider storyNameProvider */ - public function can_create_story_in_test_dir($name): void + public function can_create_story_in_test_dir(string $name): void { $tester = new CommandTester((new Application(self::bootKernel()))->find('make:story')); @@ -116,7 +117,7 @@ public function build(): void * @test * @dataProvider storyNameProvider */ - public function can_create_story_in_test_dir_interactively($name): void + public function can_create_story_in_test_dir_interactively(string $name): void { $tester = new CommandTester((new Application(self::bootKernel()))->find('make:story')); @@ -124,6 +125,7 @@ public function can_create_story_in_test_dir_interactively($name): void $tester->setInputs([$name]); $tester->execute(['--test' => true]); + $output = $tester->getDisplay(); $this->assertFileExists(self::tempFile('tests/Story/FooBarStory.php')); diff --git a/tests/Functional/FactoryTest.php b/tests/Functional/FactoryTest.php index 407cf2ef8..169ce032c 100644 --- a/tests/Functional/FactoryTest.php +++ b/tests/Functional/FactoryTest.php @@ -55,9 +55,7 @@ public function one_to_many_relationship(): void ]); $posts = \array_map( - static function($post) { - return $post->getTitle(); - }, + static fn($post) => $post->getTitle(), $category->getPosts()->toArray() ); @@ -81,9 +79,7 @@ public function many_to_many_relationship(): void ]); $tags = \array_map( - static function($tag) { - return $tag->getName(); - }, + static fn($tag) => $tag->getName(), $post->getTags()->toArray() ); @@ -106,9 +102,7 @@ public function many_to_many_reverse_relationship(): void ]); $posts = \array_map( - static function($post) { - return $post->getTitle(); - }, + static fn($post) => $post->getTitle(), $tag->getPosts()->toArray() ); @@ -148,13 +142,12 @@ public function can_delay_flush(): void AnonymousFactory::new(Post::class)->assert()->empty(); AnonymousFactory::new(Category::class)->assert()->empty(); - AnonymousFactory::delayFlush(function() { + AnonymousFactory::delayFlush(static function(): void { AnonymousFactory::new(Post::class)->create([ 'title' => 'title', 'body' => 'body', 'category' => AnonymousFactory::new(Category::class, ['name' => 'name']), ]); - AnonymousFactory::new(Post::class)->assert()->empty(); AnonymousFactory::new(Category::class)->assert()->empty(); }); @@ -171,16 +164,14 @@ public function auto_refresh_is_disabled_during_delay_flush(): void AnonymousFactory::new(Post::class)->assert()->empty(); AnonymousFactory::new(Category::class)->assert()->empty(); - AnonymousFactory::delayFlush(function() { + AnonymousFactory::delayFlush(static function(): void { $post = AnonymousFactory::new(Post::class)->create([ 'title' => 'title', 'body' => 'body', 'category' => AnonymousFactory::new(Category::class, ['name' => 'name']), ]); - $post->setTitle('new title'); $post->setBody('new body'); - AnonymousFactory::new(Post::class)->assert()->empty(); AnonymousFactory::new(Category::class)->assert()->empty(); }); diff --git a/tests/Functional/GlobalStateTest.php b/tests/Functional/GlobalStateTest.php index d79fef2ef..f80a009fa 100644 --- a/tests/Functional/GlobalStateTest.php +++ b/tests/Functional/GlobalStateTest.php @@ -20,8 +20,6 @@ protected function setUp(): void if (!\getenv('USE_FOUNDRY_BUNDLE')) { $this->markTestSkipped('ZenstruckFoundryBundle not enabled.'); } - - parent::setUp(); } /** diff --git a/tests/Functional/ModelFactoryTest.php b/tests/Functional/ModelFactoryTest.php index 625c8f293..c6bef6417 100644 --- a/tests/Functional/ModelFactoryTest.php +++ b/tests/Functional/ModelFactoryTest.php @@ -89,7 +89,7 @@ public function can_find_random_set_of_objects(): void $objects = $categoryFactoryClass::randomSet(3); $this->assertCount(3, $objects); - $this->assertCount(3, \array_unique(\array_map(static function($category) { return $category->getId(); }, $objects))); + $this->assertCount(3, \array_unique(\array_map(static fn($category) => $category->getId(), $objects))); } /** @@ -161,7 +161,7 @@ public function first_and_last_return_the_correct_object(): void $categoryFactoryClass = $this->categoryFactoryClass(); $categoryA = $categoryFactoryClass::createOne(['name' => '3']); - $categoryB = $categoryFactoryClass::createOne(['name' => '2']); + $categoryFactoryClass::createOne(['name' => '2']); $categoryC = $categoryFactoryClass::createOne(['name' => '1']); $this->assertSame($categoryA->getId(), $categoryFactoryClass::first()->getId()); @@ -287,7 +287,7 @@ public function resave_after_persist_events(): void $categoryFactoryClass = $this->categoryFactoryClass(); $categoryFactoryClass::new() - ->afterPersist(function($category) { + ->afterPersist(static function($category): void { $category->setName('new'); }) ->create(['name' => 'original']) @@ -301,7 +301,7 @@ public function resave_after_persist_events(): void * @test * @dataProvider sequenceProvider */ - public function can_create_sequence($sequence): void + public function can_create_sequence(\Closure|string|array $sequence): void { $categoryFactoryClass = $this->categoryFactoryClass(); $categoryFactoryClass::createSequence($sequence); @@ -317,9 +317,7 @@ public function sequenceProvider(): iterable ]; yield 'with a callable which returns an array of attributes' => [ - static function(): array { - return [['name' => 'foo'], ['name' => 'bar']]; - }, + static fn(): array => [['name' => 'foo'], ['name' => 'bar']], ]; yield 'with a callable which yields attributes' => [ @@ -336,11 +334,9 @@ static function(): \Generator { public function can_create_many_objects_with_index(): void { $categoryFactoryClass = $this->categoryFactoryClass(); - $categoryFactoryClass::createMany(2, static function(int $i) { - return [ - 'name' => "foo {$i}", - ]; - }); + $categoryFactoryClass::createMany(2, static fn(int $i): array => [ + 'name' => "foo {$i}", + ]); $categoryFactoryClass::assert()->exists(['name' => 'foo 1']); $categoryFactoryClass::assert()->exists(['name' => 'foo 2']); diff --git a/tests/Functional/ORMDatabaseResetterTest.php b/tests/Functional/ORMDatabaseResetterTest.php index 9112b0db3..84835c551 100644 --- a/tests/Functional/ORMDatabaseResetterTest.php +++ b/tests/Functional/ORMDatabaseResetterTest.php @@ -32,6 +32,7 @@ public function it_resets_database_correctly(string $resetMode): void { $kernel = static::createKernel(['ormResetMode' => $resetMode]); $kernel->boot(); + $container = $kernel->getContainer(); $application = new Application($kernel); diff --git a/tests/Functional/ORMRepositoryProxyTest.php b/tests/Functional/ORMRepositoryProxyTest.php index e5e555a37..26cffb462 100644 --- a/tests/Functional/ORMRepositoryProxyTest.php +++ b/tests/Functional/ORMRepositoryProxyTest.php @@ -57,8 +57,8 @@ public function doctrine_proxies_are_converted_to_foundry_proxies(): void */ public function proxy_wrapping_orm_entity_manager_can_order_by_in_find_one_by(): void { - $categoryA = CategoryFactory::createOne(); - $categoryB = CategoryFactory::createOne(); + CategoryFactory::createOne(); + CategoryFactory::createOne(); $categoryC = CategoryFactory::createOne(); $this->assertSame($categoryC->getId(), CategoryFactory::repository()->findOneBy([], ['id' => 'DESC'])->getId()); diff --git a/tests/Functional/ProxyTest.php b/tests/Functional/ProxyTest.php index 452954b66..258f13bfe 100644 --- a/tests/Functional/ProxyTest.php +++ b/tests/Functional/ProxyTest.php @@ -23,7 +23,7 @@ public function can_assert_persisted(): void { $this->postFactoryClass()::createOne()->assertPersisted(); - Assert::that(function() { $this->postFactoryClass()::new()->withoutPersisting()->create()->assertPersisted(); }) + Assert::that(function(): void { $this->postFactoryClass()::new()->withoutPersisting()->create()->assertPersisted(); }) ->throws(AssertionFailedError::class, \sprintf('%s is not persisted.', $this->postClass())) ; } @@ -35,7 +35,7 @@ public function can_assert_not_persisted(): void { $this->postFactoryClass()::new()->withoutPersisting()->create()->assertNotPersisted(); - Assert::that(function() { $this->postFactoryClass()::createOne()->assertNotPersisted(); }) + Assert::that(function(): void { $this->postFactoryClass()::createOne()->assertNotPersisted(); }) ->throws(AssertionFailedError::class, \sprintf('%s is persisted but it should not be.', $this->postClass())) ; } @@ -215,7 +215,7 @@ public function without_auto_refresh_solves_the_auto_refresh_problem(): void $post ->enableAutoRefresh() - ->withoutAutoRefresh(static function(Proxy $proxy) { + ->withoutAutoRefresh(static function(Proxy $proxy): void { $proxy ->forceSet('title', 'new title') ->forceSet('body', 'new body') @@ -239,7 +239,7 @@ public function without_auto_refresh_does_not_enable_auto_refresh_if_it_was_disa $this->assertSame('old body', $post->getBody()); $post - ->withoutAutoRefresh(static function(Proxy $proxy) { + ->withoutAutoRefresh(static function(Proxy $proxy): void { $proxy ->forceSet('title', 'new title') ->forceSet('body', 'new body') @@ -265,7 +265,7 @@ public function without_auto_refresh_keeps_disabled_if_originally_disabled(): vo $this->assertSame('old body', $post->getBody()); $post - ->withoutAutoRefresh(static function(Proxy $proxy) { + ->withoutAutoRefresh(static function(Proxy $proxy): void { $proxy ->forceSet('title', 'new title') ->forceSet('body', 'new body') diff --git a/tests/Functional/RepositoryProxyTest.php b/tests/Functional/RepositoryProxyTest.php index 502aa7f93..28544e4d4 100644 --- a/tests/Functional/RepositoryProxyTest.php +++ b/tests/Functional/RepositoryProxyTest.php @@ -38,28 +38,44 @@ public function assertions(): void $repository->assert()->exists([]); $repository->assert()->notExists(['name' => 'invalid']); - Assert::that(function() use ($repository) { $repository->assert()->empty(); }) + Assert::that(static function() use ($repository): void { + $repository->assert()->empty(); + }) ->throws(AssertionFailedError::class, \sprintf('Expected %s repository to be empty but it has 2 items.', $this->categoryClass())) ; - Assert::that(function() use ($repository) { $repository->assert()->count(1); }) + Assert::that(static function() use ($repository): void { + $repository->assert()->count(1); + }) ->throws(AssertionFailedError::class, \sprintf('Expected count of %s repository (2) to be 1.', $this->categoryClass())) ; - Assert::that(function() use ($repository) { $repository->assert()->countGreaterThan(2); }) + Assert::that(static function() use ($repository): void { + $repository->assert()->countGreaterThan(2); + }) ->throws(AssertionFailedError::class, \sprintf('Expected count of %s repository (2) to be greater than 2.', $this->categoryClass())) ; - Assert::that(function() use ($repository) { $repository->assert()->countGreaterThanOrEqual(3); }) + Assert::that(static function() use ($repository): void { + $repository->assert()->countGreaterThanOrEqual(3); + }) ->throws(AssertionFailedError::class, \sprintf('Expected count of %s repository (2) to be greater than or equal to 3.', $this->categoryClass())) ; - Assert::that(function() use ($repository) { $repository->assert()->countLessThan(2); }) + Assert::that(static function() use ($repository): void { + $repository->assert()->countLessThan(2); + }) ->throws(AssertionFailedError::class, \sprintf('Expected count of %s repository (2) to be less than 2.', $this->categoryClass())) ; - Assert::that(function() use ($repository) { $repository->assert()->countLessThanOrEqual(1); }) + Assert::that(static function() use ($repository): void { + $repository->assert()->countLessThanOrEqual(1); + }) ->throws(AssertionFailedError::class, \sprintf('Expected count of %s repository (2) to be less than or equal to 1.', $this->categoryClass())) ; - Assert::that(function() use ($repository) { $repository->assert()->exists(['name' => 'invalid-name']); }) + Assert::that(static function() use ($repository): void { + $repository->assert()->exists(['name' => 'invalid-name']); + }) ->throws(AssertionFailedError::class, \sprintf('Expected %s to exist but it does not.', $this->categoryClass())) ; - Assert::that(function() use ($repository) { $repository->assert()->notExists([]); }) + Assert::that(static function() use ($repository): void { + $repository->assert()->notExists([]); + }) ->throws(AssertionFailedError::class, \sprintf('Expected %s to not exist but it does.', $this->categoryClass())) ; } @@ -167,9 +183,7 @@ public function can_find_random_set_of_objects(): void 3, \array_unique( \array_map( - static function($category) { - return $category->getId(); - }, + static fn($category) => $category->getId(), $objects ) ) @@ -265,7 +279,7 @@ public function first_and_last_return_the_correct_object(): void $categoryFactoryClass = $this->categoryFactoryClass(); $categoryA = $categoryFactoryClass::createOne(['name' => '3']); - $categoryB = $categoryFactoryClass::createOne(['name' => '2']); + $categoryFactoryClass::createOne(['name' => '2']); $categoryC = $categoryFactoryClass::createOne(['name' => '1']); $repository = $categoryFactoryClass::repository(); diff --git a/tests/Functional/StoryTest.php b/tests/Functional/StoryTest.php index b0e1b55bb..3ae8a1d76 100644 --- a/tests/Functional/StoryTest.php +++ b/tests/Functional/StoryTest.php @@ -131,7 +131,7 @@ public function can_get_random_object_set_from_pool(): void $objects = CategoryPoolStory::getRandomSet('pool-name', 3); $this->assertCount(3, $objects); - $this->assertCount(3, \array_unique(\array_map(static function($category) { return $category->getId(); }, $objects))); + $this->assertCount(3, \array_unique(\array_map(static fn($category) => $category->getId(), $objects))); } /** diff --git a/tests/Unit/Bundle/DependencyInjection/ZenstruckFoundryExtensionTest.php b/tests/Unit/Bundle/DependencyInjection/ZenstruckFoundryExtensionTest.php index 208c78cd5..cb2f7932f 100644 --- a/tests/Unit/Bundle/DependencyInjection/ZenstruckFoundryExtensionTest.php +++ b/tests/Unit/Bundle/DependencyInjection/ZenstruckFoundryExtensionTest.php @@ -181,6 +181,9 @@ public function can_disable_auto_refresh_proxies(): void $this->assertContainerBuilderHasServiceDefinitionWithMethodCall(Configuration::class, 'disableDefaultProxyAutoRefresh', []); } + /** + * @return ZenstruckFoundryExtension[] + */ protected function getContainerExtensions(): array { return [new ZenstruckFoundryExtension()]; diff --git a/tests/Unit/FactoryTest.php b/tests/Unit/FactoryTest.php index 94a19f2ae..4dd31a0bd 100644 --- a/tests/Unit/FactoryTest.php +++ b/tests/Unit/FactoryTest.php @@ -27,9 +27,7 @@ final class FactoryTest extends TestCase public function can_instantiate_object(): void { $attributeArray = ['title' => 'title', 'body' => 'body']; - $attributeCallback = static function() { - return ['title' => 'title', 'body' => 'body']; - }; + $attributeCallback = static fn(): array => ['title' => 'title', 'body' => 'body']; $this->assertSame('title', (new AnonymousFactory(Post::class, $attributeArray))->create()->getTitle()); $this->assertSame('title', (new AnonymousFactory(Post::class))->create($attributeArray)->getTitle()); @@ -46,9 +44,7 @@ public function can_instantiate_object(): void public function can_instantiate_many_objects_legacy(): void { $attributeArray = ['title' => 'title', 'body' => 'body']; - $attributeCallback = static function() { - return ['title' => 'title', 'body' => 'body']; - }; + $attributeCallback = static fn(): array => ['title' => 'title', 'body' => 'body']; $objects = (new Factory(Post::class, $attributeArray))->createMany(3); @@ -101,7 +97,7 @@ public function can_set_instantiator(): void $attributeArray = ['title' => 'original title', 'body' => 'original body']; $object = (new AnonymousFactory(Post::class)) - ->instantiateWith(function(array $attributes, string $class) use ($attributeArray) { + ->instantiateWith(function(array $attributes, string $class) use ($attributeArray): Post { $this->assertSame(Post::class, $class); $this->assertSame($attributes, $attributeArray); @@ -122,12 +118,12 @@ public function can_add_before_instantiate_events(): void $attributeArray = ['title' => 'original title', 'body' => 'original body']; $object = (new AnonymousFactory(Post::class)) - ->beforeInstantiate(function(array $attributes) { + ->beforeInstantiate(static function(array $attributes): array { $attributes['title'] = 'title'; return $attributes; }) - ->beforeInstantiate(function(array $attributes) { + ->beforeInstantiate(static function(array $attributes): array { $attributes['body'] = 'body'; return $attributes; @@ -147,7 +143,7 @@ public function before_instantiate_event_must_return_an_array(): void $this->expectException(\LogicException::class); $this->expectExceptionMessage('Before Instantiate event callback must return an array.'); - (new AnonymousFactory(Post::class))->beforeInstantiate(function() {})->create(); + (new AnonymousFactory(Post::class))->beforeInstantiate(static function(): void {})->create(); } /** @@ -158,12 +154,12 @@ public function can_add_after_instantiate_events(): void $attributesArray = ['title' => 'title', 'body' => 'body']; $object = (new AnonymousFactory(Post::class)) - ->afterInstantiate(function(Post $post, array $attributes) use ($attributesArray) { + ->afterInstantiate(function(Post $post, array $attributes) use ($attributesArray): void { $this->assertSame($attributesArray, $attributes); $post->increaseViewCount(); }) - ->afterInstantiate(function(Post $post, array $attributes) use ($attributesArray) { + ->afterInstantiate(function(Post $post, array $attributes) use ($attributesArray): void { $this->assertSame($attributesArray, $attributes); $post->increaseViewCount(); @@ -190,9 +186,7 @@ public function can_register_custom_faker(): void */ public function can_register_default_instantiator(): void { - Factory::configuration()->setInstantiator(function() { - return new Post('different title', 'different body'); - }); + Factory::configuration()->setInstantiator(static fn(): Post => new Post('different title', 'different body')); $object = (new AnonymousFactory(Post::class, ['title' => 'title', 'body' => 'body']))->create(); @@ -238,10 +232,10 @@ public function factory_is_immutable(): void $this->assertNotSame(\spl_object_id($factory->withAttributes([])), $objectId); $this->assertNotSame(\spl_object_id($factory->withoutPersisting()), $objectId); - $this->assertNotSame(\spl_object_id($factory->instantiateWith(function() {})), $objectId); - $this->assertNotSame(\spl_object_id($factory->beforeInstantiate(function() {})), $objectId); - $this->assertNotSame(\spl_object_id($factory->afterInstantiate(function() {})), $objectId); - $this->assertNotSame(\spl_object_id($factory->afterPersist(function() {})), $objectId); + $this->assertNotSame(\spl_object_id($factory->instantiateWith(static function(): void {})), $objectId); + $this->assertNotSame(\spl_object_id($factory->beforeInstantiate(static function(): void {})), $objectId); + $this->assertNotSame(\spl_object_id($factory->afterInstantiate(static function(): void {})), $objectId); + $this->assertNotSame(\spl_object_id($factory->afterPersist(static function(): void {})), $objectId); } /** @@ -308,31 +302,31 @@ public function can_add_after_persist_events(): void $calls = 0; $object = (new AnonymousFactory(Post::class, ['shortDescription' => 'short desc'])) - ->afterPersist(function(Proxy $post, array $attributes) use ($expectedAttributes, &$calls) { + ->afterPersist(function(Proxy $post, array $attributes) use ($expectedAttributes, &$calls): void { /* @var Post $post */ $this->assertSame($expectedAttributes, $attributes); $post->increaseViewCount(); ++$calls; }) - ->afterPersist(function(Post $post, array $attributes) use ($expectedAttributes, &$calls) { + ->afterPersist(function(Post $post, array $attributes) use ($expectedAttributes, &$calls): void { $this->assertSame($expectedAttributes, $attributes); $post->increaseViewCount(); ++$calls; }) - ->afterPersist(function(Post $post, array $attributes) use ($expectedAttributes, &$calls) { + ->afterPersist(function(Post $post, array $attributes) use ($expectedAttributes, &$calls): void { $this->assertSame($expectedAttributes, $attributes); $post->increaseViewCount(); ++$calls; }) - ->afterPersist(function($post) use (&$calls) { + ->afterPersist(function($post) use (&$calls): void { $this->assertInstanceOf(Proxy::class, $post); ++$calls; }) - ->afterPersist(static function() use (&$calls) { + ->afterPersist(static function() use (&$calls): void { ++$calls; }) ->create(['title' => 'title', 'body' => 'body']) diff --git a/tests/Unit/InstantiatorTest.php b/tests/Unit/InstantiatorTest.php index 71d4a51d9..6ed03fa15 100644 --- a/tests/Unit/InstantiatorTest.php +++ b/tests/Unit/InstantiatorTest.php @@ -537,7 +537,7 @@ public function missing_variadic_argument_thtrows(): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Missing constructor argument "propB" for "Zenstruck\Foundry\Tests\Unit\VariadicInstantiatorDummy".'); - $object = (new Instantiator())([ + (new Instantiator())([ 'propA' => 'A', ], VariadicInstantiatorDummy::class); } @@ -546,11 +546,16 @@ public function missing_variadic_argument_thtrows(): void class InstantiatorDummy { public $propA; + public $propD; - private $propB; - private $propC; + + private string $propB; + + private ?string $propC = null; + private $propE; - private $propF; + + private ?object $propF = null; public function __construct($propB, $propC = null) { @@ -566,22 +571,22 @@ public function getPropA() return $this->propA; } - public function getPropB() + public function getPropB(): string { return $this->propB; } - public function setPropB($propB) + public function setPropB($propB): void { $this->propB = 'setter '.$propB; } - public function getPropC() + public function getPropC(): ?string { return $this->propC; } - public function setPropC($propC) + public function setPropC($propC): void { $this->propC = 'setter '.$propC; } @@ -591,12 +596,12 @@ public function getPropD() return $this->propD; } - public function setPropD($propD) + public function setPropD($propD): void { $this->propD = 'setter '.$propD; } - public function setPropF(object $propF) + public function setPropF(object $propF): void { $this->propF = $propF; } @@ -616,8 +621,9 @@ private function __construct() class VariadicInstantiatorDummy { - private $propA; - private $propB; + private string $propA; + + private array $propB; public function __construct($propA, ...$propB) { @@ -625,12 +631,15 @@ public function __construct($propA, ...$propB) $this->propB = $propB; } - public function getPropA() + public function getPropA(): string { return $this->propA; } - public function getPropB() + /** + * @return array + */ + public function getPropB(): array { return $this->propB; } diff --git a/tests/Unit/ProxyTest.php b/tests/Unit/ProxyTest.php index 3d23348da..38324eb23 100644 --- a/tests/Unit/ProxyTest.php +++ b/tests/Unit/ProxyTest.php @@ -96,57 +96,57 @@ public function can_use_without_auto_refresh_with_proxy_or_object_typehint(): vo $calls = 0; $proxy - ->withoutAutoRefresh(static function(Proxy $proxy) use (&$calls) { + ->withoutAutoRefresh(static function(Proxy $proxy) use (&$calls): void { ++$calls; }) - ->withoutAutoRefresh(static function(Category $category) use (&$calls) { + ->withoutAutoRefresh(static function(Category $category) use (&$calls): void { ++$calls; }) - ->withoutAutoRefresh(function($proxy) use (&$calls) { + ->withoutAutoRefresh(function($proxy) use (&$calls): void { $this->assertInstanceOf(Proxy::class, $proxy); ++$calls; }) - ->withoutAutoRefresh(static function() use (&$calls) { + ->withoutAutoRefresh(static function() use (&$calls): void { ++$calls; }) - ->withoutAutoRefresh([$this, 'functionWithProxy']) - ->withoutAutoRefresh([$this, 'functionWithObject']) - ->withoutAutoRefresh([$this, 'functionWithNoTypeHint']) - ->withoutAutoRefresh([self::class, 'staticFunctionWithProxy']) - ->withoutAutoRefresh([self::class, 'staticFunctionWithObject']) - ->withoutAutoRefresh([self::class, 'staticFunctionWithNoTypeHint']) + ->withoutAutoRefresh(fn(Proxy $proxy) => $this->functionWithProxy($proxy)) + ->withoutAutoRefresh(fn(Category $category) => $this->functionWithObject($category)) + ->withoutAutoRefresh(fn($proxy) => $this->functionWithNoTypeHint($proxy)) + ->withoutAutoRefresh(static fn(Proxy $proxy) => self::staticFunctionWithProxy($proxy)) + ->withoutAutoRefresh(static fn(Category $category) => self::staticFunctionWithObject($category)) + ->withoutAutoRefresh(static fn($proxy) => self::staticFunctionWithNoTypeHint($proxy)) ; $this->assertSame(4, $calls); } - public function functionWithProxy(Proxy $proxy) + public function functionWithProxy(Proxy $proxy): void { $this->assertInstanceOf(Category::class, $proxy->object()); } - public function functionWithObject(Category $category) + public function functionWithObject(Category $category): void { $this->assertInstanceOf(Category::class, $category); } - public function functionWithNoTypeHint($proxy) + public function functionWithNoTypeHint($proxy): void { $this->assertInstanceOf(Proxy::class, $proxy); $this->assertInstanceOf(Category::class, $proxy->object()); } - public static function staticFunctionWithProxy(Proxy $proxy) + public static function staticFunctionWithProxy(Proxy $proxy): void { self::assertInstanceOf(Category::class, $proxy->object()); } - public static function staticFunctionWithObject(Category $category) + public static function staticFunctionWithObject(Category $category): void { self::assertInstanceOf(Category::class, $category); } - public static function staticFunctionWithNoTypeHint($proxy) + public static function staticFunctionWithNoTypeHint($proxy): void { self::assertInstanceOf(Proxy::class, $proxy); self::assertInstanceOf(Category::class, $proxy->object()); diff --git a/tests/Unit/RepositoryProxyTest.php b/tests/Unit/RepositoryProxyTest.php index aa03c4d2f..c3fcf13c1 100644 --- a/tests/Unit/RepositoryProxyTest.php +++ b/tests/Unit/RepositoryProxyTest.php @@ -27,25 +27,25 @@ public function calling_find_one_by_with_order_by_when_wrapped_repo_does_not_hav public static function objectRepositoryWithoutFindOneByOrderBy(): iterable { yield [new RepositoryProxy(new class() extends RepositoryStub { - public function findOneBy(array $criteria): ?object + public function findOneBy(array $criteria): void { } })]; yield [new RepositoryProxy(new class() extends RepositoryStub { - public function findOneBy(array $criteria, ?array $foo = null): ?object + public function findOneBy(array $criteria, ?array $foo = null): void { } })]; yield [new RepositoryProxy(new class() extends RepositoryStub { - public function findOneBy(array $criteria, $orderBy = null): ?object + public function findOneBy(array $criteria, $orderBy = null): void { } })]; yield [new RepositoryProxy(new class() extends RepositoryStub { - public function findOneBy(array $criteria, ?string $orderBy = null): ?object + public function findOneBy(array $criteria, ?string $orderBy = null): void { } })]; From 210faff9429ae8777001b9c89178d4a7c87c3026 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Fri, 11 Nov 2022 17:51:51 +0100 Subject: [PATCH 04/30] [chore] use phpstan instead of psalm (#328) --- .gitattributes | 2 +- .github/workflows/ci.yml | 8 +- Makefile | 12 +- README.md | 2 +- bin/tools/phpstan/composer.json | 13 + bin/tools/phpstan/composer.lock | 258 ++ bin/tools/psalm/composer.json | 11 - bin/tools/psalm/composer.lock | 3678 ----------------- composer.json | 1 + phpstan-baseline.neon | 61 + phpstan.neon | 36 + psalm.xml | 48 - src/AnonymousFactory.php | 14 +- .../DependencyInjection/Configuration.php | 2 +- .../ZenstruckFoundryExtension.php | 3 - src/Bundle/Maker/MakeFactory.php | 3 + src/ChainManagerRegistry.php | 23 +- src/Configuration.php | 23 +- src/Factory.php | 35 +- src/FactoryCollection.php | 8 +- src/Instantiator.php | 32 +- src/ModelFactory.php | 43 +- src/ModelFactoryManager.php | 3 + src/Proxy.php | 37 +- src/RepositoryProxy.php | 61 +- src/Story.php | 12 +- src/StoryManager.php | 19 +- src/Test/LazyManagerRegistry.php | 3 + src/functions.php | 40 +- tests/Unit/ChainManagerRegistryTest.php | 6 + 30 files changed, 584 insertions(+), 3913 deletions(-) create mode 100644 bin/tools/phpstan/composer.json create mode 100644 bin/tools/phpstan/composer.lock delete mode 100644 bin/tools/psalm/composer.json delete mode 100644 bin/tools/psalm/composer.lock create mode 100644 phpstan-baseline.neon create mode 100644 phpstan.neon delete mode 100644 psalm.xml diff --git a/.gitattributes b/.gitattributes index e90f1066b..1842989ac 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,7 +12,7 @@ /docker export-ignore /docker-compose.yaml export-ignore /Makefile export-ignore +/phpstan.neon export-ignore /phpunit-dama-doctrine.xml.dist export-ignore /phpunit.xml.dist export-ignore -/psalm.xml export-ignore /tests export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cfb64f62..132a15e8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -187,7 +187,7 @@ jobs: run: bin/tools/cs-fixer/vendor/friendsofphp/php-cs-fixer/php-cs-fixer fix --dry-run --diff static-analysis: - name: Psalm Static Analysis + name: Static Analysis runs-on: ubuntu-latest steps: - name: Checkout code @@ -204,11 +204,11 @@ jobs: with: composer-options: --prefer-dist - - name: Install Psalm - run: composer bin psalm install + - name: Install PhpStan + run: composer bin phpstan install - name: Run static analysis - run: bin/tools/psalm/vendor/vimeo/psalm/psalm --output-format=github + run: bin/tools/phpstan/vendor/phpstan/phpstan/phpstan analyse docker-stack: name: CI with docker stack diff --git a/Makefile b/Makefile index bc04dc30c..a250ef334 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: $(filter-out vendor bin/tools/psalm/vendor bin/tools/cs-fixer/vendor,$(MAKECMDGOALS)) +.PHONY: $(filter-out vendor bin/tools/phpstan/vendor bin/tools/cs-fixer/vendor,$(MAKECMDGOALS)) MYSQL_URL="mysql://root:root@mysql:3306/zenstruck_foundry?charset=utf8" MONGO_URL="mongodb://mongo:mongo@mongo:27017/mongo?compressors=disabled&gssapiServiceName=mongodb&authSource=mongo" @@ -59,11 +59,11 @@ fixcs: docker-start bin/tools/cs-fixer/vendor ### Run PHP CS-Fixer bin/tools/cs-fixer/vendor: vendor bin/tools/cs-fixer/composer.json bin/tools/cs-fixer/composer.lock @${DOCKER_PHP} composer bin cs-fixer install -sca: docker-start bin/tools/psalm/vendor ### Run Psalm - @${DOCKER_PHP} bin/tools/psalm/vendor/vimeo/psalm/psalm --config=./psalm.xml +sca: docker-start bin/tools/phpstan/vendor ### Run static analysis + @${DOCKER_PHP} bin/tools/phpstan/vendor/phpstan/phpstan/phpstan analyse -bin/tools/psalm/vendor: vendor bin/tools/psalm/composer.json bin/tools/psalm/composer.lock - @${DOCKER_PHP} composer bin psalm install +bin/tools/phpstan/vendor: vendor bin/tools/phpstan/composer.json $(wildcard bin/tools/phpstan/composer.lock) + @${DOCKER_PHP} composer bin phpstan install database-generate-migration: docker-start vendor ### Generate new migration based on mapping in Zenstruck\Foundry\Tests\Fixtures\Entity @${DOCKER_PHP} vendor/bin/doctrine-migrations migrations:migrate --no-interaction --allow-no-migration # first, let's load into db existing migrations @@ -99,4 +99,4 @@ composer: ### Run composer command @${DC_EXEC} php composer $(ARGS) clear: ### Start from a fresh install (needed if vendors have already been installed with another php version) - rm -rf composer.lock bin/tools/psalm/vendor/ bin/tools/cs-fixer/vendor/ vendor/ + rm -rf composer.lock bin/tools/phpstan/vendor/ bin/tools/cs-fixer/vendor/ vendor/ diff --git a/README.md b/README.md index eeb773a06..d2c898319 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ test-mysql Run PHPunit with mysql test-postgresql Run PHPunit with postgreSQL test-mongo Run PHPunit with Mongo fixcs Run PHP CS-Fixer -sca Run Psalm +sca Run static analysis database-generate-migration Generate new migration based on mapping in Zenstruck\Foundry\Tests\Fixtures\Entity database-validate-mapping Validate mapping in Zenstruck\Foundry\Tests\Fixtures\Entity database-drop-schema Drop database schema diff --git a/bin/tools/phpstan/composer.json b/bin/tools/phpstan/composer.json new file mode 100644 index 000000000..059b57242 --- /dev/null +++ b/bin/tools/phpstan/composer.json @@ -0,0 +1,13 @@ +{ + "require": { + "ekino/phpstan-banned-code": "^1.0", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-symfony": "^1.2", + "phpstan/extension-installer": "1.2" + }, + "config": { + "allow-plugins": { + "phpstan/extension-installer": true + } + } +} diff --git a/bin/tools/phpstan/composer.lock b/bin/tools/phpstan/composer.lock new file mode 100644 index 000000000..c00ba9a79 --- /dev/null +++ b/bin/tools/phpstan/composer.lock @@ -0,0 +1,258 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "d206c71f54ea1919bc14383e1f797db1", + "packages": [ + { + "name": "ekino/phpstan-banned-code", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/ekino/phpstan-banned-code.git", + "reference": "4f0d7c8a0c9f5d222ffc24234aa6c5b3b71bf4c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ekino/phpstan-banned-code/zipball/4f0d7c8a0c9f5d222ffc24234aa6c5b3b71bf4c3", + "reference": "4f0d7c8a0c9f5d222ffc24234aa6c5b3b71bf4c3", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0", + "phpstan/phpstan": "^1.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.6", + "friendsofphp/php-cs-fixer": "^3.0", + "nikic/php-parser": "^4.3", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/var-dumper": "^5.0" + }, + "type": "phpstan-extension", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Ekino\\PHPStanBannedCode\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rémi Marseille", + "email": "remi.marseille@ekino.com", + "homepage": "https://www.ekino.com" + } + ], + "description": "Detected banned code using PHPStan", + "homepage": "https://github.com/ekino/phpstan-banned-code", + "keywords": [ + "PHPStan", + "code quality" + ], + "support": { + "issues": "https://github.com/ekino/phpstan-banned-code/issues", + "source": "https://github.com/ekino/phpstan-banned-code/tree/v1.0.0" + }, + "time": "2021-11-02T08:37:34+00:00" + }, + { + "name": "phpstan/extension-installer", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f06dbb052ddc394e7896fcd1cfcd533f9f6ace40", + "reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.8.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.2.0" + }, + "time": "2022-10-17T12:59:16+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.9.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d6fdf01c53978b6429f1393ba4afeca39cc68afa", + "reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.9.2" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-11-10T09:56:11+00:00" + }, + { + "name": "phpstan/phpstan-symfony", + "version": "1.2.16", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-symfony.git", + "reference": "d6ea16206b1b645ded5b43736d8ef5ae1168eb55" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/d6ea16206b1b645ded5b43736d8ef5ae1168eb55", + "reference": "d6ea16206b1b645ded5b43736d8ef5ae1168eb55", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.1" + }, + "conflict": { + "symfony/framework-bundle": "<3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.13.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "psr/container": "1.0 || 1.1.1", + "symfony/config": "^5.4 || ^6.1", + "symfony/console": "^5.4 || ^6.1", + "symfony/dependency-injection": "^5.4 || ^6.1", + "symfony/form": "^5.4 || ^6.1", + "symfony/framework-bundle": "^5.4 || ^6.1", + "symfony/http-foundation": "^5.4 || ^6.1", + "symfony/messenger": "^5.4", + "symfony/polyfill-php80": "^1.24", + "symfony/serializer": "^5.4" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon", + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lukáš Unger", + "email": "looky.msc@gmail.com", + "homepage": "https://lookyman.net" + } + ], + "description": "Symfony Framework extensions and rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-symfony/issues", + "source": "https://github.com/phpstan/phpstan-symfony/tree/1.2.16" + }, + "time": "2022-11-04T13:16:15+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/bin/tools/psalm/composer.json b/bin/tools/psalm/composer.json deleted file mode 100644 index dd4082477..000000000 --- a/bin/tools/psalm/composer.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "require": { - "vimeo/psalm": "4.10", - "psalm/plugin-symfony": "^3.0" - }, - "config": { - "allow-plugins": { - "composer/package-versions-deprecated": true - } - } -} diff --git a/bin/tools/psalm/composer.lock b/bin/tools/psalm/composer.lock deleted file mode 100644 index c313447c1..000000000 --- a/bin/tools/psalm/composer.lock +++ /dev/null @@ -1,3678 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "a3345774672cb79c0302924a7106fdbf", - "packages": [ - { - "name": "amphp/amp", - "version": "v2.6.2", - "source": { - "type": "git", - "url": "https://github.com/amphp/amp.git", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "ext-json": "*", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^7 | ^8 | ^9", - "psalm/phar": "^3.11@dev", - "react/promise": "^2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "files": [ - "lib/functions.php", - "lib/Internal/functions.php" - ], - "psr-4": { - "Amp\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Bob Weinand", - "email": "bobwei9@hotmail.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A non-blocking concurrency framework for PHP applications.", - "homepage": "https://amphp.org/amp", - "keywords": [ - "async", - "asynchronous", - "awaitable", - "concurrency", - "event", - "event-loop", - "future", - "non-blocking", - "promise" - ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.2" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2022-02-20T17:52:18+00:00" - }, - { - "name": "amphp/byte-stream", - "version": "v1.8.1", - "source": { - "type": "git", - "url": "https://github.com/amphp/byte-stream.git", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.4", - "friendsofphp/php-cs-fixer": "^2.3", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^6 || ^7 || ^8", - "psalm/phar": "^3.11.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "files": [ - "lib/functions.php" - ], - "psr-4": { - "Amp\\ByteStream\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A stream abstraction to make working with non-blocking I/O simple.", - "homepage": "http://amphp.org/byte-stream", - "keywords": [ - "amp", - "amphp", - "async", - "io", - "non-blocking", - "stream" - ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2021-03-30T17:13:30+00:00" - }, - { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.5", - "source": { - "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-01-17T14:14:24+00:00" - }, - { - "name": "composer/pcre", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Pcre\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" - ], - "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/1.0.1" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-01-21T20:24:37+00:00" - }, - { - "name": "composer/semver", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.2" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-04-01T19:23:25+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/9e36aeed4616366d2b690bdce11f71e9178c579a", - "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a", - "shasum": "" - }, - "require": { - "composer/pcre": "^1", - "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1 || ^2 || ^3" - }, - "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/2.0.5" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-02-24T20:20:32+00:00" - }, - { - "name": "dnoegel/php-xdg-base-dir", - "version": "v0.1.1", - "source": { - "type": "git", - "url": "https://github.com/dnoegel/php-xdg-base-dir.git", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "XdgBaseDir\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "implementation of xdg base directory specification for php", - "support": { - "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", - "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" - }, - "time": "2019-12-04T15:06:13+00:00" - }, - { - "name": "felixfbecker/advanced-json-rpc", - "version": "v3.2.1", - "source": { - "type": "git", - "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", - "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", - "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", - "shasum": "" - }, - "require": { - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "php": "^7.1 || ^8.0", - "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" - }, - "require-dev": { - "phpunit/phpunit": "^7.0 || ^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "AdvancedJsonRpc\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Felix Becker", - "email": "felix.b@outlook.com" - } - ], - "description": "A more advanced JSONRPC implementation", - "support": { - "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", - "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" - }, - "time": "2021-06-11T22:34:44+00:00" - }, - { - "name": "felixfbecker/language-server-protocol", - "version": "v1.5.2", - "source": { - "type": "git", - "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpstan/phpstan": "*", - "squizlabs/php_codesniffer": "^3.1", - "vimeo/psalm": "^4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "LanguageServerProtocol\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Felix Becker", - "email": "felix.b@outlook.com" - } - ], - "description": "PHP classes for the Language Server Protocol", - "keywords": [ - "language", - "microsoft", - "php", - "server" - ], - "support": { - "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" - }, - "time": "2022-03-02T22:36:06+00:00" - }, - { - "name": "netresearch/jsonmapper", - "version": "v4.0.0", - "source": { - "type": "git", - "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", - "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", - "squizlabs/php_codesniffer": "~3.5" - }, - "type": "library", - "autoload": { - "psr-0": { - "JsonMapper": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "OSL-3.0" - ], - "authors": [ - { - "name": "Christian Weiske", - "email": "cweiske@cweiske.de", - "homepage": "http://github.com/cweiske/jsonmapper/", - "role": "Developer" - } - ], - "description": "Map nested JSON structures onto PHP classes", - "support": { - "email": "cweiske@cweiske.de", - "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0" - }, - "time": "2020-12-01T19:48:11+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.15.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1" - }, - "time": "2022-09-04T07:30:47+00:00" - }, - { - "name": "openlss/lib-array2xml", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/nullivex/lib-array2xml.git", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "autoload": { - "psr-0": { - "LSS": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Bryan Tong", - "email": "bryan@nullivex.com", - "homepage": "https://www.nullivex.com" - }, - { - "name": "Tony Butler", - "email": "spudz76@gmail.com", - "homepage": "https://www.nullivex.com" - } - ], - "description": "Array2XML conversion library credit to lalit.org", - "homepage": "https://www.nullivex.com", - "keywords": [ - "array", - "array conversion", - "xml", - "xml conversion" - ], - "support": { - "issues": "https://github.com/nullivex/lib-array2xml/issues", - "source": "https://github.com/nullivex/lib-array2xml/tree/master" - }, - "time": "2019-03-29T20:06:56+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.6.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "77a32518733312af16a44300404e945338981de3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", - "reference": "77a32518733312af16a44300404e945338981de3", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" - }, - "time": "2022-03-15T21:29:03+00:00" - }, - { - "name": "psalm/plugin-symfony", - "version": "v3.0.4", - "source": { - "type": "git", - "url": "https://github.com/psalm/psalm-plugin-symfony.git", - "reference": "65586604237c9062c15adc92faee5f84d1698af6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/psalm/psalm-plugin-symfony/zipball/65586604237c9062c15adc92faee5f84d1698af6", - "reference": "65586604237c9062c15adc92faee5f84d1698af6", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "php": "^7.1 || ^8.0", - "symfony/framework-bundle": "^4.0 || ^5.0", - "vimeo/psalm": "^4.9" - }, - "require-dev": { - "doctrine/orm": "^2.7", - "phpunit/phpunit": "~7.5 || ~9.5", - "symfony/cache-contracts": "^1.0 || ^2.0", - "symfony/console": "*", - "symfony/form": "^4.0 || ^5.0", - "symfony/messenger": "^4.2 || ^5.0", - "symfony/security-guard": "^4.0 || ^5.0", - "symfony/serializer": "^4.0 || ^5.0", - "symfony/validator": "*", - "twig/twig": "^2.10 || ^3.0", - "weirdan/codeception-psalm-module": "^0.13.1" - }, - "suggest": { - "weirdan/doctrine-psalm-plugin": "If Doctrine is used, it is recommended install this plugin" - }, - "type": "psalm-plugin", - "extra": { - "psalm": { - "pluginClass": "Psalm\\SymfonyPsalmPlugin\\Plugin" - } - }, - "autoload": { - "psr-4": { - "Psalm\\SymfonyPsalmPlugin\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Farhad Safarov", - "email": "farhad.safarov@gmail.com" - } - ], - "description": "Psalm Plugin for Symfony", - "support": { - "issues": "https://github.com/psalm/psalm-plugin-symfony/issues", - "source": "https://github.com/psalm/psalm-plugin-symfony/tree/v3.0.4" - }, - "time": "2021-10-14T04:48:39+00:00" - }, - { - "name": "psr/cache", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/master" - }, - "time": "2016-08-06T20:24:11+00:00" - }, - { - "name": "psr/container", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", - "shasum": "" - }, - "require": { - "php": ">=7.2.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.1" - }, - "time": "2021-03-05T17:36:06+00:00" - }, - { - "name": "psr/event-dispatcher", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", - "shasum": "" - }, - "require": { - "php": ">=7.2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\EventDispatcher\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Standard interfaces for event handling.", - "keywords": [ - "events", - "psr", - "psr-14" - ], - "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" - }, - "time": "2019-01-08T18:20:26+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:59:04+00:00" - }, - { - "name": "symfony/cache", - "version": "v5.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/cache.git", - "reference": "89bb6a0fe27205636d80e568ffaf9bbb52f691e3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/89bb6a0fe27205636d80e568ffaf9bbb52f691e3", - "reference": "89bb6a0fe27205636d80e568ffaf9bbb52f691e3", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/cache": "^1.0|^2.0", - "psr/log": "^1.1|^2|^3", - "symfony/cache-contracts": "^1.1.7|^2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/var-exporter": "^4.4|^5.0|^6.0" - }, - "conflict": { - "doctrine/dbal": "<2.13.1", - "symfony/dependency-injection": "<4.4", - "symfony/http-kernel": "<4.4", - "symfony/var-dumper": "<4.4" - }, - "provide": { - "psr/cache-implementation": "1.0|2.0", - "psr/simple-cache-implementation": "1.0|2.0", - "symfony/cache-implementation": "1.0|2.0" - }, - "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/cache": "^1.6|^2.0", - "doctrine/dbal": "^2.13.1|^3.0", - "predis/predis": "^1.1", - "psr/simple-cache": "^1.0|^2.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Cache\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", - "homepage": "https://symfony.com", - "keywords": [ - "caching", - "psr6" - ], - "support": { - "source": "https://github.com/symfony/cache/tree/v5.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-09-06T13:23:31+00:00" - }, - { - "name": "symfony/cache-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/cache-contracts.git", - "reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/64be4a7acb83b6f2bf6de9a02cee6dad41277ebc", - "reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/cache": "^1.0|^2.0|^3.0" - }, - "suggest": { - "symfony/cache-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Cache\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to caching", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-01-02T09:53:40+00:00" - }, - { - "name": "symfony/config", - "version": "v5.4.11", - "source": { - "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "ec79e03125c1d2477e43dde8528535d90cc78379" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/ec79e03125c1d2477e43dde8528535d90cc78379", - "reference": "ec79e03125c1d2477e43dde8528535d90cc78379", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22" - }, - "conflict": { - "symfony/finder": "<4.4" - }, - "require-dev": { - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Config\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/config/tree/v5.4.11" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-07-20T13:00:38+00:00" - }, - { - "name": "symfony/console", - "version": "v5.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "3f97f6c7b7e26848a90c0c0cfb91eeb2bb8618be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3f97f6c7b7e26848a90c0c0cfb91eeb2bb8618be", - "reference": "3f97f6c7b7e26848a90c0c0cfb91eeb2bb8618be", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" - }, - "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0" - }, - "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v5.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-08-26T13:50:20+00:00" - }, - { - "name": "symfony/dependency-injection", - "version": "v5.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/dependency-injection.git", - "reference": "24cf522668845391c0542bc1de496366072a6d0e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/24cf522668845391c0542bc1de496366072a6d0e", - "reference": "24cf522668845391c0542bc1de496366072a6d0e", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/container": "^1.1.1", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22", - "symfony/service-contracts": "^1.1.6|^2" - }, - "conflict": { - "ext-psr": "<1.1|>=2", - "symfony/config": "<5.3", - "symfony/finder": "<4.4", - "symfony/proxy-manager-bridge": "<4.4", - "symfony/yaml": "<4.4.26" - }, - "provide": { - "psr/container-implementation": "1.0", - "symfony/service-implementation": "1.0|2.0" - }, - "require-dev": { - "symfony/config": "^5.3|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4.26|^5.0|^6.0" - }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\DependencyInjection\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows you to standardize and centralize the way objects are constructed in your application", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v5.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-08-30T19:10:13+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-01-02T09:53:40+00:00" - }, - { - "name": "symfony/error-handler", - "version": "v5.4.11", - "source": { - "type": "git", - "url": "https://github.com/symfony/error-handler.git", - "reference": "f75d17cb4769eb38cd5fccbda95cd80a054d35c8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/f75d17cb4769eb38cd5fccbda95cd80a054d35c8", - "reference": "f75d17cb4769eb38cd5fccbda95cd80a054d35c8", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "require-dev": { - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0" - }, - "bin": [ - "Resources/bin/patch-type-declarations" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\ErrorHandler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to manage errors and ease debugging PHP code", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/error-handler/tree/v5.4.11" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-07-29T07:37:50+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v5.4.9", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "8e6ce1cc0279e3ff3c8ff0f43813bc88d21ca1bc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/8e6ce1cc0279e3ff3c8ff0f43813bc88d21ca1bc", - "reference": "8e6ce1cc0279e3ff3c8ff0f43813bc88d21ca1bc", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher-contracts": "^2|^3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/dependency-injection": "<4.4" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.9" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-05T16:45:39+00:00" - }, - { - "name": "symfony/event-dispatcher-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/f98b54df6ad059855739db6fcbc2d36995283fe1", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/event-dispatcher": "^1" - }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-01-02T09:53:40+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v5.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "ac09569844a9109a5966b9438fc29113ce77cf51" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/ac09569844a9109a5966b9438fc29113ce77cf51", - "reference": "ac09569844a9109a5966b9438fc29113ce77cf51", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-09-21T19:53:16+00:00" - }, - { - "name": "symfony/finder", - "version": "v5.4.11", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/7872a66f57caffa2916a584db1aa7f12adc76f8c", - "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.11" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-07-29T07:37:50+00:00" - }, - { - "name": "symfony/framework-bundle", - "version": "v5.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/framework-bundle.git", - "reference": "394866c2cb8bb189b9bd5ebd043b66f89c800363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/394866c2cb8bb189b9bd5ebd043b66f89c800363", - "reference": "394866c2cb8bb189b9bd5ebd043b66f89c800363", - "shasum": "" - }, - "require": { - "ext-xml": "*", - "php": ">=7.2.5", - "symfony/cache": "^5.2|^6.0", - "symfony/config": "^5.3|^6.0", - "symfony/dependency-injection": "^5.4.5|^6.0.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/error-handler": "^4.4.1|^5.0.1|^6.0", - "symfony/event-dispatcher": "^5.1|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22", - "symfony/routing": "^5.3|^6.0" - }, - "conflict": { - "doctrine/annotations": "<1.13.1", - "doctrine/cache": "<1.11", - "doctrine/persistence": "<1.3", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "phpunit/phpunit": "<5.4.3", - "symfony/asset": "<5.3", - "symfony/console": "<5.2.5", - "symfony/dom-crawler": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/form": "<5.2", - "symfony/http-client": "<4.4", - "symfony/lock": "<4.4", - "symfony/mailer": "<5.2", - "symfony/messenger": "<5.4", - "symfony/mime": "<4.4", - "symfony/property-access": "<5.3", - "symfony/property-info": "<4.4", - "symfony/security-csrf": "<5.3", - "symfony/serializer": "<5.2", - "symfony/service-contracts": ">=3.0", - "symfony/stopwatch": "<4.4", - "symfony/translation": "<5.3", - "symfony/twig-bridge": "<4.4", - "symfony/twig-bundle": "<4.4", - "symfony/validator": "<5.2", - "symfony/web-profiler-bundle": "<4.4", - "symfony/workflow": "<5.2" - }, - "require-dev": { - "doctrine/annotations": "^1.13.1", - "doctrine/cache": "^1.11|^2.0", - "doctrine/persistence": "^1.3|^2|^3", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/asset": "^5.3|^6.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/console": "^5.4.9|^6.0.9", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/dom-crawler": "^4.4.30|^5.3.7|^6.0", - "symfony/dotenv": "^5.1|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/form": "^5.2|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/mailer": "^5.2|^6.0", - "symfony/messenger": "^5.4|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/notifier": "^5.4|^6.0", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/property-info": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0", - "symfony/security-bundle": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/string": "^5.0|^6.0", - "symfony/translation": "^5.3|^6.0", - "symfony/twig-bundle": "^4.4|^5.0|^6.0", - "symfony/validator": "^5.2|^6.0", - "symfony/web-link": "^4.4|^5.0|^6.0", - "symfony/workflow": "^5.2|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0", - "twig/twig": "^2.10|^3.0" - }, - "suggest": { - "ext-apcu": "For best performance of the system caches", - "symfony/console": "For using the console commands", - "symfony/form": "For using forms", - "symfony/property-info": "For using the property_info service", - "symfony/serializer": "For using the serializer service", - "symfony/validator": "For using validation", - "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", - "symfony/yaml": "For using the debug:config and lint:yaml commands" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Symfony\\Bundle\\FrameworkBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v5.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-09-29T08:12:55+00:00" - }, - { - "name": "symfony/http-foundation", - "version": "v5.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "54be067587a4f2b7fffb7a699f9481ec3daf9379" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/54be067587a4f2b7fffb7a699f9481ec3daf9379", - "reference": "54be067587a4f2b7fffb7a699f9481ec3daf9379", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "predis/predis": "~1.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0" - }, - "suggest": { - "symfony/mime": "To use the file extension guesser" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Defines an object-oriented layer for the HTTP specification", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-09-17T07:31:22+00:00" - }, - { - "name": "symfony/http-kernel", - "version": "v5.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-kernel.git", - "reference": "4f25330c216b7bb178603b2e25fb7a9325015507" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/4f25330c216b7bb178603b2e25fb7a9325015507", - "reference": "4f25330c216b7bb178603b2e25fb7a9325015507", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^5.0|^6.0", - "symfony/http-foundation": "^5.3.7|^6.0", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/browser-kit": "<5.4", - "symfony/cache": "<5.0", - "symfony/config": "<5.0", - "symfony/console": "<4.4", - "symfony/dependency-injection": "<5.3", - "symfony/doctrine-bridge": "<5.0", - "symfony/form": "<5.0", - "symfony/http-client": "<5.0", - "symfony/mailer": "<5.0", - "symfony/messenger": "<5.0", - "symfony/translation": "<5.0", - "symfony/twig-bridge": "<5.0", - "symfony/validator": "<5.0", - "twig/twig": "<2.13" - }, - "provide": { - "psr/log-implementation": "1.0|2.0" - }, - "require-dev": { - "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/config": "^5.0|^6.0", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client-contracts": "^1.1|^2|^3", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2|^3", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "symfony/browser-kit": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpKernel\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a structured process for converting a Request into a Response", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-kernel/tree/v5.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-09-30T07:40:28+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "433d05519ce6990bf3530fba6957499d327395c2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", - "reference": "433d05519ce6990bf3530fba6957499d327395c2", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-10T07:21:04+00:00" - }, - { - "name": "symfony/polyfill-php81", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1", - "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/routing", - "version": "v5.4.11", - "source": { - "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "3e01ccd9b2a3a4167ba2b3c53612762300300226" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/3e01ccd9b2a3a4167ba2b3c53612762300300226", - "reference": "3e01ccd9b2a3a4167ba2b3c53612762300300226", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "doctrine/annotations": "<1.12", - "symfony/config": "<5.3", - "symfony/dependency-injection": "<4.4", - "symfony/yaml": "<4.4" - }, - "require-dev": { - "doctrine/annotations": "^1.12", - "psr/log": "^1|^2|^3", - "symfony/config": "^5.3|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Routing\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Maps an HTTP request to a set of configuration variables", - "homepage": "https://symfony.com", - "keywords": [ - "router", - "routing", - "uri", - "url" - ], - "support": { - "source": "https://github.com/symfony/routing/tree/v5.4.11" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-07-20T13:00:38+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-30T19:17:29+00:00" - }, - { - "name": "symfony/string", - "version": "v5.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "2900c668a32138a34118740de3e4d5a701801f53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/2900c668a32138a34118740de3e4d5a701801f53", - "reference": "2900c668a32138a34118740de3e4d5a701801f53", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" - }, - "conflict": { - "symfony/translation-contracts": ">=3.0" - }, - "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v5.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-09-01T01:52:16+00:00" - }, - { - "name": "symfony/var-dumper", - "version": "v5.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "2bf2ccab581bec363191672f0df40e0c85569e1c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2bf2ccab581bec363191672f0df40e0c85569e1c", - "reference": "2bf2ccab581bec363191672f0df40e0c85569e1c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<4.4" - }, - "require-dev": { - "ext-iconv": "*", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" - }, - "bin": [ - "Resources/bin/var-dump-server" - ], - "type": "library", - "autoload": { - "files": [ - "Resources/functions/dump.php" - ], - "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides mechanisms for walking through any arbitrary PHP variable", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], - "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-09-06T13:23:31+00:00" - }, - { - "name": "symfony/var-exporter", - "version": "v5.4.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-exporter.git", - "reference": "8fc03ee75eeece3d9be1ef47d26d79bea1afb340" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/8fc03ee75eeece3d9be1ef47d26d79bea1afb340", - "reference": "8fc03ee75eeece3d9be1ef47d26d79bea1afb340", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "symfony/var-dumper": "^4.4.9|^5.0.9|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\VarExporter\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows exporting any serializable PHP data structure to plain PHP code", - "homepage": "https://symfony.com", - "keywords": [ - "clone", - "construct", - "export", - "hydrate", - "instantiate", - "serialize" - ], - "support": { - "source": "https://github.com/symfony/var-exporter/tree/v5.4.10" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-27T12:56:18+00:00" - }, - { - "name": "vimeo/psalm", - "version": "4.10.0", - "source": { - "type": "git", - "url": "https://github.com/vimeo/psalm.git", - "reference": "916b098b008f6de4543892b1e0651c1c3b92cbfa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/916b098b008f6de4543892b1e0651c1c3b92cbfa", - "reference": "916b098b008f6de4543892b1e0651c1c3b92cbfa", - "shasum": "" - }, - "require": { - "amphp/amp": "^2.4.2", - "amphp/byte-stream": "^1.5", - "composer/package-versions-deprecated": "^1.8.0", - "composer/semver": "^1.4 || ^2.0 || ^3.0", - "composer/xdebug-handler": "^1.1 || ^2.0", - "dnoegel/php-xdg-base-dir": "^0.1.1", - "ext-ctype": "*", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-simplexml": "*", - "ext-tokenizer": "*", - "felixfbecker/advanced-json-rpc": "^3.0.3", - "felixfbecker/language-server-protocol": "^1.5", - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.12", - "openlss/lib-array2xml": "^1.0", - "php": "^7.1|^8", - "sebastian/diff": "^3.0 || ^4.0", - "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0", - "webmozart/path-util": "^2.3" - }, - "provide": { - "psalm/psalm": "self.version" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.2", - "brianium/paratest": "^4.0||^6.0", - "ext-curl": "*", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpdocumentor/reflection-docblock": "^5", - "phpmyadmin/sql-parser": "5.1.0||dev-master", - "phpspec/prophecy": ">=1.9.0", - "phpunit/phpunit": "^9.0", - "psalm/plugin-phpunit": "^0.16", - "slevomat/coding-standard": "^7.0", - "squizlabs/php_codesniffer": "^3.5", - "symfony/process": "^4.3 || ^5.0", - "weirdan/prophecy-shim": "^1.0 || ^2.0" - }, - "suggest": { - "ext-igbinary": "^2.0.5" - }, - "bin": [ - "psalm", - "psalm-language-server", - "psalm-plugin", - "psalm-refactor", - "psalter" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev", - "dev-3.x": "3.x-dev", - "dev-2.x": "2.x-dev", - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php", - "src/spl_object_id.php" - ], - "psr-4": { - "Psalm\\": "src/Psalm/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Brown" - } - ], - "description": "A static analysis tool for finding errors in PHP applications", - "keywords": [ - "code", - "inspection", - "php" - ], - "support": { - "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/4.10.0" - }, - "time": "2021-09-04T21:00:09+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "webmozart/path-util", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/path-util.git", - "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725", - "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "webmozart/assert": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\PathUtil\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", - "support": { - "issues": "https://github.com/webmozart/path-util/issues", - "source": "https://github.com/webmozart/path-util/tree/2.3.0" - }, - "abandoned": "symfony/filesystem", - "time": "2015-12-17T08:42:14+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "plugin-api-version": "2.3.0" -} diff --git a/composer.json b/composer.json index 10efaa0d4..239919515 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "fakerphp/faker": "^1.5", "symfony/deprecation-contracts": "^2.2|^3.0", "symfony/property-access": "^3.4|^4.4|^5.0|^6.0", + "symfony/string": "^6.0", "zenstruck/assert": "^1.0", "zenstruck/callback": "^1.1" }, diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 000000000..8dba530e9 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,61 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#1 \\$objectOrClass of class ReflectionClass constructor expects class\\-string\\\\|T of object, string\\|null given\\.$#" + count: 1 + path: src/Bundle/DependencyInjection/GlobalStatePass.php + + - + message: "#^Parameter \\#1 \\$class of method Doctrine\\\\Persistence\\\\ManagerRegistry\\:\\:getManagerForClass\\(\\) expects class\\-string, string given\\.$#" + count: 1 + path: src/Configuration.php + + - + message: "#^Parameter \\#1 \\$min of function random_int expects int, int\\|null given\\.$#" + count: 1 + path: src/FactoryCollection.php + + - + message: "#^Parameter \\#2 \\$max of function random_int expects int, int\\|null given\\.$#" + count: 1 + path: src/FactoryCollection.php + + - + message: "#^Method Zenstruck\\\\Foundry\\\\RepositoryProxy\\:\\:proxyResult\\(\\) should return array\\\\>\\|Zenstruck\\\\Foundry\\\\Proxy\\ but returns \\(TProxiedObject of object\\)\\|null\\.$#" + count: 1 + path: src/RepositoryProxy.php + + - + message: "#^Method Zenstruck\\\\Foundry\\\\Test\\\\DatabaseResetter\\:\\:getConfiguration\\(\\) should return Zenstruck\\\\Foundry\\\\Configuration\\|null but returns object\\|null\\.$#" + count: 1 + path: src/Test/DatabaseResetter.php + + - + message: "#^Parameter \\#1 \\$globalStateRegistry of static method Zenstruck\\\\Foundry\\\\Test\\\\TestState\\:\\:flushGlobalState\\(\\) expects Zenstruck\\\\Foundry\\\\Test\\\\GlobalStateRegistry\\|null, object\\|null given\\.$#" + count: 1 + path: src/Test/DatabaseResetter.php + + - + message: "#^Parameter \\#2 \\$registry of class Zenstruck\\\\Foundry\\\\Test\\\\ODMSchemaResetter constructor expects Doctrine\\\\Persistence\\\\ManagerRegistry, object\\|null given\\.$#" + count: 1 + path: src/Test/DatabaseResetter.php + + - + message: "#^Parameter \\#2 \\$registry of class Zenstruck\\\\Foundry\\\\Test\\\\ORMDatabaseResetter constructor expects Doctrine\\\\Persistence\\\\ManagerRegistry, object\\|null given\\.$#" + count: 1 + path: src/Test/DatabaseResetter.php + + - + message: "#^Call to an undefined method object\\:\\:getDatabasePlatform\\(\\)\\.$#" + count: 1 + path: src/Test/ORMDatabaseResetter.php + + - + message: "#^Parameter \\#1 \\$configuration of static method Zenstruck\\\\Foundry\\\\Factory\\\\:\\:boot\\(\\) expects Zenstruck\\\\Foundry\\\\Configuration, object\\|null given\\.$#" + count: 1 + path: src/ZenstruckFoundryBundle.php + + - + message: "#^Function Zenstruck\\\\Foundry\\\\factory\\(\\) should return Zenstruck\\\\Foundry\\\\AnonymousFactory\\ but returns Zenstruck\\\\Foundry\\\\AnonymousFactory\\\\.$#" + count: 1 + path: src/functions.php diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 000000000..73c7c7d95 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,36 @@ +includes: + - phpstan-baseline.neon + +parameters: + inferPrivatePropertyTypeFromConstructor: true + checkMissingIterableValueType: false + checkUninitializedProperties: true + checkGenericClassInNonGenericObjectType: false + paths: + - ./src + level: 8 + bootstrapFiles: + - ./vendor/autoload.php + banned_code: + nodes: + # enable detection of die/exit + - + type: Expr_Exit + functions: null + + # enable detection of a set of functions + - + type: Expr_FuncCall + functions: + - dd + - debug_backtrace + - dump + - print_r + - var_dump + excludePaths: + - ./src/Bundle/Resources + + ignoreErrors: + - + message: '#Unsafe usage of new static\(\).*#' + path: ./src/ModelFactory.php diff --git a/psalm.xml b/psalm.xml deleted file mode 100644 index e6c7761a7..000000000 --- a/psalm.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/AnonymousFactory.php b/src/AnonymousFactory.php index bd12f2496..37597a81f 100644 --- a/src/AnonymousFactory.php +++ b/src/AnonymousFactory.php @@ -12,8 +12,10 @@ final class AnonymousFactory extends Factory implements \Countable, \IteratorAgg { /** * @see Factory::__construct() + * + * @param class-string $class */ - public static function new(string $class, $defaultAttributes = []): self + public static function new(string $class, array|callable $defaultAttributes = []): self { return new self($class, $defaultAttributes); } @@ -23,12 +25,12 @@ public static function new(string $class, $defaultAttributes = []): self * instantiate and persist. * * @return Proxy&TModel - * @psalm-return Proxy + * @phpstan-return Proxy */ public function findOrCreate(array $attributes): Proxy { if ($found = $this->repository()->find($attributes)) { - return \is_array($found) ? $found[0] : $found; + return $found; } return $this->create($attributes); @@ -74,7 +76,7 @@ public function random(array $attributes = []): Proxy * Fetch one random object and create a new object if none exists. * * @return Proxy&TModel - * @psalm-return Proxy + * @phpstan-return Proxy */ public function randomOrCreate(array $attributes = []): Proxy { @@ -139,6 +141,8 @@ public function all(): array /** * @see RepositoryProxy::find() * + * @phpstan-param Proxy|array|mixed $criteria + * * @throws \RuntimeException If no entity found */ public function find($criteria): Proxy @@ -166,7 +170,7 @@ public function assert(): RepositoryAssertions } /** - * @psalm-return RepositoryProxy + * @phpstan-return RepositoryProxy */ public function repository(): RepositoryProxy { diff --git a/src/Bundle/DependencyInjection/Configuration.php b/src/Bundle/DependencyInjection/Configuration.php index f6df15146..d38d93d34 100644 --- a/src/Bundle/DependencyInjection/Configuration.php +++ b/src/Bundle/DependencyInjection/Configuration.php @@ -45,7 +45,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->integerNode('seed') ->defaultNull() ->info('Random number generator seed to produce the same fake values every run') - ->example(1234) + ->example('1234') ->end() ->scalarNode('service') ->defaultNull() diff --git a/src/Bundle/DependencyInjection/ZenstruckFoundryExtension.php b/src/Bundle/DependencyInjection/ZenstruckFoundryExtension.php index 052e7f0fa..00be6bde9 100644 --- a/src/Bundle/DependencyInjection/ZenstruckFoundryExtension.php +++ b/src/Bundle/DependencyInjection/ZenstruckFoundryExtension.php @@ -115,9 +115,6 @@ private function configureDatabaseResetter(array $config, ContainerBuilder $cont $configurationDefinition->setArgument('$odmObjectManagersToReset', $config['odm']['object_managers'] ?? []); } - /** - * @psalm-suppress UndefinedDocblockClass - */ private static function isBundleLoaded(ContainerBuilder $container, string $bundleName): bool { return \in_array( diff --git a/src/Bundle/Maker/MakeFactory.php b/src/Bundle/Maker/MakeFactory.php index ebaa15fa3..8031a2077 100644 --- a/src/Bundle/Maker/MakeFactory.php +++ b/src/Bundle/Maker/MakeFactory.php @@ -203,6 +203,9 @@ private function entityChoices(): array return $choices; } + /** + * @param class-string $class + */ private function defaultPropertiesFor(string $class, bool $allFields): iterable { $em = $this->managerRegistry->getManagerForClass($class); diff --git a/src/ChainManagerRegistry.php b/src/ChainManagerRegistry.php index 1eae9fc52..b7158235f 100644 --- a/src/ChainManagerRegistry.php +++ b/src/ChainManagerRegistry.php @@ -21,9 +21,7 @@ public function getRepository($persistentObject, $persistentManagerName = null): { foreach ($this->managerRegistries as $managerRegistry) { try { - if ($repository = $managerRegistry->getRepository($persistentObject, $persistentManagerName)) { - return $repository; - } + return $managerRegistry->getRepository($persistentObject, $persistentManagerName); } catch (MappingException) { // the class is not managed by the current manager } @@ -55,47 +53,50 @@ public function getManagers(): array ); } - public function getDefaultConnectionName(): void + public function getDefaultConnectionName(): string { throw new \BadMethodCallException('Not available in '.self::class); } - public function getConnection($name = null): void + public function getConnection($name = null): object { throw new \BadMethodCallException('Not available in '.self::class); } - public function getConnections(): void + public function getConnections(): array { throw new \BadMethodCallException('Not available in '.self::class); } - public function getConnectionNames(): void + public function getConnectionNames(): array { throw new \BadMethodCallException('Not available in '.self::class); } - public function getDefaultManagerName(): void + public function getDefaultManagerName(): string { throw new \BadMethodCallException('Not available in '.self::class); } - public function getManager($name = null): void + public function getManager($name = null): ObjectManager { throw new \BadMethodCallException('Not available in '.self::class); } - public function resetManager($name = null): void + public function resetManager($name = null): ObjectManager { throw new \BadMethodCallException('Not available in '.self::class); } + /** + * @param string $alias + */ public function getAliasNamespace($alias): void { throw new \BadMethodCallException('Not available in '.self::class); } - public function getManagerNames(): void + public function getManagerNames(): array { throw new \BadMethodCallException('Not available in '.self::class); } diff --git a/src/Configuration.php b/src/Configuration.php index 75648335c..4c563b8e0 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -2,6 +2,7 @@ namespace Zenstruck\Foundry; +use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ObjectManager; use Faker; @@ -151,7 +152,7 @@ public function delayFlush(callable $callback): void $callback(); - foreach ($this->managerRegistry()->getManagers() as $manager) { + foreach ($this->managerRegistry()?->getManagers() ?? [] as $manager) { $manager->flush(); } @@ -159,11 +160,9 @@ public function delayFlush(callable $callback): void } /** - * @psalm-suppress InvalidReturnType - * @psalm-suppress InvalidReturnStatement * @template TObject of object - * @psalm-param Proxy|TObject|class-string $objectOrClass - * @psalm-return RepositoryProxy + * @phpstan-param Proxy|TObject|class-string $objectOrClass + * @phpstan-return RepositoryProxy */ public function repositoryFor(object|string $objectOrClass): RepositoryProxy { @@ -175,21 +174,28 @@ public function repositoryFor(object|string $objectOrClass): RepositoryProxy $objectOrClass = $objectOrClass::class; } - return new RepositoryProxy($this->managerRegistry()->getRepository($objectOrClass)); + /** @var EntityRepository|null $repository */ + $repository = $this->managerRegistry()?->getRepository($objectOrClass); + + if (!$repository) { + throw new \RuntimeException(\sprintf('No repository registered for "%s".', $objectOrClass)); + } + + return new RepositoryProxy($repository); } public function objectManagerFor(object|string $objectOrClass): ObjectManager { $class = \is_string($objectOrClass) ? $objectOrClass : $objectOrClass::class; - if (!$objectManager = $this->managerRegistry()->getManagerForClass($class)) { + if (!$objectManager = $this->managerRegistry()?->getManagerForClass($class)) { throw new \RuntimeException(\sprintf('No object manager registered for "%s".', $class)); } return $objectManager; } - /** @psalm-assert !null $this->managerRegistry */ + /** @phpstan-assert !null $this->managerRegistry */ public function hasManagerRegistry(): bool { return null !== $this->managerRegistry; @@ -227,7 +233,6 @@ public function getOdmObjectManagersToReset(): array private function managerRegistry(): ?ManagerRegistry { if (!$this->hasManagerRegistry()) { - /** @psalm-suppress MissingDependency */ throw new \RuntimeException('Foundry was booted without doctrine. Ensure your TestCase extends '.KernelTestCase::class); } diff --git a/src/Factory.php b/src/Factory.php index b8d34fd9f..25dab2203 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -48,6 +48,7 @@ class Factory */ public function __construct(string $class, array|callable $defaultAttributes = []) { + /** @phpstan-ignore-next-line */ if (self::class === static::class) { trigger_deprecation('zenstruck/foundry', '1.9', 'Instantiating "%s" directly is deprecated and this class will be abstract in 2.0, use "%s" instead.', self::class, AnonymousFactory::class); } @@ -56,7 +57,10 @@ public function __construct(string $class, array|callable $defaultAttributes = [ $this->attributeSet[] = $defaultAttributes; } - public function __call(string $name, array $arguments) + /** + * @phpstan-return list> + */ + public function __call(string $name, array $arguments): array { if ('createMany' !== $name) { throw new \BadMethodCallException(\sprintf('Call to undefined method "%s::%s".', static::class, $name)); @@ -68,18 +72,16 @@ public function __call(string $name, array $arguments) } /** - * @param array|callable $attributes - * * @return Proxy&TObject - * @psalm-return Proxy + * @phpstan-return Proxy */ - final public function create($attributes = []): Proxy + final public function create(array|callable $attributes = []): Proxy { // merge the factory attribute set with the passed attributes $attributeSet = \array_merge($this->attributeSet, [$attributes]); // normalize each attribute set and collapse - $attributes = \array_merge(...\array_map(fn(callable|array $attributes): array => $this::normalizeAttributes($attributes), $attributeSet)); + $attributes = \array_merge(...\array_map(fn(callable|array $attributes): array => $this->normalizeAttributes($attributes), $attributeSet)); foreach ($this->beforeInstantiate as $callback) { $attributes = $callback($attributes); @@ -103,6 +105,7 @@ final public function create($attributes = []): Proxy $attributes = $mappedAttributes; // instantiate the object with the users instantiator or if not set, the default instantiator + /** @var TObject $object */ $object = ($this->instantiator ?? self::configuration()->instantiator())($attributes, $this->class); foreach ($this->afterInstantiate as $callback) { @@ -252,14 +255,12 @@ final public static function shutdown(): void return; } - self::$configuration->faker()->unique(true); // reset unique + self::configuration()->faker()->unique(true); // reset unique self::$configuration = null; } /** * @internal - * @psalm-suppress InvalidNullableReturnType - * @psalm-suppress NullableReturnStatement */ final public static function configuration(): Configuration { @@ -267,7 +268,7 @@ final public static function configuration(): Configuration throw new \RuntimeException('Foundry is not yet booted. Using in a test: is your Test case using the Factories trait? Using in a fixture: is ZenstruckFoundryBundle enabled for this environment?'); } - return self::$configuration; + return self::$configuration; // @phpstan-ignore-line } /** @@ -291,14 +292,14 @@ final public static function delayFlush(callable $callback): void /** * @internal * - * @psalm-return class-string + * @phpstan-return class-string */ final protected function class(): string { return $this->class; } - private static function normalizeAttributes(array|callable $attributes): array + private function normalizeAttributes(array|callable $attributes): array { return \is_callable($attributes) ? $attributes() : $attributes; } @@ -446,7 +447,7 @@ private function inverseRelationshipField(?string $relationName, self $factory, } if ($factoryClassMetadata->hasAssociation($relationName)) { - $relationName = $factoryClassMetadata->getAssociationMappedByTargetField($relationName) ?? null; + $relationName = $factoryClassMetadata->getAssociationMappedByTargetField($relationName); } else { $relationName = null; } @@ -463,7 +464,7 @@ private function inverseRelationshipField(?string $relationName, self $factory, foreach ($relationClassMetadata->getAssociationNames() as $field) { if ( ($relationClassMetadata->isSingleValuedAssociation($field) || $relationClassMetadata->isCollectionValuedAssociation($field)) - && \is_a($factoryClass, $relationClassMetadata->getAssociationTargetClass($field), true) + && ($relationClassMetadata->getAssociationTargetClass($field) && \is_a($factoryClass, $relationClassMetadata->getAssociationTargetClass($field), true)) && (null === $relationName || !$relationClassMetadata instanceof ORMClassMetadata || $field === $relationName) ) { $isCollectionValuedRelation = $relationClassMetadata->isCollectionValuedAssociation($field); @@ -499,7 +500,7 @@ private function inverseCollectionRelationshipField(?string $relationName, self // ensure 1-n and associated class matches if ( $collectionMetadata->isSingleValuedAssociation($field) - && \is_a($this->class, $collectionMetadata->getAssociationTargetClass($field), true) + && ($collectionMetadata->getAssociationTargetClass($field) && \is_a($this->class, $collectionMetadata->getAssociationTargetClass($field), true)) && (!$collectionMetadata instanceof ORMClassMetadata || null === $relationName || ($collectionMetadata->getAssociationMapping($field)['inversedBy'] ?? null) === $relationName) ) { return $field; @@ -530,9 +531,9 @@ private function hasCascadePersist(?self $factory, ?string $field): bool return false; } - $cascadeMetadata = $classMetadataFactory->getAssociationMapping($inversedBy)['cascade'] ?? []; + $cascadeMetadata = $classMetadataFactory->getAssociationMapping($inversedBy)['cascade']; } else { - $cascadeMetadata = $classMetadataFactory->getAssociationMapping($field)['cascade'] ?? []; + $cascadeMetadata = $classMetadataFactory->getAssociationMapping($field)['cascade']; } return \in_array('persist', $cascadeMetadata, true); diff --git a/src/FactoryCollection.php b/src/FactoryCollection.php index 358212622..d54131dc8 100644 --- a/src/FactoryCollection.php +++ b/src/FactoryCollection.php @@ -17,7 +17,7 @@ final class FactoryCollection implements \IteratorAggregate * @param int|null $max If set, when created, the collection will be a random size between $min and $max * @param iterable> $sequence|null $sequence * - * @psalm-param Factory $factory + * @phpstan-param Factory $factory * * @param Factory $factory * @@ -58,8 +58,7 @@ public static function sequence(Factory $factory, iterable $sequence): self /** * @return list> * - * @psalm-suppress InvalidReturnType - * @psalm-return list> + * @phpstan-return list> */ public function create(array|callable $attributes = []): array { @@ -76,12 +75,11 @@ public function create(array|callable $attributes = []): array /** * @return Factory[] * - * @psalm-return list> + * @phpstan-return list> */ public function all(): array { if (!$this->sequence) { - /** @psalm-suppress TooManyArguments */ return \array_map( fn(): Factory => clone $this->factory, \array_fill(0, \random_int($this->min, $this->max), null) diff --git a/src/Instantiator.php b/src/Instantiator.php index f8fdad994..c0609e86e 100644 --- a/src/Instantiator.php +++ b/src/Instantiator.php @@ -5,6 +5,7 @@ use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessor; +use function Symfony\Component\String\u; /** * @author Kevin Bond @@ -24,6 +25,9 @@ final class Instantiator /** @var string[] */ private array $forceProperties = []; + /** + * @param class-string $class + */ public function __invoke(array $attributes, string $class): object { $object = $this->instantiate($class, $attributes); @@ -207,35 +211,19 @@ private static function attributeNameForParameter(\ReflectionParameter $paramete return null; } - /** - * @see https://github.com/symfony/symfony/blob/a73523b065221b6b93cd45bf1cc7c59e7eb2dcdf/src/Symfony/Component/String/AbstractUnicodeString.php#L156 - * - * @todo use Symfony/String once stable - */ private static function camel(string $string): string { - return \str_replace(' ', '', \preg_replace_callback('/\b./u', static function($m) use (&$i): string { - return 1 === ++$i ? ('İ' === $m[0] ? 'i̇' : \mb_strtolower($m[0], 'UTF-8')) : \mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'); - }, \preg_replace('/[^\pL0-9]++/u', ' ', $string))); + return u($string)->camel(); } - /** - * @see https://github.com/symfony/symfony/blob/a73523b065221b6b93cd45bf1cc7c59e7eb2dcdf/src/Symfony/Component/String/AbstractUnicodeString.php#L361 - * - * @todo use Symfony/String once stable - */ private static function snake(string $string): string { - $string = self::camel($string); - - /** - * @see https://github.com/symfony/symfony/blob/a73523b065221b6b93cd45bf1cc7c59e7eb2dcdf/src/Symfony/Component/String/AbstractUnicodeString.php#L369 - */ - $string = \preg_replace_callback('/\b./u', static fn(array $m): string => \mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'), $string, 1); - - return \mb_strtolower(\preg_replace(['/(\p{Lu}+)(\p{Lu}\p{Ll})/u', '/([\p{Ll}0-9])(\p{Lu})/u'], '\1_\2', $string), 'UTF-8'); + return u($string)->snake(); } + /** + * @param class-string $class + */ private function instantiate(string $class, array &$attributes): object { $class = new \ReflectionClass($class); @@ -250,7 +238,7 @@ private function instantiate(string $class, array &$attributes): object foreach ($constructor->getParameters() as $parameter) { $name = self::attributeNameForParameter($parameter, $attributes); - if (\array_key_exists($name, $attributes)) { + if ($name && \array_key_exists($name, $attributes)) { if ($parameter->isVariadic()) { $arguments = \array_merge($arguments, $attributes[$name]); } else { diff --git a/src/ModelFactory.php b/src/ModelFactory.php index 285d0aff7..c789f1d5f 100644 --- a/src/ModelFactory.php +++ b/src/ModelFactory.php @@ -7,7 +7,7 @@ * @template-extends Factory * * @method static Proxy[]|TModel[] createMany(int $number, array|callable $attributes = []) - * @psalm-method static list> createMany(int $number, array|callable $attributes = []) + * @phpstan-method static list> createMany(int $number, array|callable $attributes = []) * * @author Kevin Bond */ @@ -18,7 +18,10 @@ public function __construct() parent::__construct(static::getClass()); } - public static function __callStatic(string $name, array $arguments) + /** + * @phpstan-return list> + */ + public static function __callStatic(string $name, array $arguments): array { if ('createMany' !== $name) { throw new \BadMethodCallException(\sprintf('Call to undefined static method "%s::%s".', static::class, $name)); @@ -65,7 +68,7 @@ final public static function new(array|callable|string $defaultAttributes = [], * A shortcut to create a single model without states. * * @return Proxy&TModel - * @psalm-return Proxy + * @phpstan-return Proxy */ final public static function createOne(array $attributes = []): Proxy { @@ -78,7 +81,7 @@ final public static function createOne(array $attributes = []): Proxy * @param iterable>|callable(): iterable> $sequence * * @return list> - * @psalm-return list> + * @phpstan-return list> */ final public static function createSequence(iterable|callable $sequence): array { @@ -90,12 +93,12 @@ final public static function createSequence(iterable|callable $sequence): array * instantiate and persist. * * @return Proxy&TModel - * @psalm-return Proxy + * @phpstan-return Proxy */ final public static function findOrCreate(array $attributes): Proxy { if ($found = static::repository()->find($attributes)) { - return \is_array($found) ? $found[0] : $found; + return $found; } return static::new()->create($attributes); @@ -105,7 +108,7 @@ final public static function findOrCreate(array $attributes): Proxy * @see RepositoryProxy::first() * * @return Proxy&TModel - * @psalm-return Proxy + * @phpstan-return Proxy * * @throws \RuntimeException If no entities exist */ @@ -122,7 +125,7 @@ final public static function first(string $sortedField = 'id'): Proxy * @see RepositoryProxy::last() * * @return Proxy&TModel - * @psalm-return Proxy + * @phpstan-return Proxy * * @throws \RuntimeException If no entities exist */ @@ -139,7 +142,7 @@ final public static function last(string $sortedField = 'id'): Proxy * @see RepositoryProxy::random() * * @return Proxy&TModel - * @psalm-return Proxy + * @phpstan-return Proxy */ final public static function random(array $attributes = []): Proxy { @@ -150,7 +153,7 @@ final public static function random(array $attributes = []): Proxy * Fetch one random object and create a new object if none exists. * * @return Proxy&TModel - * @psalm-return Proxy + * @phpstan-return Proxy */ final public static function randomOrCreate(array $attributes = []): Proxy { @@ -165,7 +168,7 @@ final public static function randomOrCreate(array $attributes = []): Proxy * @see RepositoryProxy::randomSet() * * @return list> - * @psalm-return list> + * @phpstan-return list> */ final public static function randomSet(int $number, array $attributes = []): array { @@ -176,7 +179,7 @@ final public static function randomSet(int $number, array $attributes = []): arr * @see RepositoryProxy::randomRange() * * @return list> - * @psalm-return list> + * @phpstan-return list> */ final public static function randomRange(int $min, int $max, array $attributes = []): array { @@ -203,7 +206,7 @@ final public static function truncate(): void * @see RepositoryProxy::findAll() * * @return list> - * @psalm-return list> + * @phpstan-return list> */ final public static function all(): array { @@ -213,8 +216,10 @@ final public static function all(): array /** * @see RepositoryProxy::find() * + * @phpstan-param Proxy|array|mixed $criteria + * @phpstan-return Proxy + * * @return Proxy&TModel - * @psalm-return Proxy * * @throws \RuntimeException If no entity found */ @@ -231,7 +236,7 @@ final public static function find($criteria): Proxy * @see RepositoryProxy::findBy() * * @return list> - * @psalm-return list> + * @phpstan-return list> */ final public static function findBy(array $attributes): array { @@ -244,7 +249,7 @@ final public static function assert(): RepositoryAssertions } /** - * @psalm-return RepositoryProxy + * @phpstan-return RepositoryProxy */ final public static function repository(): RepositoryProxy { @@ -253,18 +258,20 @@ final public static function repository(): RepositoryProxy /** * @internal - * @psalm-return class-string + * @phpstan-return class-string */ final public static function getEntityClass(): string { return static::getClass(); } - /** @psalm-return class-string */ + /** @phpstan-return class-string */ abstract protected static function getClass(): string; /** * Override to add default instantiator and default afterInstantiate/afterPersist events. + * + * @return static */ protected function initialize() { diff --git a/src/ModelFactoryManager.php b/src/ModelFactoryManager.php index d7427b8b3..6f9a1c6d3 100644 --- a/src/ModelFactoryManager.php +++ b/src/ModelFactoryManager.php @@ -16,6 +16,9 @@ public function __construct(private iterable $factories) { } + /** + * @param class-string $class + */ public function create(string $class): ModelFactory { foreach ($this->factories as $factory) { diff --git a/src/Proxy.php b/src/Proxy.php index 555dff08f..ff862ba30 100644 --- a/src/Proxy.php +++ b/src/Proxy.php @@ -18,7 +18,7 @@ final class Proxy implements \Stringable { /** - * @psalm-var class-string + * @phpstan-var class-string */ private string $class; @@ -29,28 +29,27 @@ final class Proxy implements \Stringable /** * @internal * - * @psalm-param TProxiedObject $object + * @phpstan-param TProxiedObject $object */ - public function __construct(/** - * @psalm-var TProxiedObject - */ - private object $object) - { + public function __construct( + /** @param TProxiedObject $object */ + private object $object + ) { $this->class = $object::class; $this->autoRefresh = Factory::configuration()->defaultProxyAutoRefresh(); } - public function __call(string $method, array $arguments) + public function __call(string $method, array $arguments) // @phpstan-ignore-line { return $this->object()->{$method}(...$arguments); } - public function __get(string $name) + public function __get(string $name): mixed { return $this->object()->{$name}; } - public function __set(string $name, $value): void + public function __set(string $name, mixed $value): void { $this->object()->{$name} = $value; } @@ -67,7 +66,9 @@ public function __isset(string $name): bool public function __toString(): string { - if (!\method_exists($this->object, '__toString')) { + $object = $this->object(); + + if (!\method_exists($object, '__toString')) { if (\PHP_VERSION_ID < 70400) { return '(no __toString)'; } @@ -75,15 +76,15 @@ public function __toString(): string throw new \RuntimeException(\sprintf('Proxied object "%s" cannot be converted to a string.', $this->class)); } - return $this->object()->__toString(); + return $object->__toString(); } /** * @internal * * @template TObject of object - * @psalm-param TObject $object - * @psalm-return Proxy + * @phpstan-param TObject $object + * @phpstan-return Proxy */ public static function createFromPersisted(object $object): self { @@ -127,7 +128,7 @@ public function object(): object } /** - * @psalm-return static + * @phpstan-return static */ public function save(): self { @@ -226,7 +227,7 @@ public function disableAutoRefresh(): self * * @param callable $callback (object|Proxy $object): void * - * @psalm-return static + * @phpstan-return static */ public function withoutAutoRefresh(callable $callback): self { @@ -257,7 +258,7 @@ public function assertNotPersisted(string $message = '{entity} is persisted but /** * @internal */ - public function executeCallback(callable $callback, ...$arguments): void + public function executeCallback(callable $callback, mixed ...$arguments): void { Callback::createFor($callback)->invoke( Parameter::union( @@ -270,7 +271,7 @@ public function executeCallback(callable $callback, ...$arguments): void } /** - * @psalm-return TProxiedObject|null + * @phpstan-return TProxiedObject|null */ private function fetchObject(): ?object { diff --git a/src/RepositoryProxy.php b/src/RepositoryProxy.php index 7a3ecc04d..aec429187 100644 --- a/src/RepositoryProxy.php +++ b/src/RepositoryProxy.php @@ -16,12 +16,15 @@ final class RepositoryProxy implements ObjectRepository, \IteratorAggregate, \Countable { /** - * @param ObjectRepository|EntityRepository $repository + * @param ObjectRepository $repository */ public function __construct(private ObjectRepository $repository) { } + /** + * @return list>|Proxy + */ public function __call(string $method, array $arguments) { return $this->proxyResult($this->repository->{$method}(...$arguments)); @@ -141,6 +144,7 @@ public function assertCountLessThanOrEqual(int $expected, string $message = ''): /** * @deprecated use RepositoryProxy::assert()->exists() + * @phpstan-param Proxy|array|mixed $criteria */ public function assertExists($criteria, string $message = ''): self { @@ -153,6 +157,7 @@ public function assertExists($criteria, string $message = ''): self /** * @deprecated use RepositoryProxy::assert()->notExists() + * @phpstan-param Proxy|array|mixed $criteria */ public function assertNotExists($criteria, string $message = ''): self { @@ -164,9 +169,9 @@ public function assertNotExists($criteria, string $message = ''): self } /** - * @return Proxy&TProxiedObject|null + * @return (Proxy&TProxiedObject)|null * - * @psalm-return Proxy|null + * @phpstan-return Proxy|null */ public function first(string $sortedField = 'id'): ?Proxy { @@ -174,9 +179,9 @@ public function first(string $sortedField = 'id'): ?Proxy } /** - * @return Proxy&TProxiedObject|null + * @return (Proxy&TProxiedObject)|null * - * @psalm-return Proxy|null + * @phpstan-return Proxy|null */ public function last(string $sortedField = 'id'): ?Proxy { @@ -211,7 +216,7 @@ public function truncate(): void * * @throws \RuntimeException if no objects are persisted * - * @psalm-return Proxy + * @phpstan-return Proxy */ public function random(array $attributes = []): Proxy { @@ -224,7 +229,7 @@ public function random(array $attributes = []): Proxy * @param int $number The number of objects to return * @param array $attributes The findBy criteria * - * @return Proxy[]|object[] + * @return list> * * @throws \RuntimeException if not enough persisted objects to satisfy the number requested * @throws \InvalidArgumentException if number is less than zero @@ -245,7 +250,7 @@ public function randomSet(int $number, array $attributes = []): array * @param int $max The maximum number of objects to return * @param array $attributes The findBy criteria * - * @return Proxy[]|object[] + * @return list> * * @throws \RuntimeException if not enough persisted objects to satisfy the max * @throws \InvalidArgumentException if min is less than zero @@ -269,17 +274,16 @@ public function randomRange(int $min, int $max, array $attributes = []): array throw new \RuntimeException(\sprintf('At least %d "%s" object(s) must have been persisted (%d persisted).', $max, $this->getClassName(), \count($all))); } - return \array_slice($all, 0, \random_int($min, $max)); + return \array_slice($all, 0, \random_int($min, $max)); // @phpstan-ignore-line } /** * @param object|array|mixed $criteria * - * @return Proxy&TProxiedObject|null + * @return (Proxy&TProxiedObject)|null * - * @psalm-param Proxy|array|mixed $criteria - * @psalm-return Proxy|null - * @psalm-suppress ParamNameMismatch + * @phpstan-param Proxy|array|mixed $criteria + * @phpstan-return Proxy|null */ public function find($criteria) { @@ -298,7 +302,7 @@ public function find($criteria) } /** - * @return Proxy[]|object[] + * @return list> */ public function findAll(): array { @@ -306,7 +310,10 @@ public function findAll(): array } /** - * @return Proxy[]|object[] + * @param int|null $limit + * @param int|null $offset + * + * @return list> */ public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array { @@ -316,11 +323,11 @@ public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $ /** * @param array|null $orderBy Some ObjectRepository's (ie Doctrine\ORM\EntityRepository) add this optional parameter * - * @return Proxy&TProxiedObject|null + * @return (Proxy&TProxiedObject)|null * * @throws \RuntimeException if the wrapped ObjectRepository does not have the $orderBy parameter * - * @psalm-return Proxy|null + * @phpstan-return Proxy|null */ public function findOneBy(array $criteria, ?array $orderBy = null): ?Proxy { @@ -333,7 +340,7 @@ public function findOneBy(array $criteria, ?array $orderBy = null): ?Proxy } /** @var TProxiedObject|null $result */ - $result = $this->repository->findOneBy(self::normalizeCriteria($criteria), $orderBy); + $result = $this->repository->findOneBy(self::normalizeCriteria($criteria), $orderBy); // @phpstan-ignore-line if (null === $result) { return null; } @@ -342,7 +349,7 @@ public function findOneBy(array $criteria, ?array $orderBy = null): ?Proxy } /** - * @psalm-return class-string + * @return class-string */ public function getClassName(): string { @@ -350,22 +357,20 @@ public function getClassName(): string } /** + * @param TProxiedObject|list|null $result + * * @return Proxy|Proxy[]|object|object[]|mixed * - * @psalm-suppress InvalidReturnStatement - * @psalm-suppress InvalidReturnType - * @template TResult of object - * @psalm-param TResult|list $result - * @psalm-return ($result is array ? list> : Proxy) + * @phpstan-return ($result is array ? list> : Proxy) */ private function proxyResult(mixed $result) { - if (\is_a($result, $this->getClassName())) { - return Proxy::createFromPersisted($result); + if (\is_array($result)) { + return \array_map(fn(object $o): Proxy => $this->proxyResult($o), $result); } - if (\is_array($result)) { - return \array_map([$this, 'proxyResult'], $result); + if ($result && \is_a($result, $this->getClassName())) { + return Proxy::createFromPersisted($result); } return $result; diff --git a/src/Story.php b/src/Story.php index 06c005913..f0b48e837 100644 --- a/src/Story.php +++ b/src/Story.php @@ -13,21 +13,19 @@ abstract class Story /** @var array */ private array $pools = []; - final public function __call(string $method, array $arguments) + final public function __call(string $method, array $arguments): Proxy { return $this->get($method); } - final public static function __callStatic($name, $arguments) + final public static function __callStatic(string $name, array $arguments): Proxy { return static::load()->get($name); } - /** - * @return static - */ - final public static function load(): self + final public static function load(): static { + /** @phpstan-ignore-next-line */ return Factory::configuration()->stories()->load(static::class); } @@ -86,7 +84,7 @@ final public static function getRandomRange(string $pool, int $min, int $max): a throw new \RuntimeException(\sprintf('At least %d items must be in pool "%s" (%d items found).', $max, $pool, \count($values))); } - return \array_slice($values, 0, \random_int($min, $max)); + return \array_slice($values, 0, \random_int($min, $max)); // @phpstan-ignore-line } /** diff --git a/src/StoryManager.php b/src/StoryManager.php index a1911b72e..ed450c12d 100644 --- a/src/StoryManager.php +++ b/src/StoryManager.php @@ -4,24 +4,30 @@ /** * @internal + * @template T of Story * * @author Kevin Bond */ final class StoryManager { - /** @var array */ + /** @var array */ private static array $globalInstances = []; - /** @var array */ + /** @var array */ private static array $instances = []; /** - * @param Story[] $stories + * @param T[] $stories */ public function __construct(private iterable $stories) { } + /** + * @param class-string $class + * + * @return T + */ public function load(string $class): Story { if (\array_key_exists($class, self::$globalInstances)) { @@ -55,6 +61,11 @@ public static function globalReset(): void self::$instances = []; } + /** + * @param class-string $class + * + * @return T + */ private function getOrCreateStory(string $class): Story { foreach ($this->stories as $story) { @@ -65,7 +76,7 @@ private function getOrCreateStory(string $class): Story try { return new $class(); - } catch (\ArgumentCountError $e) { + } catch (\ArgumentCountError $e) { // @phpstan-ignore-line throw new \RuntimeException('Stories with dependencies (Story services) cannot be used without the foundry bundle.', 0, $e); } } diff --git a/src/Test/LazyManagerRegistry.php b/src/Test/LazyManagerRegistry.php index 9eb952569..cd62c0a38 100644 --- a/src/Test/LazyManagerRegistry.php +++ b/src/Test/LazyManagerRegistry.php @@ -69,6 +69,9 @@ public function resetManager($name = null): ObjectManager return $this->inner()->resetManager($name); } + /** + * @param string $alias + */ public function getAliasNamespace($alias): string { $inner = $this->inner(); diff --git a/src/functions.php b/src/functions.php index ee0fcc440..a08c5a7ef 100644 --- a/src/functions.php +++ b/src/functions.php @@ -8,10 +8,12 @@ * @see Factory::__construct() * * @template TObject of object - * @psalm-param class-string $class - * @psalm-return AnonymousFactory + * + * @param class-string $class + * + * @return AnonymousFactory */ -function factory(string $class, $defaultAttributes = []): AnonymousFactory +function factory(string $class, array|callable $defaultAttributes = []): AnonymousFactory { return new AnonymousFactory($class, $defaultAttributes); } @@ -22,10 +24,10 @@ function factory(string $class, $defaultAttributes = []): AnonymousFactory * @return Proxy&TObject * * @template TObject of object - * @psalm-param class-string $class - * @psalm-return Proxy + * @phpstan-param class-string $class + * @phpstan-return Proxy */ -function create(string $class, $attributes = []): Proxy +function create(string $class, array|callable $attributes = []): Proxy { return factory($class)->create($attributes); } @@ -36,10 +38,10 @@ function create(string $class, $attributes = []): Proxy * @return Proxy[]|object[] * * @template TObject of object - * @psalm-param class-string $class - * @psalm-return list> + * @phpstan-param class-string $class + * @phpstan-return list> */ -function create_many(int $number, string $class, $attributes = []): array +function create_many(int $number, string $class, array|callable $attributes = []): array { return factory($class)->many($number)->create($attributes); } @@ -50,10 +52,10 @@ function create_many(int $number, string $class, $attributes = []): array * @return Proxy&TObject "unpersisted" Proxy wrapping the instantiated object * * @template TObject of object - * @psalm-param class-string $class - * @psalm-return Proxy + * @phpstan-param class-string $class + * @phpstan-return Proxy */ -function instantiate(string $class, $attributes = []): Proxy +function instantiate(string $class, array|callable $attributes = []): Proxy { return factory($class)->withoutPersisting()->create($attributes); } @@ -64,18 +66,24 @@ function instantiate(string $class, $attributes = []): Proxy * @return Proxy[]|object[] "unpersisted" Proxy's wrapping the instantiated objects * * @template TObject of object - * @psalm-param class-string $class - * @psalm-return list> + * @phpstan-param class-string $class + * @phpstan-return list> */ -function instantiate_many(int $number, string $class, $attributes = []): array +function instantiate_many(int $number, string $class, array|callable $attributes = []): array { return factory($class)->withoutPersisting()->many($number)->create($attributes); } /** * @see Configuration::repositoryFor() + * + * @template TObject of object + * + * @param TObject|class-string $objectOrClass + * + * @return RepositoryProxy */ -function repository($objectOrClass): RepositoryProxy +function repository(object|string $objectOrClass): RepositoryProxy { return Factory::configuration()->repositoryFor($objectOrClass); } diff --git a/tests/Unit/ChainManagerRegistryTest.php b/tests/Unit/ChainManagerRegistryTest.php index 25bfdceab..1be609bdc 100644 --- a/tests/Unit/ChainManagerRegistryTest.php +++ b/tests/Unit/ChainManagerRegistryTest.php @@ -3,6 +3,7 @@ namespace Zenstruck\Foundry\Tests\Unit; use Doctrine\Persistence\ManagerRegistry; +use Doctrine\Persistence\Mapping\MappingException; use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectRepository; use PHPUnit\Framework\TestCase; @@ -17,6 +18,8 @@ final class ChainManagerRegistryTest extends TestCase public function can_get_repository(): void { $managerRegistry1 = $this->createMock(ManagerRegistry::class); + $managerRegistry1->expects($this->once())->method('getRepository')->willThrowException(new MappingException()); + $managerRegistry2 = $this->createMock(ManagerRegistry::class); $managerRegistry2->expects($this->once())->method('getRepository')->willReturn( @@ -38,7 +41,10 @@ public function throws_exception_if_repository_not_found(): void $this->expectExceptionMessage("Cannot find repository for class {$class}"); $managerRegistry1 = $this->createMock(ManagerRegistry::class); + $managerRegistry1->expects($this->once())->method('getRepository')->willThrowException(new MappingException()); + $managerRegistry2 = $this->createMock(ManagerRegistry::class); + $managerRegistry2->expects($this->once())->method('getRepository')->willThrowException(new MappingException()); $chainManagerRegistry = new ChainManagerRegistry([$managerRegistry1, $managerRegistry2]); From 6b488786df161130d962ba79e072e7b1e2bdac0a Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Fri, 11 Nov 2022 12:02:32 -0500 Subject: [PATCH 05/30] [chore] upgrade ci actions (#329) --- .github/workflows/ci.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 132a15e8a..8d05c95be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3 - name: Verify MySQL version run: mysql --host 127.0.0.1 -uroot -p1234 -e "STATUS" @@ -66,7 +66,7 @@ jobs: tools: flex - name: Install dependencies - uses: ramsey/composer-install@v1 + uses: ramsey/composer-install@v2 with: dependency-versions: ${{ matrix.deps }} composer-options: --prefer-dist @@ -107,7 +107,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3 - name: Verify MySQL version run: mysql --host 127.0.0.1 -uroot -p1234 -e "STATUS" @@ -121,7 +121,7 @@ jobs: ini-values: xdebug.mode=coverage - name: Install dependencies - uses: ramsey/composer-install@v1 + uses: ramsey/composer-install@v2 with: composer-options: --prefer-dist dependency-versions: "highest" @@ -134,7 +134,7 @@ jobs: USE_FOUNDRY_BUNDLE: 1 - name: Publish coverage report to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: file: ./*.clover @@ -146,7 +146,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -155,7 +155,7 @@ jobs: coverage: none - name: Install dependencies - uses: ramsey/composer-install@v1 + uses: ramsey/composer-install@v2 with: composer-options: --prefer-dist @@ -167,16 +167,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3 - name: Setup PHP - uses: shivammathur/setup-php@2.7.0 + uses: shivammathur/setup-php@v2 with: php-version: 8.0 coverage: none - name: Install dependencies - uses: ramsey/composer-install@v1 + uses: ramsey/composer-install@v2 with: composer-options: --prefer-dist @@ -191,16 +191,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3 - name: Setup PHP - uses: shivammathur/setup-php@2.7.0 + uses: shivammathur/setup-php@v2 with: php-version: 8.0 coverage: none - name: Install dependencies - uses: ramsey/composer-install@v1 + uses: ramsey/composer-install@v2 with: composer-options: --prefer-dist @@ -215,7 +215,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3 - name: Run test suite with docker run: | From 65924b2a80de4a3b53f57429dbf48331ad45f342 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Sat, 12 Nov 2022 11:17:44 +0100 Subject: [PATCH 06/30] [fix] typos in docs (#331) --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d2c898319..f81425061 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,11 @@ Each target will build and start the docker stack and install composer only if n $ make help validate Run fixcs, sca, full test suite and validate migrations test-full Run full PHPunit (MySQL + Mongo) -test-fast Run PHPunit with SQLite -test-mysql Run PHPunit with mysql -test-postgresql Run PHPunit with postgreSQL -test-mongo Run PHPunit with Mongo -fixcs Run PHP CS-Fixer +test-fast Run PHPUnit with SQLite +test-mysql Run PHPUnit with MySQL +test-postgresql Run PHPUnit with PostgreSQL +test-mongo Run PHPUnit with Mongo +fixcs Run PHP-CS-Fixer sca Run static analysis database-generate-migration Generate new migration based on mapping in Zenstruck\Foundry\Tests\Fixtures\Entity database-validate-mapping Validate mapping in Zenstruck\Foundry\Tests\Fixtures\Entity From 2c34bafcad9bc936aeef52608472372d1500af1e Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Sat, 12 Nov 2022 11:18:38 +0100 Subject: [PATCH 07/30] [fix] Typos in Makefile (#330) --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index a250ef334..c87adca59 100644 --- a/Makefile +++ b/Makefile @@ -41,19 +41,19 @@ help: validate: fixcs sca test-full database-validate-mapping ### Run fixcs, sca, full test suite and validate migrations -test-full: docker-start vendor ### Run full PHPunit (MySQL + Mongo) +test-full: docker-start vendor ### Run full PHPUnit (MySQL + Mongo) @$(eval filter ?= '.') @${DC_EXEC} -e USE_ORM=1 -e USE_ODM=1 php vendor/bin/simple-phpunit --configuration phpunit-dama-doctrine.xml.dist --filter=$(filter) -test-mysql: docker-start vendor ### Run PHPunit with mysql +test-mysql: docker-start vendor ### Run PHPUnit with MySQL @$(eval filter ?= '.') @${DC_EXEC} -e USE_ORM=1 php vendor/bin/simple-phpunit --configuration phpunit-dama-doctrine.xml.dist --filter=$(filter) -test-mongo: docker-start vendor ### Run PHPunit with Mongo +test-mongo: docker-start vendor ### Run PHPUnit with Mongo @$(eval filter ?= '.') @${DC_EXEC} -e USE_ODM=1 php vendor/bin/simple-phpunit --configuration phpunit.xml.dist --filter=$(filter) -fixcs: docker-start bin/tools/cs-fixer/vendor ### Run PHP CS-Fixer +fixcs: docker-start bin/tools/cs-fixer/vendor ### Run PHP-CS-Fixer @${DOCKER_PHP} bin/tools/cs-fixer/vendor/friendsofphp/php-cs-fixer/php-cs-fixer --no-interaction --diff -v fix bin/tools/cs-fixer/vendor: vendor bin/tools/cs-fixer/composer.json bin/tools/cs-fixer/composer.lock From 0460741d20e7df90bc30c5e52a2f6e1d16d6416c Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Sun, 13 Nov 2022 09:45:19 +0100 Subject: [PATCH 08/30] [docs] remove obsolete section (#335) --- docs/index.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index e6f682454..d39c216c1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1651,16 +1651,6 @@ You will need to configure manually Foundry. Unfortunately, this may mean duplic The easiest work-around is to make the test an instance of ``Symfony\Bundle\FrameworkBundle\Test\KernelTestCase`` so the container is available. -Using in Unit Tests -~~~~~~~~~~~~~~~~~~~ - -When using foundry in unit tests, by using ``PHPUnit\Framework\TestCase``, Foundry simply creates the object instancies -but does not try to persist them (this is also true for any object not managed by Doctrine). - -You can still configure Foundry statically: - - - .. _stories: Stories From 2bd046f161d57b4de6045476a55cd086221c7850 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 14 Nov 2022 08:53:53 +0100 Subject: [PATCH 09/30] [docs] sort phpstan-method annotations (#333) --- docs/index.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index d39c216c1..5b95f5808 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -207,9 +207,8 @@ This command will generate a ``PostFactory`` class that looks like this: /** * ... * + * @phpstan-method Post&Proxy create(array|callable $attributes = []) * @phpstan-method static Post&Proxy createOne(array $attributes = []) - * @phpstan-method static Post[]&Proxy[] createMany(int $number, array|callable $attributes = []) - * @phpstan-method static Post[]&Proxy[] createSequence(array|callable $sequence) * @phpstan-method static Post&Proxy find(object|array|mixed $criteria) * @phpstan-method static Post&Proxy findOrCreate(array $attributes) * @phpstan-method static Post&Proxy first(string $sortedField = 'id') @@ -217,10 +216,11 @@ This command will generate a ``PostFactory`` class that looks like this: * @phpstan-method static Post&Proxy random(array $attributes = []) * @phpstan-method static Post&Proxy randomOrCreate(array $attributes = []) * @phpstan-method static Post[]&Proxy[] all() + * @phpstan-method static Post[]&Proxy[] createMany(int $number, array|callable $attributes = []) + * @phpstan-method static Post[]&Proxy[] createSequence(array|callable $sequence) * @phpstan-method static Post[]&Proxy[] findBy(array $attributes) - * @phpstan-method static Post[]&Proxy[] randomSet(int $number, array $attributes = []) * @phpstan-method static Post[]&Proxy[] randomRange(int $min, int $max, array $attributes = []) - * @phpstan-method Post&Proxy create(array|callable $attributes = []) + * @phpstan-method static Post[]&Proxy[] randomSet(int $number, array $attributes = []) */ final class PostFactory extends ModelFactory { From c662eb3a908bfe9a9db2967c54ac190eec1405f9 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Mon, 14 Nov 2022 18:54:48 +0100 Subject: [PATCH 10/30] [feat] auto add phpstan annotations in make:factory (#338) --- .php-cs-fixer.dist.php | 1 + docs/index.rst | 29 +- src/Bundle/Maker/MakeFactory.php | 8 +- src/Bundle/Resources/config/services.xml | 1 + src/Bundle/Resources/skeleton/Factory.tpl.php | 32 +- .../can_create_factory/CategoryFactory.php | 56 ++++ .../CategoryFactory.php | 56 ++++ .../TagFactory.php | 56 ++++ .../TagFactory.php | 56 ++++ .../CategoryFactory.php | 71 +++++ .../Bundle/Maker/MakeFactoryTest.php | 277 +++--------------- .../Functional/Bundle/Maker/MakerTestCase.php | 11 + 12 files changed, 397 insertions(+), 257 deletions(-) create mode 100644 tests/Fixtures/Maker/can_create_factory/CategoryFactory.php create mode 100644 tests/Fixtures/Maker/can_create_factory_in_test_dir/CategoryFactory.php create mode 100644 tests/Fixtures/Maker/can_create_factory_in_test_dir_interactively/TagFactory.php create mode 100644 tests/Fixtures/Maker/can_create_factory_interactively/TagFactory.php create mode 100644 tests/Fixtures/Maker/can_create_factory_with_phpstan_annotations/CategoryFactory.php diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index eaae762f8..12763c45f 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -3,6 +3,7 @@ $finder = PhpCsFixer\Finder::create() ->in([__DIR__.'/src', __DIR__.'/tests']) ->notName('*.tpl.php') + ->exclude('Fixtures/Maker') ; $config = new PhpCsFixer\Config(); diff --git a/docs/index.rst b/docs/index.rst index 5b95f5808..324d1ed30 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -207,20 +207,21 @@ This command will generate a ``PostFactory`` class that looks like this: /** * ... * - * @phpstan-method Post&Proxy create(array|callable $attributes = []) - * @phpstan-method static Post&Proxy createOne(array $attributes = []) - * @phpstan-method static Post&Proxy find(object|array|mixed $criteria) - * @phpstan-method static Post&Proxy findOrCreate(array $attributes) - * @phpstan-method static Post&Proxy first(string $sortedField = 'id') - * @phpstan-method static Post&Proxy last(string $sortedField = 'id') - * @phpstan-method static Post&Proxy random(array $attributes = []) - * @phpstan-method static Post&Proxy randomOrCreate(array $attributes = []) - * @phpstan-method static Post[]&Proxy[] all() - * @phpstan-method static Post[]&Proxy[] createMany(int $number, array|callable $attributes = []) - * @phpstan-method static Post[]&Proxy[] createSequence(array|callable $sequence) - * @phpstan-method static Post[]&Proxy[] findBy(array $attributes) - * @phpstan-method static Post[]&Proxy[] randomRange(int $min, int $max, array $attributes = []) - * @phpstan-method static Post[]&Proxy[] randomSet(int $number, array $attributes = []) + * @phpstan-method Proxy create(array|callable $attributes = []) + * @phpstan-method static Proxy createOne(array $attributes = []) + * @phpstan-method static Proxy find(object|array|mixed $criteria) + * @phpstan-method static Proxy findOrCreate(array $attributes) + * @phpstan-method static Proxy first(string $sortedField = 'id') + * @phpstan-method static Proxy last(string $sortedField = 'id') + * @phpstan-method static Proxy random(array $attributes = []) + * @phpstan-method static Proxy randomOrCreate(array $attributes = []) + * @phpstan-method static list> all() + * @phpstan-method static list> createMany(int $number, array|callable $attributes = []) + * @phpstan-method static list> createSequence(array|callable $sequence) + * @phpstan-method static list> findBy(array $attributes) + * @phpstan-method static list> randomRange(int $min, int $max, array $attributes = []) + * @phpstan-method static list> randomSet(int $number, array $attributes = []) + * @phpstan-method static RepositoryProxy repository() */ final class PostFactory extends ModelFactory { diff --git a/src/Bundle/Maker/MakeFactory.php b/src/Bundle/Maker/MakeFactory.php index 8031a2077..2616b9f17 100644 --- a/src/Bundle/Maker/MakeFactory.php +++ b/src/Bundle/Maker/MakeFactory.php @@ -50,7 +50,7 @@ final class MakeFactory extends AbstractMaker /** @var string[] */ private array $entitiesWithFactories = []; - public function __construct(private ManagerRegistry $managerRegistry, \Traversable $factories) + public function __construct(private ManagerRegistry $managerRegistry, \Traversable $factories, private string $projectDir) { $this->entitiesWithFactories = \array_map( static fn(ModelFactory $factory): string => $factory::getEntityClass(), @@ -162,6 +162,7 @@ private function generateFactory(string $class, InputInterface $input, ConsoleSt 'entity' => $entity, 'defaultProperties' => $this->defaultPropertiesFor($entity->getName(), $input->getOption('all-fields')), 'repository' => $repository, + 'phpstanEnabled' => $this->phpstanEnabled(), ] ); @@ -233,4 +234,9 @@ private function defaultPropertiesFor(string $class, bool $allFields): iterable yield $property['fieldName'] => $value; } } + + private function phpstanEnabled(): bool + { + return \file_exists("{$this->projectDir}/vendor/phpstan/phpstan/phpstan"); + } } diff --git a/src/Bundle/Resources/config/services.xml b/src/Bundle/Resources/config/services.xml index 06634b2e1..6cd4df8d4 100644 --- a/src/Bundle/Resources/config/services.xml +++ b/src/Bundle/Resources/config/services.xml @@ -43,6 +43,7 @@ + %kernel.project_dir% diff --git a/src/Bundle/Resources/skeleton/Factory.tpl.php b/src/Bundle/Resources/skeleton/Factory.tpl.php index 4b24d8a95..2b997d467 100644 --- a/src/Bundle/Resources/skeleton/Factory.tpl.php +++ b/src/Bundle/Resources/skeleton/Factory.tpl.php @@ -12,22 +12,40 @@ /** * @extends ModelFactory<getShortName() ?>> * + * @method getShortName() ?>|Proxy create(array|callable $attributes = []) * @method static getShortName() ?>|Proxy createOne(array $attributes = []) - * @method static getShortName() ?>[]|Proxy[] createMany(int $number, array|callable $attributes = []) - * @method static getShortName() ?>[]|Proxy[] createSequence(array|callable $sequence) * @method static getShortName() ?>|Proxy find(object|array|mixed $criteria) * @method static getShortName() ?>|Proxy findOrCreate(array $attributes) * @method static getShortName() ?>|Proxy first(string $sortedField = 'id') * @method static getShortName() ?>|Proxy last(string $sortedField = 'id') * @method static getShortName() ?>|Proxy random(array $attributes = []) * @method static getShortName() ?>|Proxy randomOrCreate(array $attributes = []) - * @method static getShortName() ?>[]|Proxy[] all() - * @method static getShortName() ?>[]|Proxy[] findBy(array $attributes) - * @method static getShortName() ?>[]|Proxy[] randomSet(int $number, array $attributes = []) - * @method static getShortName() ?>[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static list<getShortName() ?>>|list all() + * @method static list<getShortName() ?>>|list createMany(int $number, array|callable $attributes = []) + * @method static list<getShortName() ?>>|list createSequence(array|callable $sequence) + * @method static list<getShortName() ?>>|list findBy(array $attributes) + * @method static list<getShortName() ?>>|list randomRange(int $min, int $max, array $attributes = []) + * @method static list<getShortName() ?>>|list randomSet(int $number, array $attributes = []) * @method static getShortName() ?>|RepositoryProxy repository() - * @method getShortName() ?>|Proxy create(array|callable $attributes = []) + * + * @phpstan-method Proxy<getShortName() ?>> create(array|callable $attributes = []) + * @phpstan-method static Proxy<getShortName() ?>> createOne(array $attributes = []) + * @phpstan-method static Proxy<getShortName() ?>> find(object|array|mixed $criteria) + * @phpstan-method static Proxy<getShortName() ?>> findOrCreate(array $attributes) + * @phpstan-method static Proxy<getShortName() ?>> first(string $sortedField = 'id') + * @phpstan-method static Proxy<getShortName() ?>> last(string $sortedField = 'id') + * @phpstan-method static Proxy<getShortName() ?>> random(array $attributes = []) + * @phpstan-method static Proxy<getShortName() ?>> randomOrCreate(array $attributes = []) + * @phpstan-method static listgetShortName() ?>>> all() + * @phpstan-method static listgetShortName() ?>>> createMany(int $number, array|callable $attributes = []) + * @phpstan-method static listgetShortName() ?>>> createSequence(array|callable $sequence) + * @phpstan-method static listgetShortName() ?>>> findBy(array $attributes) + * @phpstan-method static listgetShortName() ?>>> randomRange(int $min, int $max, array $attributes = []) + * @phpstan-method static listgetShortName() ?>>> randomSet(int $number, array $attributes = []) + * @phpstan-method static RepositoryProxy<getShortName() ?>> repository() + + */ final class extends ModelFactory { diff --git a/tests/Fixtures/Maker/can_create_factory/CategoryFactory.php b/tests/Fixtures/Maker/can_create_factory/CategoryFactory.php new file mode 100644 index 000000000..11f247b39 --- /dev/null +++ b/tests/Fixtures/Maker/can_create_factory/CategoryFactory.php @@ -0,0 +1,56 @@ + + * + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) + * @method static Category|Proxy find(object|array|mixed $criteria) + * @method static Category|Proxy findOrCreate(array $attributes) + * @method static Category|Proxy first(string $sortedField = 'id') + * @method static Category|Proxy last(string $sortedField = 'id') + * @method static Category|Proxy random(array $attributes = []) + * @method static Category|Proxy randomOrCreate(array $attributes = []) + * @method static list|list all() + * @method static list|list createMany(int $number, array|callable $attributes = []) + * @method static list|list createSequence(array|callable $sequence) + * @method static list|list findBy(array $attributes) + * @method static list|list randomRange(int $min, int $max, array $attributes = []) + * @method static list|list randomSet(int $number, array $attributes = []) + */ +final class CategoryFactory extends ModelFactory +{ + public function __construct() + { + parent::__construct(); + + // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) + } + + protected function getDefaults(): array + { + return [ + // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) + 'name' => self::faker()->text(), + ]; + } + + protected function initialize(): self + { + // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + return $this + // ->afterInstantiate(function(Category $category): void {}) + ; + } + + protected static function getClass(): string + { + return Category::class; + } +} diff --git a/tests/Fixtures/Maker/can_create_factory_in_test_dir/CategoryFactory.php b/tests/Fixtures/Maker/can_create_factory_in_test_dir/CategoryFactory.php new file mode 100644 index 000000000..70a9f3c66 --- /dev/null +++ b/tests/Fixtures/Maker/can_create_factory_in_test_dir/CategoryFactory.php @@ -0,0 +1,56 @@ + + * + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) + * @method static Category|Proxy find(object|array|mixed $criteria) + * @method static Category|Proxy findOrCreate(array $attributes) + * @method static Category|Proxy first(string $sortedField = 'id') + * @method static Category|Proxy last(string $sortedField = 'id') + * @method static Category|Proxy random(array $attributes = []) + * @method static Category|Proxy randomOrCreate(array $attributes = []) + * @method static list|list all() + * @method static list|list createMany(int $number, array|callable $attributes = []) + * @method static list|list createSequence(array|callable $sequence) + * @method static list|list findBy(array $attributes) + * @method static list|list randomRange(int $min, int $max, array $attributes = []) + * @method static list|list randomSet(int $number, array $attributes = []) + */ +final class CategoryFactory extends ModelFactory +{ + public function __construct() + { + parent::__construct(); + + // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) + } + + protected function getDefaults(): array + { + return [ + // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) + 'name' => self::faker()->text(), + ]; + } + + protected function initialize(): self + { + // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + return $this + // ->afterInstantiate(function(Category $category): void {}) + ; + } + + protected static function getClass(): string + { + return Category::class; + } +} diff --git a/tests/Fixtures/Maker/can_create_factory_in_test_dir_interactively/TagFactory.php b/tests/Fixtures/Maker/can_create_factory_in_test_dir_interactively/TagFactory.php new file mode 100644 index 000000000..9cc9ce0ac --- /dev/null +++ b/tests/Fixtures/Maker/can_create_factory_in_test_dir_interactively/TagFactory.php @@ -0,0 +1,56 @@ + + * + * @method Tag|Proxy create(array|callable $attributes = []) + * @method static Tag|Proxy createOne(array $attributes = []) + * @method static Tag|Proxy find(object|array|mixed $criteria) + * @method static Tag|Proxy findOrCreate(array $attributes) + * @method static Tag|Proxy first(string $sortedField = 'id') + * @method static Tag|Proxy last(string $sortedField = 'id') + * @method static Tag|Proxy random(array $attributes = []) + * @method static Tag|Proxy randomOrCreate(array $attributes = []) + * @method static list|list all() + * @method static list|list createMany(int $number, array|callable $attributes = []) + * @method static list|list createSequence(array|callable $sequence) + * @method static list|list findBy(array $attributes) + * @method static list|list randomRange(int $min, int $max, array $attributes = []) + * @method static list|list randomSet(int $number, array $attributes = []) + */ +final class TagFactory extends ModelFactory +{ + public function __construct() + { + parent::__construct(); + + // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) + } + + protected function getDefaults(): array + { + return [ + // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) + 'name' => self::faker()->text(), + ]; + } + + protected function initialize(): self + { + // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + return $this + // ->afterInstantiate(function(Tag $tag): void {}) + ; + } + + protected static function getClass(): string + { + return Tag::class; + } +} diff --git a/tests/Fixtures/Maker/can_create_factory_interactively/TagFactory.php b/tests/Fixtures/Maker/can_create_factory_interactively/TagFactory.php new file mode 100644 index 000000000..320977e4a --- /dev/null +++ b/tests/Fixtures/Maker/can_create_factory_interactively/TagFactory.php @@ -0,0 +1,56 @@ + + * + * @method Tag|Proxy create(array|callable $attributes = []) + * @method static Tag|Proxy createOne(array $attributes = []) + * @method static Tag|Proxy find(object|array|mixed $criteria) + * @method static Tag|Proxy findOrCreate(array $attributes) + * @method static Tag|Proxy first(string $sortedField = 'id') + * @method static Tag|Proxy last(string $sortedField = 'id') + * @method static Tag|Proxy random(array $attributes = []) + * @method static Tag|Proxy randomOrCreate(array $attributes = []) + * @method static list|list all() + * @method static list|list createMany(int $number, array|callable $attributes = []) + * @method static list|list createSequence(array|callable $sequence) + * @method static list|list findBy(array $attributes) + * @method static list|list randomRange(int $min, int $max, array $attributes = []) + * @method static list|list randomSet(int $number, array $attributes = []) + */ +final class TagFactory extends ModelFactory +{ + public function __construct() + { + parent::__construct(); + + // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) + } + + protected function getDefaults(): array + { + return [ + // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) + 'name' => self::faker()->text(), + ]; + } + + protected function initialize(): self + { + // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + return $this + // ->afterInstantiate(function(Tag $tag): void {}) + ; + } + + protected static function getClass(): string + { + return Tag::class; + } +} diff --git a/tests/Fixtures/Maker/can_create_factory_with_phpstan_annotations/CategoryFactory.php b/tests/Fixtures/Maker/can_create_factory_with_phpstan_annotations/CategoryFactory.php new file mode 100644 index 000000000..01bdf2acb --- /dev/null +++ b/tests/Fixtures/Maker/can_create_factory_with_phpstan_annotations/CategoryFactory.php @@ -0,0 +1,71 @@ + + * + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) + * @method static Category|Proxy find(object|array|mixed $criteria) + * @method static Category|Proxy findOrCreate(array $attributes) + * @method static Category|Proxy first(string $sortedField = 'id') + * @method static Category|Proxy last(string $sortedField = 'id') + * @method static Category|Proxy random(array $attributes = []) + * @method static Category|Proxy randomOrCreate(array $attributes = []) + * @method static list|list all() + * @method static list|list createMany(int $number, array|callable $attributes = []) + * @method static list|list createSequence(array|callable $sequence) + * @method static list|list findBy(array $attributes) + * @method static list|list randomRange(int $min, int $max, array $attributes = []) + * @method static list|list randomSet(int $number, array $attributes = []) + * + * @phpstan-method Proxy create(array|callable $attributes = []) + * @phpstan-method static Proxy createOne(array $attributes = []) + * @phpstan-method static Proxy find(object|array|mixed $criteria) + * @phpstan-method static Proxy findOrCreate(array $attributes) + * @phpstan-method static Proxy first(string $sortedField = 'id') + * @phpstan-method static Proxy last(string $sortedField = 'id') + * @phpstan-method static Proxy random(array $attributes = []) + * @phpstan-method static Proxy randomOrCreate(array $attributes = []) + * @phpstan-method static list> all() + * @phpstan-method static list> createMany(int $number, array|callable $attributes = []) + * @phpstan-method static list> createSequence(array|callable $sequence) + * @phpstan-method static list> findBy(array $attributes) + * @phpstan-method static list> randomRange(int $min, int $max, array $attributes = []) + * @phpstan-method static list> randomSet(int $number, array $attributes = []) + */ +final class CategoryFactory extends ModelFactory +{ + public function __construct() + { + parent::__construct(); + + // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) + } + + protected function getDefaults(): array + { + return [ + // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) + 'name' => self::faker()->text(), + ]; + } + + protected function initialize(): self + { + // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + return $this + // ->afterInstantiate(function(Category $category): void {}) + ; + } + + protected static function getClass(): string + { + return Category::class; + } +} diff --git a/tests/Functional/Bundle/Maker/MakeFactoryTest.php b/tests/Functional/Bundle/Maker/MakeFactoryTest.php index 48f1e3fc4..0605dd429 100644 --- a/tests/Functional/Bundle/Maker/MakeFactoryTest.php +++ b/tests/Functional/Bundle/Maker/MakeFactoryTest.php @@ -15,6 +15,19 @@ */ final class MakeFactoryTest extends MakerTestCase { + private const PHPSTAN_PATH = __DIR__.'/../../../../vendor/phpstan/phpstan/phpstan'; + + protected function tearDown(): void + { + parent::tearDown(); + + if (\file_exists(self::PHPSTAN_PATH)) { + \unlink(self::PHPSTAN_PATH); + \rmdir(\dirname(self::PHPSTAN_PATH)); + \rmdir(\dirname(self::PHPSTAN_PATH, 2)); + } + } + /** * @test */ @@ -30,67 +43,9 @@ public function can_create_factory(): void $tester->execute(['entity' => Category::class]); - $this->assertFileExists(self::tempFile('src/Factory/CategoryFactory.php')); - $this->assertSame(<< - * - * @method static Category|Proxy createOne(array \$attributes = []) - * @method static Category[]|Proxy[] createMany(int \$number, array|callable \$attributes = []) - * @method static Category[]|Proxy[] createSequence(array|callable \$sequence) - * @method static Category|Proxy find(object|array|mixed \$criteria) - * @method static Category|Proxy findOrCreate(array \$attributes) - * @method static Category|Proxy first(string \$sortedField = 'id') - * @method static Category|Proxy last(string \$sortedField = 'id') - * @method static Category|Proxy random(array \$attributes = []) - * @method static Category|Proxy randomOrCreate(array \$attributes = []) - * @method static Category[]|Proxy[] all() - * @method static Category[]|Proxy[] findBy(array \$attributes) - * @method static Category[]|Proxy[] randomSet(int \$number, array \$attributes = []) - * @method static Category[]|Proxy[] randomRange(int \$min, int \$max, array \$attributes = []) - * @method Category|Proxy create(array|callable \$attributes = []) - */ -final class CategoryFactory extends ModelFactory -{ - public function __construct() - { - parent::__construct(); - - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) - } - - protected function getDefaults(): array - { - return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) - 'name' => self::faker()->text(), - ]; - } - - protected function initialize(): self - { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization - return \$this - // ->afterInstantiate(function(Category \$category): void {}) - ; - } - - protected static function getClass(): string - { - return Category::class; - } -} - -EOF - , \file_get_contents(self::tempFile('src/Factory/CategoryFactory.php')) + $this->assertFileFromMakerSameAsExpectedFile( + $this->expectedFile('CategoryFactory.php'), + self::tempFile('src/Factory/CategoryFactory.php') ); } @@ -113,68 +68,11 @@ public function can_create_factory_interactively(): void $output = $tester->getDisplay(); $this->assertStringNotContainsString(Category::class, $output); - $this->assertFileExists(self::tempFile('src/Factory/TagFactory.php')); $this->assertStringContainsString('Note: pass --test if you want to generate factories in your tests/ directory', $output); - $this->assertSame(<< - * - * @method static Tag|Proxy createOne(array \$attributes = []) - * @method static Tag[]|Proxy[] createMany(int \$number, array|callable \$attributes = []) - * @method static Tag[]|Proxy[] createSequence(array|callable \$sequence) - * @method static Tag|Proxy find(object|array|mixed \$criteria) - * @method static Tag|Proxy findOrCreate(array \$attributes) - * @method static Tag|Proxy first(string \$sortedField = 'id') - * @method static Tag|Proxy last(string \$sortedField = 'id') - * @method static Tag|Proxy random(array \$attributes = []) - * @method static Tag|Proxy randomOrCreate(array \$attributes = []) - * @method static Tag[]|Proxy[] all() - * @method static Tag[]|Proxy[] findBy(array \$attributes) - * @method static Tag[]|Proxy[] randomSet(int \$number, array \$attributes = []) - * @method static Tag[]|Proxy[] randomRange(int \$min, int \$max, array \$attributes = []) - * @method Tag|Proxy create(array|callable \$attributes = []) - */ -final class TagFactory extends ModelFactory -{ - public function __construct() - { - parent::__construct(); - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) - } - - protected function getDefaults(): array - { - return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) - 'name' => self::faker()->text(), - ]; - } - - protected function initialize(): self - { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization - return \$this - // ->afterInstantiate(function(Tag \$tag): void {}) - ; - } - - protected static function getClass(): string - { - return Tag::class; - } -} - -EOF - , \file_get_contents(self::tempFile('src/Factory/TagFactory.php')) + $this->assertFileFromMakerSameAsExpectedFile( + $this->expectedFile('TagFactory.php'), + self::tempFile('src/Factory/TagFactory.php') ); } @@ -193,67 +91,9 @@ public function can_create_factory_in_test_dir(): void $tester->execute(['entity' => Category::class, '--test' => true]); - $this->assertFileExists(self::tempFile('tests/Factory/CategoryFactory.php')); - $this->assertSame(<< - * - * @method static Category|Proxy createOne(array \$attributes = []) - * @method static Category[]|Proxy[] createMany(int \$number, array|callable \$attributes = []) - * @method static Category[]|Proxy[] createSequence(array|callable \$sequence) - * @method static Category|Proxy find(object|array|mixed \$criteria) - * @method static Category|Proxy findOrCreate(array \$attributes) - * @method static Category|Proxy first(string \$sortedField = 'id') - * @method static Category|Proxy last(string \$sortedField = 'id') - * @method static Category|Proxy random(array \$attributes = []) - * @method static Category|Proxy randomOrCreate(array \$attributes = []) - * @method static Category[]|Proxy[] all() - * @method static Category[]|Proxy[] findBy(array \$attributes) - * @method static Category[]|Proxy[] randomSet(int \$number, array \$attributes = []) - * @method static Category[]|Proxy[] randomRange(int \$min, int \$max, array \$attributes = []) - * @method Category|Proxy create(array|callable \$attributes = []) - */ -final class CategoryFactory extends ModelFactory -{ - public function __construct() - { - parent::__construct(); - - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) - } - - protected function getDefaults(): array - { - return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) - 'name' => self::faker()->text(), - ]; - } - - protected function initialize(): self - { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization - return \$this - // ->afterInstantiate(function(Category \$category): void {}) - ; - } - - protected static function getClass(): string - { - return Category::class; - } -} - -EOF - , \file_get_contents(self::tempFile('tests/Factory/CategoryFactory.php')) + $this->assertFileFromMakerSameAsExpectedFile( + $this->expectedFile('CategoryFactory.php'), + self::tempFile('tests/Factory/CategoryFactory.php') ); } @@ -275,69 +115,36 @@ public function can_create_factory_in_test_dir_interactively(): void $output = $tester->getDisplay(); - $this->assertFileExists(self::tempFile('tests/Factory/TagFactory.php')); $this->assertStringNotContainsString(Category::class, $output); $this->assertStringNotContainsString('Note: pass --test if you want to generate factories in your tests/ directory', $output); - $this->assertSame(<<assertFileFromMakerSameAsExpectedFile( + $this->expectedFile('TagFactory.php'), + self::tempFile('tests/Factory/TagFactory.php') + ); + } -/** - * @extends ModelFactory - * - * @method static Tag|Proxy createOne(array \$attributes = []) - * @method static Tag[]|Proxy[] createMany(int \$number, array|callable \$attributes = []) - * @method static Tag[]|Proxy[] createSequence(array|callable \$sequence) - * @method static Tag|Proxy find(object|array|mixed \$criteria) - * @method static Tag|Proxy findOrCreate(array \$attributes) - * @method static Tag|Proxy first(string \$sortedField = 'id') - * @method static Tag|Proxy last(string \$sortedField = 'id') - * @method static Tag|Proxy random(array \$attributes = []) - * @method static Tag|Proxy randomOrCreate(array \$attributes = []) - * @method static Tag[]|Proxy[] all() - * @method static Tag[]|Proxy[] findBy(array \$attributes) - * @method static Tag[]|Proxy[] randomSet(int \$number, array \$attributes = []) - * @method static Tag[]|Proxy[] randomRange(int \$min, int \$max, array \$attributes = []) - * @method Tag|Proxy create(array|callable \$attributes = []) - */ -final class TagFactory extends ModelFactory -{ - public function __construct() + /** + * @test + */ + public function can_create_factory_with_phpstan_annotations(): void { - parent::__construct(); + if (!\getenv('USE_ORM')) { + self::markTestSkipped('doctrine/orm not enabled.'); + } - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) - } + \mkdir(\dirname(self::PHPSTAN_PATH), 0777, true); + \touch(self::PHPSTAN_PATH); - protected function getDefaults(): array - { - return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) - 'name' => self::faker()->text(), - ]; - } + $tester = new CommandTester((new Application(self::bootKernel()))->find('make:factory')); - protected function initialize(): self - { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization - return \$this - // ->afterInstantiate(function(Tag \$tag): void {}) - ; - } + $this->assertFileDoesNotExist(self::tempFile('src/Factory/CategoryFactory.php')); - protected static function getClass(): string - { - return Tag::class; - } -} + $tester->execute(['entity' => Category::class]); -EOF - , \file_get_contents(self::tempFile('tests/Factory/TagFactory.php')) + $this->assertFileFromMakerSameAsExpectedFile( + $this->expectedFile('CategoryFactory.php'), + self::tempFile('src/Factory/CategoryFactory.php') ); } diff --git a/tests/Functional/Bundle/Maker/MakerTestCase.php b/tests/Functional/Bundle/Maker/MakerTestCase.php index 86adc9f02..fc18a0a54 100644 --- a/tests/Functional/Bundle/Maker/MakerTestCase.php +++ b/tests/Functional/Bundle/Maker/MakerTestCase.php @@ -41,4 +41,15 @@ protected static function tempFile(string $path): string { return \sprintf('%s/%s', self::tempDir(), $path); } + + protected function expectedFile(string $file): string + { + return \sprintf('%s/../../../Fixtures/Maker/%s/%s', __DIR__, $this->getName(), $file); + } + + protected function assertFileFromMakerSameAsExpectedFile(string $expectedFile, string $fileFromMaker): void + { + $this->assertFileExists($fileFromMaker); + $this->assertFileEquals($expectedFile, $fileFromMaker); + } } From 778607ae0b39d281469ddecc607ad2385e928024 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Tue, 15 Nov 2022 07:57:30 +0100 Subject: [PATCH 11/30] [chore] use cache for docker CI (#339) fixes https://github.com/zenstruck/foundry/issues/337 --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++++ Makefile | 12 ++++++++++-- docker-compose.ci.yaml | 5 +++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 docker-compose.ci.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d05c95be..f6dd06582 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -217,6 +217,33 @@ jobs: - name: Checkout code uses: actions/checkout@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Create cache directory + run: mkdir -p var/cache-docker + + - id: cache-docker + uses: actions/cache@v3 + with: + path: var/cache-docker + key: ${{ hashFiles('docker/*') }} + + - name: Docker build + uses: docker/build-push-action@v3 + if: steps.cache-docker.outputs.cache-hit != 'true' + with: + context: . + file: ./docker/Dockerfile + outputs: type=docker,dest=var/cache-docker/php.tar + tags: | + foundry_php:ci + push: false + + - name: Load docker context + run: | + docker load < var/cache-docker/php.tar + - name: Run test suite with docker run: | make validate diff --git a/Makefile b/Makefile index c87adca59..1a6d52ca5 100644 --- a/Makefile +++ b/Makefile @@ -4,15 +4,19 @@ MYSQL_URL="mysql://root:root@mysql:3306/zenstruck_foundry?charset=utf8" MONGO_URL="mongodb://mongo:mongo@mongo:27017/mongo?compressors=disabled&gssapiServiceName=mongodb&authSource=mongo" ifeq ($(shell docker --help | grep "compose"),) - DOCKER_COMPOSE=docker-compose + DOCKER_COMPOSE_BIN=docker-compose else - DOCKER_COMPOSE=docker compose + DOCKER_COMPOSE_BIN=docker compose endif INTERACTIVE:=$(shell [ -t 0 ] && echo 1) ifdef INTERACTIVE + CI='false' + DOCKER_COMPOSE=$(DOCKER_COMPOSE_BIN) DC_EXEC=$(DOCKER_COMPOSE) exec -e USE_FOUNDRY_BUNDLE=1 -e DATABASE_URL=${MYSQL_URL} -e MONGO_URL=${MONGO_URL} else + CI='true' + DOCKER_COMPOSE=$(DOCKER_COMPOSE_BIN) -f docker-compose.yaml -f docker-compose.ci.yaml DC_EXEC=$(DOCKER_COMPOSE) exec -e USE_FOUNDRY_BUNDLE=1 -e DATABASE_URL=${MYSQL_URL} -e MONGO_URL=${MONGO_URL} -T endif @@ -84,8 +88,12 @@ vendor: composer.json $(wildcard composer.lock) docker-start: ### Build and run containers ifeq ($(DOCKER_IS_UP),) +ifeq ($(CI),'false') @$(DOCKER_COMPOSE) build --build-arg UID="${UID}" --build-arg XDEBUG_HOST="${XDEBUG_HOST}" @$(DOCKER_COMPOSE) up --detach --remove-orphans +else + @$(DOCKER_COMPOSE) up --detach --no-build +endif @$(DOCKER_COMPOSE) ps endif diff --git a/docker-compose.ci.yaml b/docker-compose.ci.yaml new file mode 100644 index 000000000..852eac11f --- /dev/null +++ b/docker-compose.ci.yaml @@ -0,0 +1,5 @@ +version: '3.7' + +services: + php: + image: foundry_php:ci From b1d7ce3fd8fe9faf0a4efb3787e04403a6826150 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Tue, 15 Nov 2022 11:46:15 +0100 Subject: [PATCH 12/30] [feat] add default for Mongo properties in (#340) --- composer.json | 3 +- src/Bundle/Maker/MakeFactory.php | 16 +++-- ...factory-for-odm-with-data-set-document.php | 61 +++++++++++++++++++ ...or-odm-with-data-set-embedded-document.php | 59 ++++++++++++++++++ ...ate-factory-in-test-dir-interactively.php} | 0 .../can-create-factory-in-test-dir.php} | 0 .../can-create-factory-interactively.php} | 0 ...eate-factory-with-phpstan-annotations.php} | 0 .../can-create-factory.php} | 0 .../Bundle/Maker/MakeFactoryTest.php | 27 ++------ .../Functional/Bundle/Maker/MakerTestCase.php | 17 ++++-- 11 files changed, 152 insertions(+), 31 deletions(-) create mode 100644 tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php create mode 100644 tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php rename tests/Fixtures/Maker/{can_create_factory_in_test_dir_interactively/TagFactory.php => expected/can-create-factory-in-test-dir-interactively.php} (100%) rename tests/Fixtures/Maker/{can_create_factory_in_test_dir/CategoryFactory.php => expected/can-create-factory-in-test-dir.php} (100%) rename tests/Fixtures/Maker/{can_create_factory_interactively/TagFactory.php => expected/can-create-factory-interactively.php} (100%) rename tests/Fixtures/Maker/{can_create_factory_with_phpstan_annotations/CategoryFactory.php => expected/can-create-factory-with-phpstan-annotations.php} (100%) rename tests/Fixtures/Maker/{can_create_factory/CategoryFactory.php => expected/can-create-factory.php} (100%) diff --git a/composer.json b/composer.json index 239919515..8c3c9bb06 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,8 @@ "matthiasnoback/symfony-dependency-injection-test": "^4.1", "symfony/framework-bundle": "^4.4|^5.0|^6.0", "symfony/maker-bundle": "^1.30", - "symfony/phpunit-bridge": "^6.0" + "symfony/phpunit-bridge": "^6.0", + "symfony/translation-contracts": "^3.0" }, "config": { "preferred-install": "dist", diff --git a/src/Bundle/Maker/MakeFactory.php b/src/Bundle/Maker/MakeFactory.php index 2616b9f17..0b2ed58f1 100644 --- a/src/Bundle/Maker/MakeFactory.php +++ b/src/Bundle/Maker/MakeFactory.php @@ -2,8 +2,11 @@ namespace Zenstruck\Foundry\Bundle\Maker; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata as ODMClassMetadata; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo as ORMClassMetadata; use Doctrine\Persistence\ManagerRegistry; +use Doctrine\Persistence\ObjectManager; use Symfony\Bundle\MakerBundle\ConsoleStyle; use Symfony\Bundle\MakerBundle\DependencyBuilder; use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; @@ -21,7 +24,7 @@ */ final class MakeFactory extends AbstractMaker { - private const ORM_DEFAULTS = [ + private const DEFAULTS = [ 'ARRAY' => '[],', 'ASCII_STRING' => 'self::faker()->text(),', 'BIGINT' => 'self::faker()->randomNumber(),', @@ -211,13 +214,16 @@ private function defaultPropertiesFor(string $class, bool $allFields): iterable { $em = $this->managerRegistry->getManagerForClass($class); - if (!$em instanceof EntityManagerInterface) { + if (!$em instanceof ObjectManager) { return []; } + /** @var ORMClassMetadata|ODMClassMetadata $metadata */ $metadata = $em->getClassMetadata($class); $ids = $metadata->getIdentifierFieldNames(); + $dbType = $em instanceof EntityManagerInterface ? 'ORM' : 'ODM'; + foreach ($metadata->fieldMappings as $property) { // ignore identifiers and nullable fields if ((!$allFields && ($property['nullable'] ?? false)) || \in_array($property['fieldName'], $ids, true)) { @@ -225,10 +231,10 @@ private function defaultPropertiesFor(string $class, bool $allFields): iterable } $type = \mb_strtoupper($property['type']); - $value = "null, // TODO add {$type} ORM type manually"; + $value = "null, // TODO add {$type} {$dbType} type manually"; - if (\array_key_exists($type, self::ORM_DEFAULTS)) { - $value = self::ORM_DEFAULTS[$type]; + if (\array_key_exists($type, self::DEFAULTS)) { + $value = self::DEFAULTS[$type]; } yield $property['fieldName'] => $value; diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php new file mode 100644 index 000000000..eca653169 --- /dev/null +++ b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php @@ -0,0 +1,61 @@ + + * + * @method Post|Proxy create(array|callable $attributes = []) + * @method static Post|Proxy createOne(array $attributes = []) + * @method static Post|Proxy find(object|array|mixed $criteria) + * @method static Post|Proxy findOrCreate(array $attributes) + * @method static Post|Proxy first(string $sortedField = 'id') + * @method static Post|Proxy last(string $sortedField = 'id') + * @method static Post|Proxy random(array $attributes = []) + * @method static Post|Proxy randomOrCreate(array $attributes = []) + * @method static list|list all() + * @method static list|list createMany(int $number, array|callable $attributes = []) + * @method static list|list createSequence(array|callable $sequence) + * @method static list|list findBy(array $attributes) + * @method static list|list randomRange(int $min, int $max, array $attributes = []) + * @method static list|list randomSet(int $number, array $attributes = []) + */ +final class PostFactory extends ModelFactory +{ + public function __construct() + { + parent::__construct(); + + // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) + } + + protected function getDefaults(): array + { + return [ + // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) + 'title' => self::faker()->text(), + 'body' => self::faker()->text(), + 'viewCount' => null, // TODO add INT ODM type manually + 'createdAt' => self::faker()->dateTime(), + 'comments' => null, // TODO add MANY ODM type manually + 'user' => null, // TODO add ONE ODM type manually + ]; + } + + protected function initialize(): self + { + // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + return $this + // ->afterInstantiate(function(Post $post): void {}) + ; + } + + protected static function getClass(): string + { + return Post::class; + } +} diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php new file mode 100644 index 000000000..38b3d9be8 --- /dev/null +++ b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php @@ -0,0 +1,59 @@ + + * + * @method Comment|Proxy create(array|callable $attributes = []) + * @method static Comment|Proxy createOne(array $attributes = []) + * @method static Comment|Proxy find(object|array|mixed $criteria) + * @method static Comment|Proxy findOrCreate(array $attributes) + * @method static Comment|Proxy first(string $sortedField = 'id') + * @method static Comment|Proxy last(string $sortedField = 'id') + * @method static Comment|Proxy random(array $attributes = []) + * @method static Comment|Proxy randomOrCreate(array $attributes = []) + * @method static list|list all() + * @method static list|list createMany(int $number, array|callable $attributes = []) + * @method static list|list createSequence(array|callable $sequence) + * @method static list|list findBy(array $attributes) + * @method static list|list randomRange(int $min, int $max, array $attributes = []) + * @method static list|list randomSet(int $number, array $attributes = []) + */ +final class CommentFactory extends ModelFactory +{ + public function __construct() + { + parent::__construct(); + + // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) + } + + protected function getDefaults(): array + { + return [ + // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) + 'user' => null, // TODO add ONE ODM type manually + 'body' => self::faker()->text(), + 'createdAt' => self::faker()->dateTime(), + 'approved' => self::faker()->boolean(), + ]; + } + + protected function initialize(): self + { + // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + return $this + // ->afterInstantiate(function(Comment $comment): void {}) + ; + } + + protected static function getClass(): string + { + return Comment::class; + } +} diff --git a/tests/Fixtures/Maker/can_create_factory_in_test_dir_interactively/TagFactory.php b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php similarity index 100% rename from tests/Fixtures/Maker/can_create_factory_in_test_dir_interactively/TagFactory.php rename to tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php diff --git a/tests/Fixtures/Maker/can_create_factory_in_test_dir/CategoryFactory.php b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php similarity index 100% rename from tests/Fixtures/Maker/can_create_factory_in_test_dir/CategoryFactory.php rename to tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php diff --git a/tests/Fixtures/Maker/can_create_factory_interactively/TagFactory.php b/tests/Fixtures/Maker/expected/can-create-factory-interactively.php similarity index 100% rename from tests/Fixtures/Maker/can_create_factory_interactively/TagFactory.php rename to tests/Fixtures/Maker/expected/can-create-factory-interactively.php diff --git a/tests/Fixtures/Maker/can_create_factory_with_phpstan_annotations/CategoryFactory.php b/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php similarity index 100% rename from tests/Fixtures/Maker/can_create_factory_with_phpstan_annotations/CategoryFactory.php rename to tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php diff --git a/tests/Fixtures/Maker/can_create_factory/CategoryFactory.php b/tests/Fixtures/Maker/expected/can-create-factory.php similarity index 100% rename from tests/Fixtures/Maker/can_create_factory/CategoryFactory.php rename to tests/Fixtures/Maker/expected/can-create-factory.php diff --git a/tests/Functional/Bundle/Maker/MakeFactoryTest.php b/tests/Functional/Bundle/Maker/MakeFactoryTest.php index 0605dd429..babdc705c 100644 --- a/tests/Functional/Bundle/Maker/MakeFactoryTest.php +++ b/tests/Functional/Bundle/Maker/MakeFactoryTest.php @@ -43,10 +43,7 @@ public function can_create_factory(): void $tester->execute(['entity' => Category::class]); - $this->assertFileFromMakerSameAsExpectedFile( - $this->expectedFile('CategoryFactory.php'), - self::tempFile('src/Factory/CategoryFactory.php') - ); + $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/CategoryFactory.php')); } /** @@ -70,10 +67,7 @@ public function can_create_factory_interactively(): void $this->assertStringNotContainsString(Category::class, $output); $this->assertStringContainsString('Note: pass --test if you want to generate factories in your tests/ directory', $output); - $this->assertFileFromMakerSameAsExpectedFile( - $this->expectedFile('TagFactory.php'), - self::tempFile('src/Factory/TagFactory.php') - ); + $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/TagFactory.php')); } /** @@ -91,10 +85,7 @@ public function can_create_factory_in_test_dir(): void $tester->execute(['entity' => Category::class, '--test' => true]); - $this->assertFileFromMakerSameAsExpectedFile( - $this->expectedFile('CategoryFactory.php'), - self::tempFile('tests/Factory/CategoryFactory.php') - ); + $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('tests/Factory/CategoryFactory.php')); } /** @@ -118,10 +109,7 @@ public function can_create_factory_in_test_dir_interactively(): void $this->assertStringNotContainsString(Category::class, $output); $this->assertStringNotContainsString('Note: pass --test if you want to generate factories in your tests/ directory', $output); - $this->assertFileFromMakerSameAsExpectedFile( - $this->expectedFile('TagFactory.php'), - self::tempFile('tests/Factory/TagFactory.php') - ); + $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('tests/Factory/TagFactory.php')); } /** @@ -142,10 +130,7 @@ public function can_create_factory_with_phpstan_annotations(): void $tester->execute(['entity' => Category::class]); - $this->assertFileFromMakerSameAsExpectedFile( - $this->expectedFile('CategoryFactory.php'), - self::tempFile('src/Factory/CategoryFactory.php') - ); + $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/CategoryFactory.php')); } /** @@ -293,7 +278,7 @@ public function can_create_factory_for_odm(string $class, string $file): void $tester->setInputs([$class]); $tester->execute([]); - $this->assertFileExists(self::tempFile("src/Factory/{$file}.php")); + $this->assertFileFromMakerSameAsExpectedFile(self::tempFile("src/Factory/{$file}.php")); } public function documentProvider(): iterable diff --git a/tests/Functional/Bundle/Maker/MakerTestCase.php b/tests/Functional/Bundle/Maker/MakerTestCase.php index fc18a0a54..2108a8624 100644 --- a/tests/Functional/Bundle/Maker/MakerTestCase.php +++ b/tests/Functional/Bundle/Maker/MakerTestCase.php @@ -4,6 +4,7 @@ use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\String\Slugger\AsciiSlugger; /** * @author Kevin Bond @@ -42,14 +43,22 @@ protected static function tempFile(string $path): string return \sprintf('%s/%s', self::tempDir(), $path); } - protected function expectedFile(string $file): string + protected function expectedFile(): string { - return \sprintf('%s/../../../Fixtures/Maker/%s/%s', __DIR__, $this->getName(), $file); + $path = \sprintf( + '%s/../../../Fixtures/Maker/expected/%s.php', + __DIR__, + (new AsciiSlugger())->slug($this->getName()) + ); + + $this->assertFileExists($path); + + return \realpath($path); } - protected function assertFileFromMakerSameAsExpectedFile(string $expectedFile, string $fileFromMaker): void + protected function assertFileFromMakerSameAsExpectedFile(string $fileFromMaker): void { $this->assertFileExists($fileFromMaker); - $this->assertFileEquals($expectedFile, $fileFromMaker); + $this->assertFileEquals($this->expectedFile(), $fileFromMaker); } } From cd1e39474a052527b30b52dc42a78434e6384569 Mon Sep 17 00:00:00 2001 From: MrYamous Date: Tue, 15 Nov 2022 13:58:53 +0100 Subject: [PATCH 13/30] fix: use orm limit length in factory (#294) --- src/Bundle/Maker/MakeFactory.php | 13 +++++++------ ...can-create-factory-in-test-dir-interactively.php | 2 +- .../expected/can-create-factory-in-test-dir.php | 2 +- .../expected/can-create-factory-interactively.php | 2 +- .../can-create-factory-with-phpstan-annotations.php | 2 +- .../Fixtures/Maker/expected/can-create-factory.php | 2 +- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Bundle/Maker/MakeFactory.php b/src/Bundle/Maker/MakeFactory.php index 0b2ed58f1..70dbb2a50 100644 --- a/src/Bundle/Maker/MakeFactory.php +++ b/src/Bundle/Maker/MakeFactory.php @@ -26,7 +26,7 @@ final class MakeFactory extends AbstractMaker { private const DEFAULTS = [ 'ARRAY' => '[],', - 'ASCII_STRING' => 'self::faker()->text(),', + 'ASCII_STRING' => 'self::faker()->text({length}),', 'BIGINT' => 'self::faker()->randomNumber(),', 'BLOB' => 'self::faker()->text(),', 'BOOLEAN' => 'self::faker()->boolean(),', @@ -44,10 +44,10 @@ final class MakeFactory extends AbstractMaker 'JSON_ARRAY' => '[],', 'SIMPLE_ARRAY' => '[],', 'SMALLINT' => 'self::faker()->numberBetween(1, 32767),', - 'STRING' => 'self::faker()->text(),', - 'TEXT' => 'self::faker()->text(),', - 'TIME_MUTABLE' => 'self::faker()->dateTime(),', - 'TIME_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', + 'STRING' => 'self::faker()->text({length}),', + 'TEXT' => 'self::faker()->text({length}),', + 'TIME_MUTABLE' => 'self::faker()->datetime(),', + 'TIME_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->datetime()),', ]; /** @var string[] */ @@ -232,12 +232,13 @@ private function defaultPropertiesFor(string $class, bool $allFields): iterable $type = \mb_strtoupper($property['type']); $value = "null, // TODO add {$type} {$dbType} type manually"; + $length = $property['length'] ?? ''; if (\array_key_exists($type, self::DEFAULTS)) { $value = self::DEFAULTS[$type]; } - yield $property['fieldName'] => $value; + yield $property['fieldName'] => \str_replace('{length}', (string) $length, $value); } } diff --git a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php index 9cc9ce0ac..4c4b075c3 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php @@ -37,7 +37,7 @@ protected function getDefaults(): array { return [ // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) - 'name' => self::faker()->text(), + 'name' => self::faker()->text(255), ]; } diff --git a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php index 70a9f3c66..c439d1eb2 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php @@ -37,7 +37,7 @@ protected function getDefaults(): array { return [ // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) - 'name' => self::faker()->text(), + 'name' => self::faker()->text(255), ]; } diff --git a/tests/Fixtures/Maker/expected/can-create-factory-interactively.php b/tests/Fixtures/Maker/expected/can-create-factory-interactively.php index 320977e4a..ad81771f5 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-interactively.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-interactively.php @@ -37,7 +37,7 @@ protected function getDefaults(): array { return [ // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) - 'name' => self::faker()->text(), + 'name' => self::faker()->text(255), ]; } diff --git a/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php b/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php index 01bdf2acb..494309e06 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php @@ -52,7 +52,7 @@ protected function getDefaults(): array { return [ // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) - 'name' => self::faker()->text(), + 'name' => self::faker()->text(255), ]; } diff --git a/tests/Fixtures/Maker/expected/can-create-factory.php b/tests/Fixtures/Maker/expected/can-create-factory.php index 11f247b39..6e6c00f0b 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory.php +++ b/tests/Fixtures/Maker/expected/can-create-factory.php @@ -37,7 +37,7 @@ protected function getDefaults(): array { return [ // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) - 'name' => self::faker()->text(), + 'name' => self::faker()->text(255), ]; } From cbeb2ce4d5d7f744f51d41c20cfa8b7abc767860 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Tue, 15 Nov 2022 17:21:35 +0100 Subject: [PATCH 14/30] fix: adjust docblocks to remove PhpStorm errors (#341) --- src/Bundle/Resources/skeleton/Factory.tpl.php | 12 ++++++------ ...create-factory-for-odm-with-data-set-document.php | 12 ++++++------ ...ctory-for-odm-with-data-set-embedded-document.php | 12 ++++++------ .../can-create-factory-in-test-dir-interactively.php | 12 ++++++------ .../expected/can-create-factory-in-test-dir.php | 12 ++++++------ .../expected/can-create-factory-interactively.php | 12 ++++++------ .../can-create-factory-with-phpstan-annotations.php | 12 ++++++------ tests/Fixtures/Maker/expected/can-create-factory.php | 12 ++++++------ 8 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/Bundle/Resources/skeleton/Factory.tpl.php b/src/Bundle/Resources/skeleton/Factory.tpl.php index 2b997d467..92f30ec21 100644 --- a/src/Bundle/Resources/skeleton/Factory.tpl.php +++ b/src/Bundle/Resources/skeleton/Factory.tpl.php @@ -20,12 +20,12 @@ * @method static getShortName() ?>|Proxy last(string $sortedField = 'id') * @method static getShortName() ?>|Proxy random(array $attributes = []) * @method static getShortName() ?>|Proxy randomOrCreate(array $attributes = []) - * @method static list<getShortName() ?>>|list all() - * @method static list<getShortName() ?>>|list createMany(int $number, array|callable $attributes = []) - * @method static list<getShortName() ?>>|list createSequence(array|callable $sequence) - * @method static list<getShortName() ?>>|list findBy(array $attributes) - * @method static list<getShortName() ?>>|list randomRange(int $min, int $max, array $attributes = []) - * @method static list<getShortName() ?>>|list randomSet(int $number, array $attributes = []) + * @method static getShortName() ?>[]|Proxy[] all() + * @method static getShortName() ?>[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static getShortName() ?>[]|Proxy[] createSequence(array|callable $sequence) + * @method static getShortName() ?>[]|Proxy[] findBy(array $attributes) + * @method static getShortName() ?>[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static getShortName() ?>[]|Proxy[] randomSet(int $number, array $attributes = []) * @method static getShortName() ?>|RepositoryProxy repository() * diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php index eca653169..2a297f342 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php @@ -17,12 +17,12 @@ * @method static Post|Proxy last(string $sortedField = 'id') * @method static Post|Proxy random(array $attributes = []) * @method static Post|Proxy randomOrCreate(array $attributes = []) - * @method static list|list all() - * @method static list|list createMany(int $number, array|callable $attributes = []) - * @method static list|list createSequence(array|callable $sequence) - * @method static list|list findBy(array $attributes) - * @method static list|list randomRange(int $min, int $max, array $attributes = []) - * @method static list|list randomSet(int $number, array $attributes = []) + * @method static Post[]|Proxy[] all() + * @method static Post[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static Post[]|Proxy[] createSequence(array|callable $sequence) + * @method static Post[]|Proxy[] findBy(array $attributes) + * @method static Post[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static Post[]|Proxy[] randomSet(int $number, array $attributes = []) */ final class PostFactory extends ModelFactory { diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php index 38b3d9be8..8688fbb06 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php @@ -17,12 +17,12 @@ * @method static Comment|Proxy last(string $sortedField = 'id') * @method static Comment|Proxy random(array $attributes = []) * @method static Comment|Proxy randomOrCreate(array $attributes = []) - * @method static list|list all() - * @method static list|list createMany(int $number, array|callable $attributes = []) - * @method static list|list createSequence(array|callable $sequence) - * @method static list|list findBy(array $attributes) - * @method static list|list randomRange(int $min, int $max, array $attributes = []) - * @method static list|list randomSet(int $number, array $attributes = []) + * @method static Comment[]|Proxy[] all() + * @method static Comment[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static Comment[]|Proxy[] createSequence(array|callable $sequence) + * @method static Comment[]|Proxy[] findBy(array $attributes) + * @method static Comment[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static Comment[]|Proxy[] randomSet(int $number, array $attributes = []) */ final class CommentFactory extends ModelFactory { diff --git a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php index 4c4b075c3..8b0ab2435 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php @@ -17,12 +17,12 @@ * @method static Tag|Proxy last(string $sortedField = 'id') * @method static Tag|Proxy random(array $attributes = []) * @method static Tag|Proxy randomOrCreate(array $attributes = []) - * @method static list|list all() - * @method static list|list createMany(int $number, array|callable $attributes = []) - * @method static list|list createSequence(array|callable $sequence) - * @method static list|list findBy(array $attributes) - * @method static list|list randomRange(int $min, int $max, array $attributes = []) - * @method static list|list randomSet(int $number, array $attributes = []) + * @method static Tag[]|Proxy[] all() + * @method static Tag[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static Tag[]|Proxy[] createSequence(array|callable $sequence) + * @method static Tag[]|Proxy[] findBy(array $attributes) + * @method static Tag[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static Tag[]|Proxy[] randomSet(int $number, array $attributes = []) */ final class TagFactory extends ModelFactory { diff --git a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php index c439d1eb2..d5133fe13 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php @@ -17,12 +17,12 @@ * @method static Category|Proxy last(string $sortedField = 'id') * @method static Category|Proxy random(array $attributes = []) * @method static Category|Proxy randomOrCreate(array $attributes = []) - * @method static list|list all() - * @method static list|list createMany(int $number, array|callable $attributes = []) - * @method static list|list createSequence(array|callable $sequence) - * @method static list|list findBy(array $attributes) - * @method static list|list randomRange(int $min, int $max, array $attributes = []) - * @method static list|list randomSet(int $number, array $attributes = []) + * @method static Category[]|Proxy[] all() + * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static Category[]|Proxy[] createSequence(array|callable $sequence) + * @method static Category[]|Proxy[] findBy(array $attributes) + * @method static Category[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static Category[]|Proxy[] randomSet(int $number, array $attributes = []) */ final class CategoryFactory extends ModelFactory { diff --git a/tests/Fixtures/Maker/expected/can-create-factory-interactively.php b/tests/Fixtures/Maker/expected/can-create-factory-interactively.php index ad81771f5..f845c0172 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-interactively.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-interactively.php @@ -17,12 +17,12 @@ * @method static Tag|Proxy last(string $sortedField = 'id') * @method static Tag|Proxy random(array $attributes = []) * @method static Tag|Proxy randomOrCreate(array $attributes = []) - * @method static list|list all() - * @method static list|list createMany(int $number, array|callable $attributes = []) - * @method static list|list createSequence(array|callable $sequence) - * @method static list|list findBy(array $attributes) - * @method static list|list randomRange(int $min, int $max, array $attributes = []) - * @method static list|list randomSet(int $number, array $attributes = []) + * @method static Tag[]|Proxy[] all() + * @method static Tag[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static Tag[]|Proxy[] createSequence(array|callable $sequence) + * @method static Tag[]|Proxy[] findBy(array $attributes) + * @method static Tag[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static Tag[]|Proxy[] randomSet(int $number, array $attributes = []) */ final class TagFactory extends ModelFactory { diff --git a/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php b/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php index 494309e06..bae66ca93 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php @@ -17,12 +17,12 @@ * @method static Category|Proxy last(string $sortedField = 'id') * @method static Category|Proxy random(array $attributes = []) * @method static Category|Proxy randomOrCreate(array $attributes = []) - * @method static list|list all() - * @method static list|list createMany(int $number, array|callable $attributes = []) - * @method static list|list createSequence(array|callable $sequence) - * @method static list|list findBy(array $attributes) - * @method static list|list randomRange(int $min, int $max, array $attributes = []) - * @method static list|list randomSet(int $number, array $attributes = []) + * @method static Category[]|Proxy[] all() + * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static Category[]|Proxy[] createSequence(array|callable $sequence) + * @method static Category[]|Proxy[] findBy(array $attributes) + * @method static Category[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static Category[]|Proxy[] randomSet(int $number, array $attributes = []) * * @phpstan-method Proxy create(array|callable $attributes = []) * @phpstan-method static Proxy createOne(array $attributes = []) diff --git a/tests/Fixtures/Maker/expected/can-create-factory.php b/tests/Fixtures/Maker/expected/can-create-factory.php index 6e6c00f0b..2150e61a8 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory.php +++ b/tests/Fixtures/Maker/expected/can-create-factory.php @@ -17,12 +17,12 @@ * @method static Category|Proxy last(string $sortedField = 'id') * @method static Category|Proxy random(array $attributes = []) * @method static Category|Proxy randomOrCreate(array $attributes = []) - * @method static list|list all() - * @method static list|list createMany(int $number, array|callable $attributes = []) - * @method static list|list createSequence(array|callable $sequence) - * @method static list|list findBy(array $attributes) - * @method static list|list randomRange(int $min, int $max, array $attributes = []) - * @method static list|list randomSet(int $number, array $attributes = []) + * @method static Category[]|Proxy[] all() + * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static Category[]|Proxy[] createSequence(array|callable $sequence) + * @method static Category[]|Proxy[] findBy(array $attributes) + * @method static Category[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static Category[]|Proxy[] randomSet(int $number, array $attributes = []) */ final class CategoryFactory extends ModelFactory { From 96c4cbe4d8a55fe686b4b65e8952f99e25cfeb92 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 16 Nov 2022 15:23:32 +0100 Subject: [PATCH 15/30] minor(make:factory): Use `@see`/`@todo` annoations (#344) --- docs/index.rst | 20 +++++++++++++------ src/Bundle/Resources/skeleton/Factory.tpl.php | 17 ++++++++++++---- ...factory-for-odm-with-data-set-document.php | 17 ++++++++++++---- ...or-odm-with-data-set-embedded-document.php | 17 ++++++++++++---- ...eate-factory-in-test-dir-interactively.php | 17 ++++++++++++---- .../can-create-factory-in-test-dir.php | 17 ++++++++++++---- .../can-create-factory-interactively.php | 17 ++++++++++++---- ...reate-factory-with-phpstan-annotations.php | 17 ++++++++++++---- .../Maker/expected/can-create-factory.php | 17 ++++++++++++---- 9 files changed, 118 insertions(+), 38 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 324d1ed30..ca1f3b3f5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -164,23 +164,31 @@ This command will generate a ``PostFactory`` class that looks like this: */ final class PostFactory extends ModelFactory { + /** + * @see https://github.com/zenstruck/foundry#factories-as-services + * + * @todo inject services if required + */ public function __construct() { parent::__construct(); - - // TODO inject services if required (https://github.com/zenstruck/foundry#factories-as-services) } + /** + * @see https://github.com/zenstruck/foundry#model-factories + * + * @todo add your default values here + */ protected function getDefaults(): array { - return [ - // TODO add your default values here (https://github.com/zenstruck/foundry#model-factories) - ]; + return []; } + /** + * @see hhttps://github.com/zenstruck/foundry#initialization + */ protected function initialize(): self { - // see https://github.com/zenstruck/foundry#initialization return $this // ->afterInstantiate(function(Post $post) {}) ; diff --git a/src/Bundle/Resources/skeleton/Factory.tpl.php b/src/Bundle/Resources/skeleton/Factory.tpl.php index 92f30ec21..13c92cc5b 100644 --- a/src/Bundle/Resources/skeleton/Factory.tpl.php +++ b/src/Bundle/Resources/skeleton/Factory.tpl.php @@ -49,17 +49,24 @@ */ final class extends ModelFactory { + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ public function __construct() { parent::__construct(); - - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ protected function getDefaults(): array { return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) $type) { echo " '".$fieldname."' => ".$type."\n"; @@ -68,9 +75,11 @@ protected function getDefaults(): array ]; } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ protected function initialize(): self { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization return $this // ->afterInstantiate(function(getShortName() ?> $getShortName()) ?>): void {}) ; diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php index 2a297f342..6373950fc 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php @@ -26,17 +26,24 @@ */ final class PostFactory extends ModelFactory { + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ public function __construct() { parent::__construct(); - - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ protected function getDefaults(): array { return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) 'title' => self::faker()->text(), 'body' => self::faker()->text(), 'viewCount' => null, // TODO add INT ODM type manually @@ -46,9 +53,11 @@ protected function getDefaults(): array ]; } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ protected function initialize(): self { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization return $this // ->afterInstantiate(function(Post $post): void {}) ; diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php index 8688fbb06..f949a33da 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php @@ -26,17 +26,24 @@ */ final class CommentFactory extends ModelFactory { + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ public function __construct() { parent::__construct(); - - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ protected function getDefaults(): array { return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) 'user' => null, // TODO add ONE ODM type manually 'body' => self::faker()->text(), 'createdAt' => self::faker()->dateTime(), @@ -44,9 +51,11 @@ protected function getDefaults(): array ]; } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ protected function initialize(): self { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization return $this // ->afterInstantiate(function(Comment $comment): void {}) ; diff --git a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php index 8b0ab2435..b040578bc 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php @@ -26,24 +26,33 @@ */ final class TagFactory extends ModelFactory { + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ public function __construct() { parent::__construct(); - - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ protected function getDefaults(): array { return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) 'name' => self::faker()->text(255), ]; } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ protected function initialize(): self { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization return $this // ->afterInstantiate(function(Tag $tag): void {}) ; diff --git a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php index d5133fe13..ef7d0b726 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php @@ -26,24 +26,33 @@ */ final class CategoryFactory extends ModelFactory { + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ public function __construct() { parent::__construct(); - - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ protected function getDefaults(): array { return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) 'name' => self::faker()->text(255), ]; } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ protected function initialize(): self { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization return $this // ->afterInstantiate(function(Category $category): void {}) ; diff --git a/tests/Fixtures/Maker/expected/can-create-factory-interactively.php b/tests/Fixtures/Maker/expected/can-create-factory-interactively.php index f845c0172..0bb286e01 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-interactively.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-interactively.php @@ -26,24 +26,33 @@ */ final class TagFactory extends ModelFactory { + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ public function __construct() { parent::__construct(); - - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ protected function getDefaults(): array { return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) 'name' => self::faker()->text(255), ]; } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ protected function initialize(): self { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization return $this // ->afterInstantiate(function(Tag $tag): void {}) ; diff --git a/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php b/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php index bae66ca93..86ecb218b 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php +++ b/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php @@ -41,24 +41,33 @@ */ final class CategoryFactory extends ModelFactory { + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ public function __construct() { parent::__construct(); - - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ protected function getDefaults(): array { return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) 'name' => self::faker()->text(255), ]; } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ protected function initialize(): self { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization return $this // ->afterInstantiate(function(Category $category): void {}) ; diff --git a/tests/Fixtures/Maker/expected/can-create-factory.php b/tests/Fixtures/Maker/expected/can-create-factory.php index 2150e61a8..94591d2ce 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory.php +++ b/tests/Fixtures/Maker/expected/can-create-factory.php @@ -26,24 +26,33 @@ */ final class CategoryFactory extends ModelFactory { + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ public function __construct() { parent::__construct(); - - // TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ protected function getDefaults(): array { return [ - // TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) 'name' => self::faker()->text(255), ]; } + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ protected function initialize(): self { - // see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization return $this // ->afterInstantiate(function(Category $category): void {}) ; From 1a98fc48bbd8289c9fc0a8fc26b066b91986b300 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 16 Nov 2022 15:50:27 +0100 Subject: [PATCH 16/30] chore: Use composer 2.4 (#346) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index a44206f07..a800d2b4c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,6 @@ FROM php:8.0-cli -COPY --from=composer:2.3 /usr/bin/composer /usr/bin/composer +COPY --from=composer:2.4 /usr/bin/composer /usr/bin/composer COPY docker/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini ARG XDEBUG_HOST From 64786fc9ea83e70cc6e87390d57acdd877924557 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Wed, 16 Nov 2022 16:50:52 +0100 Subject: [PATCH 17/30] fix: typo in docs (#348) --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index ca1f3b3f5..364ad93fc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -185,7 +185,7 @@ This command will generate a ``PostFactory`` class that looks like this: } /** - * @see hhttps://github.com/zenstruck/foundry#initialization + * @see https://github.com/zenstruck/foundry#initialization */ protected function initialize(): self { From 18ea4fb182d0b4e5ffed0ecc9128a5f1460793a4 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Wed, 16 Nov 2022 20:39:27 +0100 Subject: [PATCH 18/30] feat(make:factory): create factory for not-persisted objects (#343) --- .php-cs-fixer.dist.php | 2 +- src/Bundle/Maker/MakeFactory.php | 138 ++++++++++++++---- src/Bundle/Resources/config/services.xml | 1 + src/Bundle/Resources/skeleton/Factory.tpl.php | 74 +++++----- ...-for-not-persisted-class-interactively.php | 63 ++++++++ ...create-factory-for-not-persisted-class.php | 66 +++++++++ ...rsisted-option-if-doctrine-not-enabled.php | 57 ++++++++ tests/Fixtures/Object/SomeObject.php | 20 +++ tests/Fixtures/Object/SomeOtherObject.php | 9 ++ .../Bundle/Maker/MakeFactoryTest.php | 69 ++++++++- 10 files changed, 435 insertions(+), 64 deletions(-) create mode 100644 tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class-interactively.php create mode 100644 tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class.php create mode 100644 tests/Fixtures/Maker/expected/it-auto-activate-not-persisted-option-if-doctrine-not-enabled.php create mode 100644 tests/Fixtures/Object/SomeObject.php create mode 100644 tests/Fixtures/Object/SomeOtherObject.php diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 12763c45f..61400e514 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -3,7 +3,7 @@ $finder = PhpCsFixer\Finder::create() ->in([__DIR__.'/src', __DIR__.'/tests']) ->notName('*.tpl.php') - ->exclude('Fixtures/Maker') + ->exclude('Fixtures/') ; $config = new PhpCsFixer\Config(); diff --git a/src/Bundle/Maker/MakeFactory.php b/src/Bundle/Maker/MakeFactory.php index 70dbb2a50..2ffa228c6 100644 --- a/src/Bundle/Maker/MakeFactory.php +++ b/src/Bundle/Maker/MakeFactory.php @@ -17,6 +17,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\HttpKernel\KernelInterface; use Zenstruck\Foundry\ModelFactory; /** @@ -24,7 +25,7 @@ */ final class MakeFactory extends AbstractMaker { - private const DEFAULTS = [ + private const DEFAULTS_FOR_PERSISTED = [ 'ARRAY' => '[],', 'ASCII_STRING' => 'self::faker()->text({length}),', 'BIGINT' => 'self::faker()->randomNumber(),', @@ -50,10 +51,20 @@ final class MakeFactory extends AbstractMaker 'TIME_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->datetime()),', ]; + private const DEFAULTS_FOR_NOT_PERSISTED = [ + 'array' => '[],', + 'string' => 'self::faker()->text(),', + 'int' => 'self::faker()->randomNumber(),', + 'float' => 'self::faker()->randomFloat(),', + 'bool' => 'self::faker()->boolean(),', + \DateTime::class => 'self::faker()->dateTime(),', + \DateTimeImmutable::class => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', + ]; + /** @var string[] */ - private array $entitiesWithFactories = []; + private array $entitiesWithFactories; - public function __construct(private ManagerRegistry $managerRegistry, \Traversable $factories, private string $projectDir) + public function __construct(private ManagerRegistry $managerRegistry, \Traversable $factories, private string $projectDir, private KernelInterface $kernel) { $this->entitiesWithFactories = \array_map( static fn(ModelFactory $factory): string => $factory::getEntityClass(), @@ -68,7 +79,7 @@ public static function getCommandName(): string public static function getCommandDescription(): string { - return 'Creates a Foundry model factory for a Doctrine entity class'; + return 'Creates a Foundry model factory for a Doctrine entity class or a regular object'; } public function configureDependencies(DependencyBuilder $dependencies): void @@ -80,18 +91,26 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->setDescription(self::getCommandDescription()) - ->addArgument('entity', InputArgument::OPTIONAL, 'Entity class to create a factory for') + ->addArgument('class', InputArgument::OPTIONAL, 'Entity, Document or class to create a factory for') ->addOption('namespace', null, InputOption::VALUE_REQUIRED, 'Customize the namespace for generated factories', 'Factory') ->addOption('test', null, InputOption::VALUE_NONE, 'Create in tests/ instead of src/') ->addOption('all-fields', null, InputOption::VALUE_NONE, 'Create defaults for all entity fields, not only required fields') + ->addOption('not-persisted', null, InputOption::VALUE_NONE, 'Create a factory for an object not managed by Doctrine') ; - $inputConfig->setArgumentAsNonInteractive('entity'); + $inputConfig->setArgumentAsNonInteractive('class'); } public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void { - if ($input->getArgument('entity')) { + if (!$this->doctrineEnabled() && !$input->getOption('not-persisted')) { + $io->text('// Note: Doctrine not enabled: auto-activating --not-persisted option.'); + $io->newLine(); + + $input->setOption('not-persisted', true); + } + + if ($input->getArgument('class')) { return; } @@ -105,16 +124,30 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma $io->newLine(); } - $argument = $command->getDefinition()->getArgument('entity'); - $entity = $io->choice($argument->getDescription(), \array_merge($this->entityChoices(), ['All'])); + if ($input->getOption('not-persisted')) { + $class = $io->ask( + 'Not persisted class to create a factory for', + validator: static function(string $class) { + if (!\class_exists($class)) { + throw new RuntimeCommandException("Given class \"{$class}\" does not exist."); + } + + return $class; + } + ); + } else { + $argument = $command->getDefinition()->getArgument('class'); + + $class = $io->choice($argument->getDescription(), \array_merge($this->entityChoices(), ['All'])); + } - $input->setArgument('entity', $entity); + $input->setArgument('class', $class); } public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void { - $entity = $input->getArgument('entity'); - $classes = 'All' === $entity ? $this->entityChoices() : [$entity]; + $class = $input->getArgument('class'); + $classes = 'All' === $class ? $this->entityChoices() : [$class]; foreach ($classes as $class) { $this->generateFactory($class, $input, $io, $generator); @@ -131,7 +164,7 @@ private function generateFactory(string $class, InputInterface $input, ConsoleSt } if (!\class_exists($class)) { - throw new RuntimeCommandException(\sprintf('Entity "%s" not found.', $input->getArgument('entity'))); + throw new RuntimeCommandException(\sprintf('Class "%s" not found.', $input->getArgument('class'))); } $namespace = $input->getOption('namespace'); @@ -148,24 +181,29 @@ private function generateFactory(string $class, InputInterface $input, ConsoleSt $namespace = 'Tests\\'.$namespace; } - $entity = new \ReflectionClass($class); - $factory = $generator->createClassNameDetails($entity->getShortName(), $namespace, 'Factory'); + $object = new \ReflectionClass($class); + $factory = $generator->createClassNameDetails($object->getShortName(), $namespace, 'Factory'); - $repository = new \ReflectionClass($this->managerRegistry->getRepository($entity->getName())); + if (!$input->getOption('not-persisted')) { + $repository = new \ReflectionClass($this->managerRegistry->getRepository($object->getName())); - if (0 !== \mb_strpos($repository->getName(), $generator->getRootNamespace())) { - // not using a custom repository - $repository = null; + if (0 !== \mb_strpos($repository->getName(), $generator->getRootNamespace())) { + // not using a custom repository + $repository = null; + } } $generator->generateClass( $factory->getFullName(), __DIR__.'/../Resources/skeleton/Factory.tpl.php', [ - 'entity' => $entity, - 'defaultProperties' => $this->defaultPropertiesFor($entity->getName(), $input->getOption('all-fields')), - 'repository' => $repository, + 'object' => $object, + 'defaultProperties' => $input->getOption('not-persisted') + ? $this->defaultPropertiesForNotPersistedObject($object->getName(), $input->getOption('all-fields')) + : $this->defaultPropertiesForPersistedObject($object->getName(), $input->getOption('all-fields')), + 'repository' => $repository ?? null, 'phpstanEnabled' => $this->phpstanEnabled(), + 'persisted' => !$input->getOption('not-persisted'), ] ); @@ -210,7 +248,7 @@ private function entityChoices(): array /** * @param class-string $class */ - private function defaultPropertiesFor(string $class, bool $allFields): iterable + private function defaultPropertiesForPersistedObject(string $class, bool $allFields): iterable { $em = $this->managerRegistry->getManagerForClass($class); @@ -234,16 +272,66 @@ private function defaultPropertiesFor(string $class, bool $allFields): iterable $value = "null, // TODO add {$type} {$dbType} type manually"; $length = $property['length'] ?? ''; - if (\array_key_exists($type, self::DEFAULTS)) { - $value = self::DEFAULTS[$type]; + if (\array_key_exists($type, self::DEFAULTS_FOR_PERSISTED)) { + $value = self::DEFAULTS_FOR_PERSISTED[$type]; } yield $property['fieldName'] => \str_replace('{length}', (string) $length, $value); } } + /** + * @param class-string $class + */ + private function defaultPropertiesForNotPersistedObject(string $class, bool $allFields): iterable + { + $object = new \ReflectionClass($class); + + foreach ($object->getProperties() as $property) { + // ignore identifiers and nullable fields + if (!$allFields && ($property->hasDefaultValue() || !$property->hasType() || $property->getType()?->allowsNull())) { + continue; + } + + $type = null; + $reflectionType = $property->getType(); + if ($reflectionType instanceof \ReflectionNamedType) { + $type = $reflectionType->getName(); + } + + $value = \sprintf('null, // TODO add %svalue manually', $type ? "{$type} " : ''); + + if (\array_key_exists($type ?? '', self::DEFAULTS_FOR_NOT_PERSISTED)) { + $value = self::DEFAULTS_FOR_NOT_PERSISTED[$type]; + } + + yield $property->getName() => $value; + } + } + private function phpstanEnabled(): bool { return \file_exists("{$this->projectDir}/vendor/phpstan/phpstan/phpstan"); } + + private function doctrineEnabled(): bool + { + try { + $this->kernel->getBundle('DoctrineBundle'); + + $ormEnabled = true; + } catch (\InvalidArgumentException) { + $ormEnabled = false; + } + + try { + $this->kernel->getBundle('DoctrineMongoDBBundle'); + + $odmEnabled = true; + } catch (\InvalidArgumentException) { + $odmEnabled = false; + } + + return $ormEnabled || $odmEnabled; + } } diff --git a/src/Bundle/Resources/config/services.xml b/src/Bundle/Resources/config/services.xml index 6cd4df8d4..1a26ac5c7 100644 --- a/src/Bundle/Resources/config/services.xml +++ b/src/Bundle/Resources/config/services.xml @@ -44,6 +44,7 @@ %kernel.project_dir% + diff --git a/src/Bundle/Resources/skeleton/Factory.tpl.php b/src/Bundle/Resources/skeleton/Factory.tpl.php index 13c92cc5b..fbcd45c24 100644 --- a/src/Bundle/Resources/skeleton/Factory.tpl.php +++ b/src/Bundle/Resources/skeleton/Factory.tpl.php @@ -2,49 +2,54 @@ namespace ; -use getName() ?>; -use getName() ?>; +use getName() ?>; +use getName() ?>; use Zenstruck\Foundry\RepositoryProxy; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; /** - * @extends ModelFactory<getShortName() ?>> + * @extends ModelFactory<getShortName() ?>> * - * @method getShortName() ?>|Proxy create(array|callable $attributes = []) - * @method static getShortName() ?>|Proxy createOne(array $attributes = []) - * @method static getShortName() ?>|Proxy find(object|array|mixed $criteria) - * @method static getShortName() ?>|Proxy findOrCreate(array $attributes) - * @method static getShortName() ?>|Proxy first(string $sortedField = 'id') - * @method static getShortName() ?>|Proxy last(string $sortedField = 'id') - * @method static getShortName() ?>|Proxy random(array $attributes = []) - * @method static getShortName() ?>|Proxy randomOrCreate(array $attributes = []) - * @method static getShortName() ?>[]|Proxy[] all() - * @method static getShortName() ?>[]|Proxy[] createMany(int $number, array|callable $attributes = []) - * @method static getShortName() ?>[]|Proxy[] createSequence(array|callable $sequence) - * @method static getShortName() ?>[]|Proxy[] findBy(array $attributes) - * @method static getShortName() ?>[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) - * @method static getShortName() ?>[]|Proxy[] randomSet(int $number, array $attributes = []) + * @method getShortName() ?>|Proxy create(array|callable $attributes = []) + * @method static getShortName() ?>|Proxy createOne(array $attributes = []) + * @method static getShortName() ?>|Proxy find(object|array|mixed $criteria) + * @method static getShortName() ?>|Proxy findOrCreate(array $attributes) + * @method static getShortName() ?>|Proxy first(string $sortedField = 'id') + * @method static getShortName() ?>|Proxy last(string $sortedField = 'id') + * @method static getShortName() ?>|Proxy random(array $attributes = []) + * @method static getShortName() ?>|Proxy randomOrCreate(array $attributes = []) + * @method static getShortName() ?>[]|Proxy[] all() + + * @method static getShortName() ?>[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static getShortName() ?>[]|Proxy[] createSequence(array|callable $sequence) + * @method static getShortName() ?>[]|Proxy[] findBy(array $attributes) + * @method static getShortName() ?>[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static getShortName() ?>[]|Proxy[] randomSet(int $number, array $attributes = []) * @method static getShortName() ?>|RepositoryProxy repository() + * - * @phpstan-method Proxy<getShortName() ?>> create(array|callable $attributes = []) - * @phpstan-method static Proxy<getShortName() ?>> createOne(array $attributes = []) - * @phpstan-method static Proxy<getShortName() ?>> find(object|array|mixed $criteria) - * @phpstan-method static Proxy<getShortName() ?>> findOrCreate(array $attributes) - * @phpstan-method static Proxy<getShortName() ?>> first(string $sortedField = 'id') - * @phpstan-method static Proxy<getShortName() ?>> last(string $sortedField = 'id') - * @phpstan-method static Proxy<getShortName() ?>> random(array $attributes = []) - * @phpstan-method static Proxy<getShortName() ?>> randomOrCreate(array $attributes = []) - * @phpstan-method static listgetShortName() ?>>> all() - * @phpstan-method static listgetShortName() ?>>> createMany(int $number, array|callable $attributes = []) - * @phpstan-method static listgetShortName() ?>>> createSequence(array|callable $sequence) - * @phpstan-method static listgetShortName() ?>>> findBy(array $attributes) - * @phpstan-method static listgetShortName() ?>>> randomRange(int $min, int $max, array $attributes = []) - * @phpstan-method static listgetShortName() ?>>> randomSet(int $number, array $attributes = []) + * @phpstan-method Proxy<getShortName() ?>> create(array|callable $attributes = []) + * @phpstan-method static Proxy<getShortName() ?>> createOne(array $attributes = []) + * @phpstan-method static Proxy<getShortName() ?>> find(object|array|mixed $criteria) + * @phpstan-method static Proxy<getShortName() ?>> findOrCreate(array $attributes) + * @phpstan-method static Proxy<getShortName() ?>> first(string $sortedField = 'id') + * @phpstan-method static Proxy<getShortName() ?>> last(string $sortedField = 'id') + * @phpstan-method static Proxy<getShortName() ?>> random(array $attributes = []) + * @phpstan-method static Proxy<getShortName() ?>> randomOrCreate(array $attributes = []) + * @phpstan-method static listgetShortName() ?>>> all() + + * @phpstan-method static listgetShortName() ?>>> createMany(int $number, array|callable $attributes = []) + * @phpstan-method static listgetShortName() ?>>> createSequence(array|callable $sequence) + + * @phpstan-method static listgetShortName() ?>>> findBy(array $attributes) + * @phpstan-method static listgetShortName() ?>>> randomRange(int $min, int $max, array $attributes = []) + * @phpstan-method static listgetShortName() ?>>> randomSet(int $number, array $attributes = []) * @phpstan-method static RepositoryProxy<getShortName() ?>> repository() + */ final class extends ModelFactory @@ -81,12 +86,15 @@ protected function getDefaults(): array protected function initialize(): self { return $this - // ->afterInstantiate(function(getShortName() ?> $getShortName()) ?>): void {}) + + ->withoutPersisting() + + // ->afterInstantiate(function(getShortName() ?> $getShortName()) ?>): void {}) ; } protected static function getClass(): string { - return getShortName() ?>::class; + return getShortName() ?>::class; } } diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class-interactively.php b/tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class-interactively.php new file mode 100644 index 000000000..2282a475e --- /dev/null +++ b/tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class-interactively.php @@ -0,0 +1,63 @@ + + * + * @method SomeObject|Proxy create(array|callable $attributes = []) + * @method static SomeObject|Proxy createOne(array $attributes = []) + * @method static SomeObject[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static SomeObject[]|Proxy[] createSequence(array|callable $sequence) + */ +final class SomeObjectFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'stringMandatory' => self::faker()->text(), + 'intMandatory' => self::faker()->randomNumber(), + 'floatMandatory' => self::faker()->randomFloat(), + 'arrayMandatory' => [], + 'dateTimeMandatory' => self::faker()->dateTime(), + 'dateTimeImmutableMandatory' => \DateTimeImmutable::createFromMutable(self::faker()->dateTime()), + 'someOtherObjectMandatory' => null, // TODO add Zenstruck\Foundry\Tests\Fixtures\Object\SomeOtherObject value manually + 'someMandatoryPropertyWithUnionType' => null, // TODO add value manually + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + ->withoutPersisting() + // ->afterInstantiate(function(SomeObject $someObject): void {}) + ; + } + + protected static function getClass(): string + { + return SomeObject::class; + } +} diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class.php b/tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class.php new file mode 100644 index 000000000..48b8c17cf --- /dev/null +++ b/tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class.php @@ -0,0 +1,66 @@ + + * + * @method SomeObject|Proxy create(array|callable $attributes = []) + * @method static SomeObject|Proxy createOne(array $attributes = []) + * @method static SomeObject[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static SomeObject[]|Proxy[] createSequence(array|callable $sequence) + */ +final class SomeObjectFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'propertyWithoutType' => null, // TODO add value manually + 'stringMandatory' => self::faker()->text(), + 'stringNullable' => self::faker()->text(), + 'stringWithDefault' => self::faker()->text(), + 'intMandatory' => self::faker()->randomNumber(), + 'floatMandatory' => self::faker()->randomFloat(), + 'arrayMandatory' => [], + 'dateTimeMandatory' => self::faker()->dateTime(), + 'dateTimeImmutableMandatory' => \DateTimeImmutable::createFromMutable(self::faker()->dateTime()), + 'someOtherObjectMandatory' => null, // TODO add Zenstruck\Foundry\Tests\Fixtures\Object\SomeOtherObject value manually + 'someMandatoryPropertyWithUnionType' => null, // TODO add value manually + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + ->withoutPersisting() + // ->afterInstantiate(function(SomeObject $someObject): void {}) + ; + } + + protected static function getClass(): string + { + return SomeObject::class; + } +} diff --git a/tests/Fixtures/Maker/expected/it-auto-activate-not-persisted-option-if-doctrine-not-enabled.php b/tests/Fixtures/Maker/expected/it-auto-activate-not-persisted-option-if-doctrine-not-enabled.php new file mode 100644 index 000000000..421380d0b --- /dev/null +++ b/tests/Fixtures/Maker/expected/it-auto-activate-not-persisted-option-if-doctrine-not-enabled.php @@ -0,0 +1,57 @@ + + * + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) + * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static Category[]|Proxy[] createSequence(array|callable $sequence) + */ +final class CategoryFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'posts' => null, // TODO add Doctrine\Common\Collections\Collection value manually + 'secondaryPosts' => null, // TODO add Doctrine\Common\Collections\Collection value manually + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + ->withoutPersisting() + // ->afterInstantiate(function(Category $category): void {}) + ; + } + + protected static function getClass(): string + { + return Category::class; + } +} diff --git a/tests/Fixtures/Object/SomeObject.php b/tests/Fixtures/Object/SomeObject.php new file mode 100644 index 000000000..539d4cfc7 --- /dev/null +++ b/tests/Fixtures/Object/SomeObject.php @@ -0,0 +1,20 @@ + @@ -41,7 +43,7 @@ public function can_create_factory(): void $this->assertFileDoesNotExist(self::tempFile('src/Factory/CategoryFactory.php')); - $tester->execute(['entity' => Category::class]); + $tester->execute(['class' => Category::class]); $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/CategoryFactory.php')); } @@ -83,7 +85,7 @@ public function can_create_factory_in_test_dir(): void $this->assertFileDoesNotExist(self::tempFile('tests/Factory/CategoryFactory.php')); - $tester->execute(['entity' => Category::class, '--test' => true]); + $tester->execute(['class' => Category::class, '--test' => true]); $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('tests/Factory/CategoryFactory.php')); } @@ -128,7 +130,7 @@ public function can_create_factory_with_phpstan_annotations(): void $this->assertFileDoesNotExist(self::tempFile('src/Factory/CategoryFactory.php')); - $tester->execute(['entity' => Category::class]); + $tester->execute(['class' => Category::class]); $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/CategoryFactory.php')); } @@ -143,9 +145,9 @@ public function invalid_entity_throws_exception(): void $this->assertFileDoesNotExist(self::tempFile('src/Factory/InvalidFactory.php')); try { - $tester->execute(['entity' => 'Invalid']); + $tester->execute(['class' => 'Invalid']); } catch (RuntimeCommandException $e) { - $this->assertSame('Entity "Invalid" not found.', $e->getMessage()); + $this->assertSame('Class "Invalid" not found.', $e->getMessage()); $this->assertFileDoesNotExist(self::tempFile('src/Factory/InvalidFactory.php')); return; @@ -154,6 +156,40 @@ public function invalid_entity_throws_exception(): void $this->fail('Exception not thrown.'); } + /** + * @test + */ + public function can_create_factory_for_not_persisted_class(): void + { + $tester = new CommandTester((new Application(self::bootKernel()))->find('make:factory')); + + $this->assertFileDoesNotExist(self::tempFile('src/Factory/SomeObjectFactory.php')); + + $tester->execute(['class' => SomeObject::class, '--not-persisted' => true, '--all-fields' => true]); + + $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/SomeObjectFactory.php')); + } + + /** + * @test + */ + public function can_create_factory_for_not_persisted_class_interactively(): void + { + $tester = new CommandTester((new Application(self::bootKernel()))->find('make:factory')); + + $this->assertFileDoesNotExist(self::tempFile('src/Factory/SomeObjectFactory.php')); + + $tester->setInputs(['Foo', SomeObject::class]); // "Foo" will generate a validation error + $tester->execute(['--not-persisted' => true]); + + $output = $tester->getDisplay(); + + $this->assertStringContainsString('Not persisted class to create a factory for:', $output); + $this->assertStringContainsString('[ERROR] Given class "Foo" does not exist', $output); + + $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/SomeObjectFactory.php')); + } + /** * @test */ @@ -286,4 +322,27 @@ public function documentProvider(): iterable yield 'document' => [Post::class, 'PostFactory']; yield 'embedded document' => [Comment::class, 'CommentFactory']; } + + /** + * @test + */ + public function it_auto_activate_not_persisted_option_if_doctrine_not_enabled(): void + { + if (\getenv('USE_DAMA_DOCTRINE_TEST_BUNDLE')) { + self::markTestSkipped('dama/doctrine-test-bundle should not be enabled.'); + } + + $kernel = Kernel::create(enableDoctrine: false); + $kernel->boot(); + + $tester = new CommandTester((new Application($kernel))->find('make:factory')); + $this->assertFileDoesNotExist(self::tempFile('src/Factory/CategoryFactory.php')); + + $tester->execute(['class' => Category::class]); + + $output = $tester->getDisplay(); + $this->assertStringContainsString('Note: Doctrine not enabled: auto-activating --not-persisted option.', $output); + + $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/CategoryFactory.php')); + } } From 8a055b08e8a6d56fbb5459e798fb118e822a382a Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Wed, 16 Nov 2022 22:52:43 +0100 Subject: [PATCH 19/30] chore: fix docker cache (#350) --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6dd06582..5ab78263b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -221,12 +221,12 @@ jobs: uses: docker/setup-buildx-action@v2 - name: Create cache directory - run: mkdir -p var/cache-docker + run: mkdir -p cache-docker - id: cache-docker uses: actions/cache@v3 with: - path: var/cache-docker + path: cache-docker key: ${{ hashFiles('docker/*') }} - name: Docker build @@ -235,14 +235,14 @@ jobs: with: context: . file: ./docker/Dockerfile - outputs: type=docker,dest=var/cache-docker/php.tar + outputs: type=docker,dest=cache-docker/php.tar tags: | foundry_php:ci push: false - name: Load docker context run: | - docker load < var/cache-docker/php.tar + docker load < cache-docker/php.tar - name: Run test suite with docker run: | From 3cc95a533a5f53abbe8bf1d438a7bfd52346d61c Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Wed, 16 Nov 2022 22:53:08 +0100 Subject: [PATCH 20/30] minor: remove php 7.4 related tests (#349) --- tests/Functional/ORMProxyTest.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/Functional/ORMProxyTest.php b/tests/Functional/ORMProxyTest.php index c126c86dc..74ef25505 100644 --- a/tests/Functional/ORMProxyTest.php +++ b/tests/Functional/ORMProxyTest.php @@ -23,7 +23,6 @@ protected function setUp(): void /** * @test - * @requires PHP >= 7.4 */ public function cannot_convert_to_string_if_underlying_object_cant(): void { @@ -33,15 +32,6 @@ public function cannot_convert_to_string_if_underlying_object_cant(): void (string) CategoryFactory::createOne(); } - /** - * @test - * @requires PHP < 7.4 - */ - public function on_php_versions_less_than_7_4_if_underlying_object_is_missing_to_string_proxy_to_string_returns_note(): void - { - $this->assertSame('(no __toString)', (string) CategoryFactory::createOne()); - } - /** * @test */ From b89bcff2503df8a5f6dfbdf6a9640463b00f9f95 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Thu, 17 Nov 2022 10:20:15 +0100 Subject: [PATCH 21/30] minor(make:factory): misc enhancements of maker (#345) --- src/Bundle/Maker/MakeFactory.php | 20 ++++--- src/Bundle/Resources/skeleton/Factory.tpl.php | 54 +++++++++++-------- ...ate-factory.php => can_create_factory.php} | 18 +++---- ...reate_factory_for_not_persisted_class.php} | 22 ++++---- ...for_not_persisted_class_interactively.php} | 16 +++--- ...actory_for_odm_with_data_set_document.php} | 24 ++++----- ...r_odm_with_data_set_embedded_document.php} | 22 ++++---- ...php => can_create_factory_in_test_dir.php} | 18 +++---- ...ate_factory_in_test_dir_interactively.php} | 18 +++---- ...p => can_create_factory_interactively.php} | 18 +++---- ...h_auto_activated_not_persisted_option.php} | 6 +-- ...eate_factory_with_phpstan_annotations.php} | 34 ++++++------ .../Bundle/Maker/MakeFactoryTest.php | 2 +- .../Functional/Bundle/Maker/MakerTestCase.php | 2 +- 14 files changed, 146 insertions(+), 128 deletions(-) rename tests/Fixtures/Maker/expected/{can-create-factory.php => can_create_factory.php} (73%) rename tests/Fixtures/Maker/expected/{can-create-factory-for-not-persisted-class.php => can_create_factory_for_not_persisted_class.php} (85%) rename tests/Fixtures/Maker/expected/{can-create-factory-for-not-persisted-class-interactively.php => can_create_factory_for_not_persisted_class_interactively.php} (89%) rename tests/Fixtures/Maker/expected/{can-create-factory-for-odm-with-data-set-document.php => can_create_factory_for_odm_with_data_set_document.php} (77%) rename tests/Fixtures/Maker/expected/{can-create-factory-for-odm-with-data-set-embedded-document.php => can_create_factory_for_odm_with_data_set_embedded_document.php} (75%) rename tests/Fixtures/Maker/expected/{can-create-factory-in-test-dir.php => can_create_factory_in_test_dir.php} (73%) rename tests/Fixtures/Maker/expected/{can-create-factory-in-test-dir-interactively.php => can_create_factory_in_test_dir_interactively.php} (74%) rename tests/Fixtures/Maker/expected/{can-create-factory-interactively.php => can_create_factory_interactively.php} (74%) rename tests/Fixtures/Maker/expected/{it-auto-activate-not-persisted-option-if-doctrine-not-enabled.php => can_create_factory_with_auto_activated_not_persisted_option.php} (91%) rename tests/Fixtures/Maker/expected/{can-create-factory-with-phpstan-annotations.php => can_create_factory_with_phpstan_annotations.php} (63%) diff --git a/src/Bundle/Maker/MakeFactory.php b/src/Bundle/Maker/MakeFactory.php index 2ffa228c6..7af1d2ba3 100644 --- a/src/Bundle/Maker/MakeFactory.php +++ b/src/Bundle/Maker/MakeFactory.php @@ -53,7 +53,7 @@ final class MakeFactory extends AbstractMaker private const DEFAULTS_FOR_NOT_PERSISTED = [ 'array' => '[],', - 'string' => 'self::faker()->text(),', + 'string' => 'self::faker()->sentence(),', 'int' => 'self::faker()->randomNumber(),', 'float' => 'self::faker()->randomFloat(),', 'bool' => 'self::faker()->boolean(),', @@ -193,14 +193,18 @@ private function generateFactory(string $class, InputInterface $input, ConsoleSt } } + $defaultValues = $input->getOption('not-persisted') + ? $this->defaultPropertiesForNotPersistedObject($object->getName(), $input->getOption('all-fields')) + : $this->defaultPropertiesForPersistedObject($object->getName(), $input->getOption('all-fields')); + $defaultValues = \iterator_to_array($defaultValues, true); + \ksort($defaultValues); + $generator->generateClass( $factory->getFullName(), __DIR__.'/../Resources/skeleton/Factory.tpl.php', [ 'object' => $object, - 'defaultProperties' => $input->getOption('not-persisted') - ? $this->defaultPropertiesForNotPersistedObject($object->getName(), $input->getOption('all-fields')) - : $this->defaultPropertiesForPersistedObject($object->getName(), $input->getOption('all-fields')), + 'defaultProperties' => $defaultValues, 'repository' => $repository ?? null, 'phpstanEnabled' => $this->phpstanEnabled(), 'persisted' => !$input->getOption('not-persisted'), @@ -247,13 +251,15 @@ private function entityChoices(): array /** * @param class-string $class + * + * @return \Generator */ private function defaultPropertiesForPersistedObject(string $class, bool $allFields): iterable { $em = $this->managerRegistry->getManagerForClass($class); if (!$em instanceof ObjectManager) { - return []; + return; } /** @var ORMClassMetadata|ODMClassMetadata $metadata */ @@ -282,8 +288,10 @@ private function defaultPropertiesForPersistedObject(string $class, bool $allFie /** * @param class-string $class + * + * @return \Generator */ - private function defaultPropertiesForNotPersistedObject(string $class, bool $allFields): iterable + private function defaultPropertiesForNotPersistedObject(string $class, bool $allFields): \Generator { $object = new \ReflectionClass($class); diff --git a/src/Bundle/Resources/skeleton/Factory.tpl.php b/src/Bundle/Resources/skeleton/Factory.tpl.php index fbcd45c24..a6143e727 100644 --- a/src/Bundle/Resources/skeleton/Factory.tpl.php +++ b/src/Bundle/Resources/skeleton/Factory.tpl.php @@ -2,24 +2,34 @@ namespace ; -use getName() ?>; -use getName() ?>; -use Zenstruck\Foundry\RepositoryProxy; - -use Zenstruck\Foundry\ModelFactory; -use Zenstruck\Foundry\Proxy; +getName()};", +); + +if ($persisted && $repository) { + $uses[] = "use {$repository->getName()};"; +} + +sort($uses); + +foreach ($uses as $use) { + echo "$use\n"; +} +?> /** * @extends ModelFactory<getShortName() ?>> * - * @method getShortName() ?>|Proxy create(array|callable $attributes = []) - * @method static getShortName() ?>|Proxy createOne(array $attributes = []) - * @method static getShortName() ?>|Proxy find(object|array|mixed $criteria) - * @method static getShortName() ?>|Proxy findOrCreate(array $attributes) - * @method static getShortName() ?>|Proxy first(string $sortedField = 'id') - * @method static getShortName() ?>|Proxy last(string $sortedField = 'id') - * @method static getShortName() ?>|Proxy random(array $attributes = []) - * @method static getShortName() ?>|Proxy randomOrCreate(array $attributes = []) + * @method getShortName() ?>|Proxy create(array|callable $attributes = []) + * @method static getShortName() ?>|Proxy createOne(array $attributes = []) + * @method static getShortName() ?>|Proxy find(object|array|mixed $criteria) + * @method static getShortName() ?>|Proxy findOrCreate(array $attributes) + * @method static getShortName() ?>|Proxy first(string $sortedField = 'id') + * @method static getShortName() ?>|Proxy last(string $sortedField = 'id') + * @method static getShortName() ?>|Proxy random(array $attributes = []) + * @method static getShortName() ?>|Proxy randomOrCreate(array $attributes = []) * @method static getShortName() ?>[]|Proxy[] all() * @method static getShortName() ?>[]|Proxy[] createMany(int $number, array|callable $attributes = []) @@ -31,14 +41,14 @@ * - * @phpstan-method Proxy<getShortName() ?>> create(array|callable $attributes = []) - * @phpstan-method static Proxy<getShortName() ?>> createOne(array $attributes = []) - * @phpstan-method static Proxy<getShortName() ?>> find(object|array|mixed $criteria) - * @phpstan-method static Proxy<getShortName() ?>> findOrCreate(array $attributes) - * @phpstan-method static Proxy<getShortName() ?>> first(string $sortedField = 'id') - * @phpstan-method static Proxy<getShortName() ?>> last(string $sortedField = 'id') - * @phpstan-method static Proxy<getShortName() ?>> random(array $attributes = []) - * @phpstan-method static Proxy<getShortName() ?>> randomOrCreate(array $attributes = []) + * @phpstan-method Proxy<getShortName() ?>> create(array|callable $attributes = []) + * @phpstan-method static Proxy<getShortName() ?>> createOne(array $attributes = []) + * @phpstan-method static Proxy<getShortName() ?>> find(object|array|mixed $criteria) + * @phpstan-method static Proxy<getShortName() ?>> findOrCreate(array $attributes) + * @phpstan-method static Proxy<getShortName() ?>> first(string $sortedField = 'id') + * @phpstan-method static Proxy<getShortName() ?>> last(string $sortedField = 'id') + * @phpstan-method static Proxy<getShortName() ?>> random(array $attributes = []) + * @phpstan-method static Proxy<getShortName() ?>> randomOrCreate(array $attributes = []) * @phpstan-method static listgetShortName() ?>>> all() * @phpstan-method static listgetShortName() ?>>> createMany(int $number, array|callable $attributes = []) diff --git a/tests/Fixtures/Maker/expected/can-create-factory.php b/tests/Fixtures/Maker/expected/can_create_factory.php similarity index 73% rename from tests/Fixtures/Maker/expected/can-create-factory.php rename to tests/Fixtures/Maker/expected/can_create_factory.php index 94591d2ce..af79def5e 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory.php +++ b/tests/Fixtures/Maker/expected/can_create_factory.php @@ -2,21 +2,21 @@ namespace App\Factory; -use Zenstruck\Foundry\Tests\Fixtures\Entity\Category; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; +use Zenstruck\Foundry\Tests\Fixtures\Entity\Category; /** * @extends ModelFactory * - * @method Category|Proxy create(array|callable $attributes = []) - * @method static Category|Proxy createOne(array $attributes = []) - * @method static Category|Proxy find(object|array|mixed $criteria) - * @method static Category|Proxy findOrCreate(array $attributes) - * @method static Category|Proxy first(string $sortedField = 'id') - * @method static Category|Proxy last(string $sortedField = 'id') - * @method static Category|Proxy random(array $attributes = []) - * @method static Category|Proxy randomOrCreate(array $attributes = []) + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) + * @method static Category|Proxy find(object|array|mixed $criteria) + * @method static Category|Proxy findOrCreate(array $attributes) + * @method static Category|Proxy first(string $sortedField = 'id') + * @method static Category|Proxy last(string $sortedField = 'id') + * @method static Category|Proxy random(array $attributes = []) + * @method static Category|Proxy randomOrCreate(array $attributes = []) * @method static Category[]|Proxy[] all() * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Category[]|Proxy[] createSequence(array|callable $sequence) diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class.php b/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class.php similarity index 85% rename from tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class.php rename to tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class.php index 48b8c17cf..58effd4d8 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class.php @@ -2,15 +2,15 @@ namespace App\Factory; -use Zenstruck\Foundry\Tests\Fixtures\Object\SomeObject; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; +use Zenstruck\Foundry\Tests\Fixtures\Object\SomeObject; /** * @extends ModelFactory * - * @method SomeObject|Proxy create(array|callable $attributes = []) - * @method static SomeObject|Proxy createOne(array $attributes = []) + * @method SomeObject|Proxy create(array|callable $attributes = []) + * @method static SomeObject|Proxy createOne(array $attributes = []) * @method static SomeObject[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static SomeObject[]|Proxy[] createSequence(array|callable $sequence) */ @@ -34,17 +34,17 @@ public function __construct() protected function getDefaults(): array { return [ - 'propertyWithoutType' => null, // TODO add value manually - 'stringMandatory' => self::faker()->text(), - 'stringNullable' => self::faker()->text(), - 'stringWithDefault' => self::faker()->text(), - 'intMandatory' => self::faker()->randomNumber(), - 'floatMandatory' => self::faker()->randomFloat(), 'arrayMandatory' => [], - 'dateTimeMandatory' => self::faker()->dateTime(), 'dateTimeImmutableMandatory' => \DateTimeImmutable::createFromMutable(self::faker()->dateTime()), - 'someOtherObjectMandatory' => null, // TODO add Zenstruck\Foundry\Tests\Fixtures\Object\SomeOtherObject value manually + 'dateTimeMandatory' => self::faker()->dateTime(), + 'floatMandatory' => self::faker()->randomFloat(), + 'intMandatory' => self::faker()->randomNumber(), + 'propertyWithoutType' => null, // TODO add value manually 'someMandatoryPropertyWithUnionType' => null, // TODO add value manually + 'someOtherObjectMandatory' => null, // TODO add Zenstruck\Foundry\Tests\Fixtures\Object\SomeOtherObject value manually + 'stringMandatory' => self::faker()->sentence(), + 'stringNullable' => self::faker()->sentence(), + 'stringWithDefault' => self::faker()->sentence(), ]; } diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class-interactively.php b/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class_interactively.php similarity index 89% rename from tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class-interactively.php rename to tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class_interactively.php index 2282a475e..fba13b481 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-for-not-persisted-class-interactively.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class_interactively.php @@ -2,15 +2,15 @@ namespace App\Factory; -use Zenstruck\Foundry\Tests\Fixtures\Object\SomeObject; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; +use Zenstruck\Foundry\Tests\Fixtures\Object\SomeObject; /** * @extends ModelFactory * - * @method SomeObject|Proxy create(array|callable $attributes = []) - * @method static SomeObject|Proxy createOne(array $attributes = []) + * @method SomeObject|Proxy create(array|callable $attributes = []) + * @method static SomeObject|Proxy createOne(array $attributes = []) * @method static SomeObject[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static SomeObject[]|Proxy[] createSequence(array|callable $sequence) */ @@ -34,14 +34,14 @@ public function __construct() protected function getDefaults(): array { return [ - 'stringMandatory' => self::faker()->text(), - 'intMandatory' => self::faker()->randomNumber(), - 'floatMandatory' => self::faker()->randomFloat(), 'arrayMandatory' => [], - 'dateTimeMandatory' => self::faker()->dateTime(), 'dateTimeImmutableMandatory' => \DateTimeImmutable::createFromMutable(self::faker()->dateTime()), - 'someOtherObjectMandatory' => null, // TODO add Zenstruck\Foundry\Tests\Fixtures\Object\SomeOtherObject value manually + 'dateTimeMandatory' => self::faker()->dateTime(), + 'floatMandatory' => self::faker()->randomFloat(), + 'intMandatory' => self::faker()->randomNumber(), 'someMandatoryPropertyWithUnionType' => null, // TODO add value manually + 'someOtherObjectMandatory' => null, // TODO add Zenstruck\Foundry\Tests\Fixtures\Object\SomeOtherObject value manually + 'stringMandatory' => self::faker()->sentence(), ]; } diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php b/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_document.php similarity index 77% rename from tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php rename to tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_document.php index 6373950fc..041a04bf5 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-document.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_document.php @@ -2,21 +2,21 @@ namespace App\Factory; -use Zenstruck\Foundry\Tests\Fixtures\Document\Post; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; +use Zenstruck\Foundry\Tests\Fixtures\Document\Post; /** * @extends ModelFactory * - * @method Post|Proxy create(array|callable $attributes = []) - * @method static Post|Proxy createOne(array $attributes = []) - * @method static Post|Proxy find(object|array|mixed $criteria) - * @method static Post|Proxy findOrCreate(array $attributes) - * @method static Post|Proxy first(string $sortedField = 'id') - * @method static Post|Proxy last(string $sortedField = 'id') - * @method static Post|Proxy random(array $attributes = []) - * @method static Post|Proxy randomOrCreate(array $attributes = []) + * @method Post|Proxy create(array|callable $attributes = []) + * @method static Post|Proxy createOne(array $attributes = []) + * @method static Post|Proxy find(object|array|mixed $criteria) + * @method static Post|Proxy findOrCreate(array $attributes) + * @method static Post|Proxy first(string $sortedField = 'id') + * @method static Post|Proxy last(string $sortedField = 'id') + * @method static Post|Proxy random(array $attributes = []) + * @method static Post|Proxy randomOrCreate(array $attributes = []) * @method static Post[]|Proxy[] all() * @method static Post[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Post[]|Proxy[] createSequence(array|callable $sequence) @@ -44,12 +44,12 @@ public function __construct() protected function getDefaults(): array { return [ - 'title' => self::faker()->text(), 'body' => self::faker()->text(), - 'viewCount' => null, // TODO add INT ODM type manually - 'createdAt' => self::faker()->dateTime(), 'comments' => null, // TODO add MANY ODM type manually + 'createdAt' => self::faker()->dateTime(), + 'title' => self::faker()->text(), 'user' => null, // TODO add ONE ODM type manually + 'viewCount' => null, // TODO add INT ODM type manually ]; } diff --git a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php b/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_embedded_document.php similarity index 75% rename from tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php rename to tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_embedded_document.php index f949a33da..3417058f6 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-for-odm-with-data-set-embedded-document.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_embedded_document.php @@ -2,21 +2,21 @@ namespace App\Factory; -use Zenstruck\Foundry\Tests\Fixtures\Document\Comment; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; +use Zenstruck\Foundry\Tests\Fixtures\Document\Comment; /** * @extends ModelFactory * - * @method Comment|Proxy create(array|callable $attributes = []) - * @method static Comment|Proxy createOne(array $attributes = []) - * @method static Comment|Proxy find(object|array|mixed $criteria) - * @method static Comment|Proxy findOrCreate(array $attributes) - * @method static Comment|Proxy first(string $sortedField = 'id') - * @method static Comment|Proxy last(string $sortedField = 'id') - * @method static Comment|Proxy random(array $attributes = []) - * @method static Comment|Proxy randomOrCreate(array $attributes = []) + * @method Comment|Proxy create(array|callable $attributes = []) + * @method static Comment|Proxy createOne(array $attributes = []) + * @method static Comment|Proxy find(object|array|mixed $criteria) + * @method static Comment|Proxy findOrCreate(array $attributes) + * @method static Comment|Proxy first(string $sortedField = 'id') + * @method static Comment|Proxy last(string $sortedField = 'id') + * @method static Comment|Proxy random(array $attributes = []) + * @method static Comment|Proxy randomOrCreate(array $attributes = []) * @method static Comment[]|Proxy[] all() * @method static Comment[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Comment[]|Proxy[] createSequence(array|callable $sequence) @@ -44,10 +44,10 @@ public function __construct() protected function getDefaults(): array { return [ - 'user' => null, // TODO add ONE ODM type manually + 'approved' => self::faker()->boolean(), 'body' => self::faker()->text(), 'createdAt' => self::faker()->dateTime(), - 'approved' => self::faker()->boolean(), + 'user' => null, // TODO add ONE ODM type manually ]; } diff --git a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php b/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir.php similarity index 73% rename from tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php rename to tests/Fixtures/Maker/expected/can_create_factory_in_test_dir.php index ef7d0b726..17c8d8b75 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir.php @@ -2,21 +2,21 @@ namespace App\Tests\Factory; -use Zenstruck\Foundry\Tests\Fixtures\Entity\Category; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; +use Zenstruck\Foundry\Tests\Fixtures\Entity\Category; /** * @extends ModelFactory * - * @method Category|Proxy create(array|callable $attributes = []) - * @method static Category|Proxy createOne(array $attributes = []) - * @method static Category|Proxy find(object|array|mixed $criteria) - * @method static Category|Proxy findOrCreate(array $attributes) - * @method static Category|Proxy first(string $sortedField = 'id') - * @method static Category|Proxy last(string $sortedField = 'id') - * @method static Category|Proxy random(array $attributes = []) - * @method static Category|Proxy randomOrCreate(array $attributes = []) + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) + * @method static Category|Proxy find(object|array|mixed $criteria) + * @method static Category|Proxy findOrCreate(array $attributes) + * @method static Category|Proxy first(string $sortedField = 'id') + * @method static Category|Proxy last(string $sortedField = 'id') + * @method static Category|Proxy random(array $attributes = []) + * @method static Category|Proxy randomOrCreate(array $attributes = []) * @method static Category[]|Proxy[] all() * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Category[]|Proxy[] createSequence(array|callable $sequence) diff --git a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php b/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir_interactively.php similarity index 74% rename from tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php rename to tests/Fixtures/Maker/expected/can_create_factory_in_test_dir_interactively.php index b040578bc..baf3d01d2 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-in-test-dir-interactively.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir_interactively.php @@ -2,21 +2,21 @@ namespace App\Tests\Factory; -use Zenstruck\Foundry\Tests\Fixtures\Entity\Tag; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; +use Zenstruck\Foundry\Tests\Fixtures\Entity\Tag; /** * @extends ModelFactory * - * @method Tag|Proxy create(array|callable $attributes = []) - * @method static Tag|Proxy createOne(array $attributes = []) - * @method static Tag|Proxy find(object|array|mixed $criteria) - * @method static Tag|Proxy findOrCreate(array $attributes) - * @method static Tag|Proxy first(string $sortedField = 'id') - * @method static Tag|Proxy last(string $sortedField = 'id') - * @method static Tag|Proxy random(array $attributes = []) - * @method static Tag|Proxy randomOrCreate(array $attributes = []) + * @method Tag|Proxy create(array|callable $attributes = []) + * @method static Tag|Proxy createOne(array $attributes = []) + * @method static Tag|Proxy find(object|array|mixed $criteria) + * @method static Tag|Proxy findOrCreate(array $attributes) + * @method static Tag|Proxy first(string $sortedField = 'id') + * @method static Tag|Proxy last(string $sortedField = 'id') + * @method static Tag|Proxy random(array $attributes = []) + * @method static Tag|Proxy randomOrCreate(array $attributes = []) * @method static Tag[]|Proxy[] all() * @method static Tag[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Tag[]|Proxy[] createSequence(array|callable $sequence) diff --git a/tests/Fixtures/Maker/expected/can-create-factory-interactively.php b/tests/Fixtures/Maker/expected/can_create_factory_interactively.php similarity index 74% rename from tests/Fixtures/Maker/expected/can-create-factory-interactively.php rename to tests/Fixtures/Maker/expected/can_create_factory_interactively.php index 0bb286e01..ac8f4f6d6 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-interactively.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_interactively.php @@ -2,21 +2,21 @@ namespace App\Factory; -use Zenstruck\Foundry\Tests\Fixtures\Entity\Tag; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; +use Zenstruck\Foundry\Tests\Fixtures\Entity\Tag; /** * @extends ModelFactory * - * @method Tag|Proxy create(array|callable $attributes = []) - * @method static Tag|Proxy createOne(array $attributes = []) - * @method static Tag|Proxy find(object|array|mixed $criteria) - * @method static Tag|Proxy findOrCreate(array $attributes) - * @method static Tag|Proxy first(string $sortedField = 'id') - * @method static Tag|Proxy last(string $sortedField = 'id') - * @method static Tag|Proxy random(array $attributes = []) - * @method static Tag|Proxy randomOrCreate(array $attributes = []) + * @method Tag|Proxy create(array|callable $attributes = []) + * @method static Tag|Proxy createOne(array $attributes = []) + * @method static Tag|Proxy find(object|array|mixed $criteria) + * @method static Tag|Proxy findOrCreate(array $attributes) + * @method static Tag|Proxy first(string $sortedField = 'id') + * @method static Tag|Proxy last(string $sortedField = 'id') + * @method static Tag|Proxy random(array $attributes = []) + * @method static Tag|Proxy randomOrCreate(array $attributes = []) * @method static Tag[]|Proxy[] all() * @method static Tag[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Tag[]|Proxy[] createSequence(array|callable $sequence) diff --git a/tests/Fixtures/Maker/expected/it-auto-activate-not-persisted-option-if-doctrine-not-enabled.php b/tests/Fixtures/Maker/expected/can_create_factory_with_auto_activated_not_persisted_option.php similarity index 91% rename from tests/Fixtures/Maker/expected/it-auto-activate-not-persisted-option-if-doctrine-not-enabled.php rename to tests/Fixtures/Maker/expected/can_create_factory_with_auto_activated_not_persisted_option.php index 421380d0b..0b53f2119 100644 --- a/tests/Fixtures/Maker/expected/it-auto-activate-not-persisted-option-if-doctrine-not-enabled.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_with_auto_activated_not_persisted_option.php @@ -2,15 +2,15 @@ namespace App\Factory; -use Zenstruck\Foundry\Tests\Fixtures\Entity\Category; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; +use Zenstruck\Foundry\Tests\Fixtures\Entity\Category; /** * @extends ModelFactory * - * @method Category|Proxy create(array|callable $attributes = []) - * @method static Category|Proxy createOne(array $attributes = []) + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Category[]|Proxy[] createSequence(array|callable $sequence) */ diff --git a/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php b/tests/Fixtures/Maker/expected/can_create_factory_with_phpstan_annotations.php similarity index 63% rename from tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php rename to tests/Fixtures/Maker/expected/can_create_factory_with_phpstan_annotations.php index 86ecb218b..87ce43907 100644 --- a/tests/Fixtures/Maker/expected/can-create-factory-with-phpstan-annotations.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_with_phpstan_annotations.php @@ -2,21 +2,21 @@ namespace App\Factory; -use Zenstruck\Foundry\Tests\Fixtures\Entity\Category; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; +use Zenstruck\Foundry\Tests\Fixtures\Entity\Category; /** * @extends ModelFactory * - * @method Category|Proxy create(array|callable $attributes = []) - * @method static Category|Proxy createOne(array $attributes = []) - * @method static Category|Proxy find(object|array|mixed $criteria) - * @method static Category|Proxy findOrCreate(array $attributes) - * @method static Category|Proxy first(string $sortedField = 'id') - * @method static Category|Proxy last(string $sortedField = 'id') - * @method static Category|Proxy random(array $attributes = []) - * @method static Category|Proxy randomOrCreate(array $attributes = []) + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) + * @method static Category|Proxy find(object|array|mixed $criteria) + * @method static Category|Proxy findOrCreate(array $attributes) + * @method static Category|Proxy first(string $sortedField = 'id') + * @method static Category|Proxy last(string $sortedField = 'id') + * @method static Category|Proxy random(array $attributes = []) + * @method static Category|Proxy randomOrCreate(array $attributes = []) * @method static Category[]|Proxy[] all() * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Category[]|Proxy[] createSequence(array|callable $sequence) @@ -24,14 +24,14 @@ * @method static Category[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) * @method static Category[]|Proxy[] randomSet(int $number, array $attributes = []) * - * @phpstan-method Proxy create(array|callable $attributes = []) - * @phpstan-method static Proxy createOne(array $attributes = []) - * @phpstan-method static Proxy find(object|array|mixed $criteria) - * @phpstan-method static Proxy findOrCreate(array $attributes) - * @phpstan-method static Proxy first(string $sortedField = 'id') - * @phpstan-method static Proxy last(string $sortedField = 'id') - * @phpstan-method static Proxy random(array $attributes = []) - * @phpstan-method static Proxy randomOrCreate(array $attributes = []) + * @phpstan-method Proxy create(array|callable $attributes = []) + * @phpstan-method static Proxy createOne(array $attributes = []) + * @phpstan-method static Proxy find(object|array|mixed $criteria) + * @phpstan-method static Proxy findOrCreate(array $attributes) + * @phpstan-method static Proxy first(string $sortedField = 'id') + * @phpstan-method static Proxy last(string $sortedField = 'id') + * @phpstan-method static Proxy random(array $attributes = []) + * @phpstan-method static Proxy randomOrCreate(array $attributes = []) * @phpstan-method static list> all() * @phpstan-method static list> createMany(int $number, array|callable $attributes = []) * @phpstan-method static list> createSequence(array|callable $sequence) diff --git a/tests/Functional/Bundle/Maker/MakeFactoryTest.php b/tests/Functional/Bundle/Maker/MakeFactoryTest.php index 099d2e24d..06531c82a 100644 --- a/tests/Functional/Bundle/Maker/MakeFactoryTest.php +++ b/tests/Functional/Bundle/Maker/MakeFactoryTest.php @@ -326,7 +326,7 @@ public function documentProvider(): iterable /** * @test */ - public function it_auto_activate_not_persisted_option_if_doctrine_not_enabled(): void + public function can_create_factory_with_auto_activated_not_persisted_option(): void { if (\getenv('USE_DAMA_DOCTRINE_TEST_BUNDLE')) { self::markTestSkipped('dama/doctrine-test-bundle should not be enabled.'); diff --git a/tests/Functional/Bundle/Maker/MakerTestCase.php b/tests/Functional/Bundle/Maker/MakerTestCase.php index 2108a8624..be75b9b95 100644 --- a/tests/Functional/Bundle/Maker/MakerTestCase.php +++ b/tests/Functional/Bundle/Maker/MakerTestCase.php @@ -48,7 +48,7 @@ protected function expectedFile(): string $path = \sprintf( '%s/../../../Fixtures/Maker/expected/%s.php', __DIR__, - (new AsciiSlugger())->slug($this->getName()) + (new AsciiSlugger())->slug($this->getName(), '_') ); $this->assertFileExists($path); From 8332956ad7274a556b9069af76f6cf2f89cfc827 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Thu, 17 Nov 2022 10:25:03 +0100 Subject: [PATCH 22/30] feat: make `Story::get()` static (implies `Story::load()->get()`) (#253) --- docs/index.rst | 4 +--- src/Story.php | 34 +++++++++++++++++++++++----------- tests/Functional/StoryTest.php | 21 ++++++++++++++++++++- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 364ad93fc..e52ee693d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1797,9 +1797,7 @@ Later, you can access the story's state when creating other fixtures: .. code-block:: php - PostFactory::createOne([ - 'category' => CategoryStory::load()->get('php') // Category Proxy - ]); + PostFactory::createOne(['category' => CategoryStory::get('php')]); // or use the magic method (functionally equivalent to above) PostFactory::createOne(['category' => CategoryStory::php()]); diff --git a/src/Story.php b/src/Story.php index f0b48e837..92e511483 100644 --- a/src/Story.php +++ b/src/Story.php @@ -4,6 +4,8 @@ /** * @author Kevin Bond + * + * @method static Proxy get(string $name) */ abstract class Story { @@ -15,12 +17,22 @@ abstract class Story final public function __call(string $method, array $arguments): Proxy { - return $this->get($method); + if ('get' !== $method) { + return $this->getState($method); + } + + trigger_deprecation('zenstruck/foundry', '1.24', 'Calling instance method "%1$s::get()" is deprecated and will be removed in 2.0, use the static "%1$s::get()" method instead.', static::class); + + return $this->getState($arguments[0]); } final public static function __callStatic(string $name, array $arguments): Proxy { - return static::load()->get($name); + if ('get' !== $name) { + return static::load()->getState($name); + } + + return static::load()->getState($arguments[0]); } final public static function load(): static @@ -97,15 +109,6 @@ final public function add(string $name, object $object): static return $this->addState($name, $object); } - final public function get(string $name): Proxy - { - if (!\array_key_exists($name, $this->objects)) { - throw new \InvalidArgumentException(\sprintf('"%s" was not registered. Did you forget to call "%s::add()"?', $name, static::class)); - } - - return $this->objects[$name]; - } - abstract public function build(): void; /** @@ -148,6 +151,15 @@ final protected function addState(string $name, object $object, ?string $pool = return $this; } + private function getState(string $name): Proxy + { + if (!\array_key_exists($name, $this->objects)) { + throw new \InvalidArgumentException(\sprintf('"%s" was not registered. Did you forget to call "%s::add()"?', $name, static::class)); + } + + return $this->objects[$name]; + } + private static function normalizeObject(object $object): Proxy { // ensure factories are persisted diff --git a/tests/Functional/StoryTest.php b/tests/Functional/StoryTest.php index 3ae8a1d76..113239c29 100644 --- a/tests/Functional/StoryTest.php +++ b/tests/Functional/StoryTest.php @@ -57,6 +57,14 @@ public function can_access_managed_proxies_via_magic_call_static(): void $this->assertSame('php', CategoryStory::php()->getName()); } + /** + * @test + */ + public function can_access_managed_proxies_static_get(): void + { + $this->assertSame('php', CategoryStory::get('php')->getName()); + } + /** * @test */ @@ -64,7 +72,18 @@ public function cannot_access_invalid_object(): void { $this->expectException(\InvalidArgumentException::class); - CategoryStory::load()->get('invalid'); + CategoryStory::get('invalid'); + } + + /** + * @test + * @group legacy + */ + public function can_access_managed_proxies_instance_get(): void + { + $this->expectDeprecation(\sprintf('Since zenstruck/foundry 1.24: Calling instance method "%1$s::get()" is deprecated and will be removed in 2.0, use the static "%1$s::get()" method instead.', CategoryStory::class)); + + $this->assertSame('php', CategoryStory::load()->get('php')->getName()); } /** From 4e5f9d9ab47e5d182f44b0446aa83778bf857793 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Tue, 22 Nov 2022 18:26:56 +0100 Subject: [PATCH 23/30] feat(make:factory): use factories to default non-nullable relationships (#351) Co-authored-by: Benjamin Knecht --- Makefile | 2 +- src/Bundle/Maker/MakeFactory.php | 48 +++++++++++-- tests/Fixtures/Entity/EntityWithRelations.php | 60 +++++++++++++++++ ..._create_factory_with_relation_defaults.php | 67 +++++++++++++++++++ .../Migrations/Version20221117081744.php | 54 +++++++++++++++ .../Bundle/Maker/MakeFactoryTest.php | 19 ++++++ tests/Functional/WithMigrationTest.php | 5 +- 7 files changed, 249 insertions(+), 6 deletions(-) create mode 100644 tests/Fixtures/Entity/EntityWithRelations.php create mode 100644 tests/Fixtures/Maker/expected/can_create_factory_with_relation_defaults.php create mode 100644 tests/Fixtures/Migrations/Version20221117081744.php diff --git a/Makefile b/Makefile index 1a6d52ca5..7153d1268 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ sca: docker-start bin/tools/phpstan/vendor ### Run static analysis bin/tools/phpstan/vendor: vendor bin/tools/phpstan/composer.json $(wildcard bin/tools/phpstan/composer.lock) @${DOCKER_PHP} composer bin phpstan install -database-generate-migration: docker-start vendor ### Generate new migration based on mapping in Zenstruck\Foundry\Tests\Fixtures\Entity +database-generate-migration: docker-start vendor database-drop-schema ### Generate new migration based on mapping in Zenstruck\Foundry\Tests\Fixtures\Entity @${DOCKER_PHP} vendor/bin/doctrine-migrations migrations:migrate --no-interaction --allow-no-migration # first, let's load into db existing migrations @${DOCKER_PHP} vendor/bin/doctrine-migrations migrations:diff --no-interaction @${DOCKER_PHP} vendor/bin/doctrine-migrations migrations:migrate --no-interaction # load the new migration diff --git a/src/Bundle/Maker/MakeFactory.php b/src/Bundle/Maker/MakeFactory.php index 7af1d2ba3..f2b6a7f86 100644 --- a/src/Bundle/Maker/MakeFactory.php +++ b/src/Bundle/Maker/MakeFactory.php @@ -61,14 +61,21 @@ final class MakeFactory extends AbstractMaker \DateTimeImmutable::class => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', ]; - /** @var string[] */ + /** @var array */ private array $entitiesWithFactories; public function __construct(private ManagerRegistry $managerRegistry, \Traversable $factories, private string $projectDir, private KernelInterface $kernel) { - $this->entitiesWithFactories = \array_map( - static fn(ModelFactory $factory): string => $factory::getEntityClass(), - \iterator_to_array($factories) + $this->entitiesWithFactories = \array_unique( + \array_reduce( + \iterator_to_array($factories), + static function(array $carry, ModelFactory $factory): array { + $carry[\get_class($factory)] = $factory::getEntityClass(); + + return $carry; + }, + [] + ) ); } @@ -268,6 +275,32 @@ private function defaultPropertiesForPersistedObject(string $class, bool $allFie $dbType = $em instanceof EntityManagerInterface ? 'ORM' : 'ODM'; + // If Factory exist for related entities populate too with auto defaults + foreach ($metadata->associationMappings as $item) { + // if joinColumns is not written entity is default nullable ($nullable = true;) + if (!\array_key_exists('joinColumns', $item)) { + continue; + } + + if (!\array_key_exists('nullable', $item['joinColumns'][0] ?? [])) { + continue; + } + + if (true === $item['joinColumns'][0]['nullable']) { + continue; + } + + $fieldName = $item['fieldName']; + + if (!$factoryClass = $this->getFactoryForClass($item['targetEntity'])) { + yield \lcfirst($fieldName) => "null, // TODO add {$item['targetEntity']} {$dbType} type manually"; + + continue; + } + + yield \lcfirst($fieldName) => "\\{$factoryClass}::new(),"; + } + foreach ($metadata->fieldMappings as $property) { // ignore identifiers and nullable fields if ((!$allFields && ($property['nullable'] ?? false)) || \in_array($property['fieldName'], $ids, true)) { @@ -342,4 +375,11 @@ private function doctrineEnabled(): bool return $ormEnabled || $odmEnabled; } + + private function getFactoryForClass(string $class): ?string + { + $factories = \array_flip($this->entitiesWithFactories); + + return $factories[$class] ?? null; + } } diff --git a/tests/Fixtures/Entity/EntityWithRelations.php b/tests/Fixtures/Entity/EntityWithRelations.php new file mode 100644 index 000000000..5c9e48fde --- /dev/null +++ b/tests/Fixtures/Entity/EntityWithRelations.php @@ -0,0 +1,60 @@ + + * + * @method EntityWithRelations|Proxy create(array|callable $attributes = []) + * @method static EntityWithRelations|Proxy createOne(array $attributes = []) + * @method static EntityWithRelations|Proxy find(object|array|mixed $criteria) + * @method static EntityWithRelations|Proxy findOrCreate(array $attributes) + * @method static EntityWithRelations|Proxy first(string $sortedField = 'id') + * @method static EntityWithRelations|Proxy last(string $sortedField = 'id') + * @method static EntityWithRelations|Proxy random(array $attributes = []) + * @method static EntityWithRelations|Proxy randomOrCreate(array $attributes = []) + * @method static EntityWithRelations[]|Proxy[] all() + * @method static EntityWithRelations[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static EntityWithRelations[]|Proxy[] createSequence(array|callable $sequence) + * @method static EntityWithRelations[]|Proxy[] findBy(array $attributes) + * @method static EntityWithRelations[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static EntityWithRelations[]|Proxy[] randomSet(int $number, array $attributes = []) + */ +final class EntityWithRelationsFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'manyToOne' => \Zenstruck\Foundry\Tests\Fixtures\Factories\CategoryFactory::new(), + 'manyToOneWithNotExistingFactory' => null, // TODO add Zenstruck\Foundry\Tests\Fixtures\Entity\Cascade\Brand ORM type manually + 'oneToOne' => \Zenstruck\Foundry\Tests\Fixtures\Factories\CategoryFactory::new(), + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(EntityWithRelations $entityWithRelations): void {}) + ; + } + + protected static function getClass(): string + { + return EntityWithRelations::class; + } +} diff --git a/tests/Fixtures/Migrations/Version20221117081744.php b/tests/Fixtures/Migrations/Version20221117081744.php new file mode 100644 index 000000000..849c6a115 --- /dev/null +++ b/tests/Fixtures/Migrations/Version20221117081744.php @@ -0,0 +1,54 @@ +addSql('CREATE TABLE entity_with_relations (id INT AUTO_INCREMENT NOT NULL, oneToOne_id INT NOT NULL, oneToOneNullable_id INT DEFAULT NULL, manyToOne_id INT NOT NULL, manyToOneNullable_id INT DEFAULT NULL, manyToOneNullableDefault_id INT DEFAULT NULL, manyToOneWithNotExistingFactory_id INT NOT NULL, UNIQUE INDEX UNIQ_A9C9EC969017888C (oneToOne_id), UNIQUE INDEX UNIQ_A9C9EC96DA2BFB84 (oneToOneNullable_id), INDEX IDX_A9C9EC962E3A088A (manyToOne_id), INDEX IDX_A9C9EC968097B86C (manyToOneNullable_id), INDEX IDX_A9C9EC968572C13C (manyToOneNullableDefault_id), INDEX IDX_A9C9EC96FF92FDCA (manyToOneWithNotExistingFactory_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE entitywithrelations_category (entitywithrelations_id INT NOT NULL, category_id INT NOT NULL, INDEX IDX_CD6EBFAB337AA4F7 (entitywithrelations_id), INDEX IDX_CD6EBFAB12469DE2 (category_id), PRIMARY KEY(entitywithrelations_id, category_id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE entity_with_relations ADD CONSTRAINT FK_A9C9EC969017888C FOREIGN KEY (oneToOne_id) REFERENCES categories (id)'); + $this->addSql('ALTER TABLE entity_with_relations ADD CONSTRAINT FK_A9C9EC96DA2BFB84 FOREIGN KEY (oneToOneNullable_id) REFERENCES categories (id)'); + $this->addSql('ALTER TABLE entity_with_relations ADD CONSTRAINT FK_A9C9EC962E3A088A FOREIGN KEY (manyToOne_id) REFERENCES categories (id)'); + $this->addSql('ALTER TABLE entity_with_relations ADD CONSTRAINT FK_A9C9EC968097B86C FOREIGN KEY (manyToOneNullable_id) REFERENCES categories (id)'); + $this->addSql('ALTER TABLE entity_with_relations ADD CONSTRAINT FK_A9C9EC968572C13C FOREIGN KEY (manyToOneNullableDefault_id) REFERENCES categories (id)'); + $this->addSql('ALTER TABLE entity_with_relations ADD CONSTRAINT FK_A9C9EC96FF92FDCA FOREIGN KEY (manyToOneWithNotExistingFactory_id) REFERENCES brand_cascade (id)'); + $this->addSql('ALTER TABLE entitywithrelations_category ADD CONSTRAINT FK_CD6EBFAB337AA4F7 FOREIGN KEY (entitywithrelations_id) REFERENCES entity_with_relations (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE entitywithrelations_category ADD CONSTRAINT FK_CD6EBFAB12469DE2 FOREIGN KEY (category_id) REFERENCES categories (id) ON DELETE CASCADE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE entity_with_relations DROP FOREIGN KEY FK_A9C9EC969017888C'); + $this->addSql('ALTER TABLE entity_with_relations DROP FOREIGN KEY FK_A9C9EC96DA2BFB84'); + $this->addSql('ALTER TABLE entity_with_relations DROP FOREIGN KEY FK_A9C9EC962E3A088A'); + $this->addSql('ALTER TABLE entity_with_relations DROP FOREIGN KEY FK_A9C9EC968097B86C'); + $this->addSql('ALTER TABLE entity_with_relations DROP FOREIGN KEY FK_A9C9EC968572C13C'); + $this->addSql('ALTER TABLE entity_with_relations DROP FOREIGN KEY FK_A9C9EC96FF92FDCA'); + $this->addSql('ALTER TABLE entitywithrelations_category DROP FOREIGN KEY FK_CD6EBFAB337AA4F7'); + $this->addSql('ALTER TABLE entitywithrelations_category DROP FOREIGN KEY FK_CD6EBFAB12469DE2'); + $this->addSql('DROP TABLE entity_with_relations'); + $this->addSql('DROP TABLE entitywithrelations_category'); + } + + public function isTransactional(): bool + { + return false; + } +} diff --git a/tests/Functional/Bundle/Maker/MakeFactoryTest.php b/tests/Functional/Bundle/Maker/MakeFactoryTest.php index 06531c82a..8f780b67b 100644 --- a/tests/Functional/Bundle/Maker/MakeFactoryTest.php +++ b/tests/Functional/Bundle/Maker/MakeFactoryTest.php @@ -8,6 +8,7 @@ use Zenstruck\Foundry\Tests\Fixtures\Document\Comment; use Zenstruck\Foundry\Tests\Fixtures\Document\Post; use Zenstruck\Foundry\Tests\Fixtures\Entity\Category; +use Zenstruck\Foundry\Tests\Fixtures\Entity\EntityWithRelations; use Zenstruck\Foundry\Tests\Fixtures\Entity\Tag; use Zenstruck\Foundry\Tests\Fixtures\Kernel; use Zenstruck\Foundry\Tests\Fixtures\Object\SomeObject; @@ -345,4 +346,22 @@ public function can_create_factory_with_auto_activated_not_persisted_option(): v $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/CategoryFactory.php')); } + + /** + * @test + */ + public function can_create_factory_with_relation_defaults(): void + { + if (!\getenv('USE_ORM')) { + self::markTestSkipped('doctrine/orm not enabled.'); + } + + $tester = new CommandTester((new Application(self::bootKernel()))->find('make:factory')); + + $this->assertFileDoesNotExist(self::tempFile('src/Factory/EntityWithRelationsFactory.php')); + + $tester->execute(['class' => EntityWithRelations::class]); + + $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/EntityWithRelationsFactory.php')); + } } diff --git a/tests/Functional/WithMigrationTest.php b/tests/Functional/WithMigrationTest.php index fd1c54e5b..5858e6aa7 100644 --- a/tests/Functional/WithMigrationTest.php +++ b/tests/Functional/WithMigrationTest.php @@ -23,7 +23,10 @@ public function it_generates_a_valid_schema(): void { $kernel = static::bootKernel(); $validator = new SchemaValidator($kernel->getContainer()->get('doctrine')->getManager()); - self::assertEmpty($validator->validateMapping()); + self::assertEmpty( + $validator->validateMapping(), + \implode("\n", \array_map(static fn($s): string => \implode("\n", $s), $validator->validateMapping())) + ); self::assertTrue($validator->schemaInSyncWithMetadata()); } From 20ac3499c224c7ad4f5fe3e5f4da04ad2330ea5d Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Wed, 23 Nov 2022 19:15:19 +0100 Subject: [PATCH 24/30] refactor(make:factory): use value object to render template (#354) --- src/Bundle/Maker/MakeFactory.php | 116 +++++++++--------- src/Bundle/Maker/MakeFactoryData.php | 107 ++++++++++++++++ src/Bundle/Maker/MakeFactoryPHPDocMethod.php | 71 +++++++++++ src/Bundle/Resources/skeleton/Factory.tpl.php | 83 ++++--------- .../Maker/expected/can_create_factory.php | 16 +-- ...ate_factory_for_entity_with_repository.php | 87 +++++++++++++ ...create_factory_for_not_persisted_class.php | 4 +- ..._for_not_persisted_class_interactively.php | 4 +- ...factory_for_odm_with_data_set_document.php | 16 +-- ...or_odm_with_data_set_embedded_document.php | 16 +-- .../can_create_factory_in_test_dir.php | 16 +-- ...eate_factory_in_test_dir_interactively.php | 16 +-- .../can_create_factory_interactively.php | 16 +-- ...th_auto_activated_not_persisted_option.php | 4 +- ...reate_factory_with_phpstan_annotations.php | 32 ++--- ..._create_factory_with_relation_defaults.php | 21 ++-- .../Bundle/Maker/MakeFactoryTest.php | 34 ++++- 17 files changed, 460 insertions(+), 199 deletions(-) create mode 100644 src/Bundle/Maker/MakeFactoryData.php create mode 100644 src/Bundle/Maker/MakeFactoryPHPDocMethod.php create mode 100644 tests/Fixtures/Maker/expected/can_create_factory_for_entity_with_repository.php diff --git a/src/Bundle/Maker/MakeFactory.php b/src/Bundle/Maker/MakeFactory.php index f2b6a7f86..0121aa7e9 100644 --- a/src/Bundle/Maker/MakeFactory.php +++ b/src/Bundle/Maker/MakeFactory.php @@ -61,14 +61,15 @@ final class MakeFactory extends AbstractMaker \DateTimeImmutable::class => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', ]; - /** @var array */ + /** @var array */ private array $entitiesWithFactories; public function __construct(private ManagerRegistry $managerRegistry, \Traversable $factories, private string $projectDir, private KernelInterface $kernel) { + /** @phpstan-ignore-next-line */ $this->entitiesWithFactories = \array_unique( \array_reduce( - \iterator_to_array($factories), + \iterator_to_array($factories, preserve_keys: true), static function(array $carry, ModelFactory $factory): array { $carry[\get_class($factory)] = $factory::getEntityClass(); @@ -174,47 +175,23 @@ private function generateFactory(string $class, InputInterface $input, ConsoleSt throw new RuntimeCommandException(\sprintf('Class "%s" not found.', $input->getArgument('class'))); } - $namespace = $input->getOption('namespace'); + $makeFactoryData = $this->createMakeFactoryData($generator, $class, !$input->getOption('not-persisted')); - // strip maker's root namespace if set - if (0 === \mb_strpos($namespace, $generator->getRootNamespace())) { - $namespace = \mb_substr($namespace, \mb_strlen($generator->getRootNamespace())); - } - - $namespace = \trim($namespace, '\\'); - - // if creating in tests dir, ensure namespace prefixed with Tests\ - if ($input->getOption('test') && 0 !== \mb_strpos($namespace, 'Tests\\')) { - $namespace = 'Tests\\'.$namespace; - } - - $object = new \ReflectionClass($class); - $factory = $generator->createClassNameDetails($object->getShortName(), $namespace, 'Factory'); - - if (!$input->getOption('not-persisted')) { - $repository = new \ReflectionClass($this->managerRegistry->getRepository($object->getName())); - - if (0 !== \mb_strpos($repository->getName(), $generator->getRootNamespace())) { - // not using a custom repository - $repository = null; - } - } + $factory = $generator->createClassNameDetails( + $makeFactoryData->getObjectShortName(), + $this->guessNamespace($generator, $input->getOption('namespace'), (bool) $input->getOption('test')), + 'Factory' + ); - $defaultValues = $input->getOption('not-persisted') - ? $this->defaultPropertiesForNotPersistedObject($object->getName(), $input->getOption('all-fields')) - : $this->defaultPropertiesForPersistedObject($object->getName(), $input->getOption('all-fields')); - $defaultValues = \iterator_to_array($defaultValues, true); - \ksort($defaultValues); + $input->getOption('not-persisted') + ? $this->defaultPropertiesForNotPersistedObject($makeFactoryData, $input->getOption('all-fields')) + : $this->defaultPropertiesForPersistedObject($makeFactoryData, $input->getOption('all-fields')); $generator->generateClass( $factory->getFullName(), __DIR__.'/../Resources/skeleton/Factory.tpl.php', [ - 'object' => $object, - 'defaultProperties' => $defaultValues, - 'repository' => $repository ?? null, - 'phpstanEnabled' => $this->phpstanEnabled(), - 'persisted' => !$input->getOption('not-persisted'), + 'makeFactoryData' => $makeFactoryData, ] ); @@ -256,13 +233,10 @@ private function entityChoices(): array return $choices; } - /** - * @param class-string $class - * - * @return \Generator - */ - private function defaultPropertiesForPersistedObject(string $class, bool $allFields): iterable + private function defaultPropertiesForPersistedObject(MakeFactoryData $makeFactoryData, bool $allFields): void { + $class = $makeFactoryData->getObjectFullyQualifiedClassName(); + $em = $this->managerRegistry->getManagerForClass($class); if (!$em instanceof ObjectManager) { @@ -293,12 +267,14 @@ private function defaultPropertiesForPersistedObject(string $class, bool $allFie $fieldName = $item['fieldName']; if (!$factoryClass = $this->getFactoryForClass($item['targetEntity'])) { - yield \lcfirst($fieldName) => "null, // TODO add {$item['targetEntity']} {$dbType} type manually"; + $makeFactoryData->addDefaultProperty(\lcfirst($fieldName), "null, // TODO add {$item['targetEntity']} {$dbType} type manually"); continue; } - yield \lcfirst($fieldName) => "\\{$factoryClass}::new(),"; + $factory = new \ReflectionClass($factoryClass); + $makeFactoryData->addUse($factory->getName()); + $makeFactoryData->addDefaultProperty(\lcfirst($fieldName), "{$factory->getShortName()}::new(),"); } foreach ($metadata->fieldMappings as $property) { @@ -315,20 +291,13 @@ private function defaultPropertiesForPersistedObject(string $class, bool $allFie $value = self::DEFAULTS_FOR_PERSISTED[$type]; } - yield $property['fieldName'] => \str_replace('{length}', (string) $length, $value); + $makeFactoryData->addDefaultProperty($property['fieldName'], \str_replace('{length}', (string) $length, $value)); } } - /** - * @param class-string $class - * - * @return \Generator - */ - private function defaultPropertiesForNotPersistedObject(string $class, bool $allFields): \Generator + private function defaultPropertiesForNotPersistedObject(MakeFactoryData $makeFactoryData, bool $allFields): void { - $object = new \ReflectionClass($class); - - foreach ($object->getProperties() as $property) { + foreach ($makeFactoryData->getObject()->getProperties() as $property) { // ignore identifiers and nullable fields if (!$allFields && ($property->hasDefaultValue() || !$property->hasType() || $property->getType()?->allowsNull())) { continue; @@ -346,7 +315,7 @@ private function defaultPropertiesForNotPersistedObject(string $class, bool $all $value = self::DEFAULTS_FOR_NOT_PERSISTED[$type]; } - yield $property->getName() => $value; + $makeFactoryData->addDefaultProperty($property->getName(), $value); } } @@ -376,10 +345,47 @@ private function doctrineEnabled(): bool return $ormEnabled || $odmEnabled; } + /** @return class-string|null */ private function getFactoryForClass(string $class): ?string { $factories = \array_flip($this->entitiesWithFactories); return $factories[$class] ?? null; } + + /** + * @param class-string $class + */ + private function createMakeFactoryData(Generator $generator, string $class, bool $persisted): MakeFactoryData + { + $object = new \ReflectionClass($class); + + if ($persisted) { + $repository = new \ReflectionClass($this->managerRegistry->getRepository($object->getName())); + + if (\str_starts_with($repository->getName(), 'Doctrine')) { + // not using a custom repository + $repository = null; + } + } + + return new MakeFactoryData($object, $repository ?? null, $this->phpstanEnabled(), $persisted); + } + + private function guessNamespace(Generator $generator, string $namespace, bool $test): string + { + // strip maker's root namespace if set + if (0 === \mb_strpos($namespace, $generator->getRootNamespace())) { + $namespace = \mb_substr($namespace, \mb_strlen($generator->getRootNamespace())); + } + + $namespace = \trim($namespace, '\\'); + + // if creating in tests dir, ensure namespace prefixed with Tests\ + if ($test && 0 !== \mb_strpos($namespace, 'Tests\\')) { + $namespace = 'Tests\\'.$namespace; + } + + return $namespace; + } } diff --git a/src/Bundle/Maker/MakeFactoryData.php b/src/Bundle/Maker/MakeFactoryData.php new file mode 100644 index 000000000..eeb3dc55b --- /dev/null +++ b/src/Bundle/Maker/MakeFactoryData.php @@ -0,0 +1,107 @@ + */ + private array $uses; + /** @var array */ + private array $defaultProperties = []; + /** @var non-empty-list */ + private array $methodsInPHPDoc; + + public function __construct(private \ReflectionClass $object, private ?\ReflectionClass $repository, private bool $withPHPStanEnabled, private bool $persisted) + { + $this->uses = [ + ModelFactory::class, + Proxy::class, + $object->getName(), + ]; + + if ($repository) { + $this->uses[] = $repository->getName(); + $this->uses[] = RepositoryProxy::class; + } + + $this->methodsInPHPDoc = MakeFactoryPHPDocMethod::createAll($this); + } + + public function getObject(): \ReflectionClass + { + return $this->object; + } + + public function getObjectShortName(): string + { + return $this->object->getShortName(); + } + + /** @return class-string */ + public function getObjectFullyQualifiedClassName(): string + { + return $this->object->getName(); + } + + public function getRepositoryShortName(): ?string + { + return $this->repository?->getShortName(); + } + + public function isPersisted(): bool + { + return $this->persisted; + } + + public function hasPHPStanEnabled(): bool + { + return $this->withPHPStanEnabled; + } + + public function addUse(string $use): void + { + if (!\in_array($use, $this->uses, true)) { + $this->uses[] = $use; + } + } + + public function getUses(): array + { + $uses = $this->uses; + \sort($uses); + + return $uses; + } + + public function addDefaultProperty(string $propertyName, string $defaultValue): void + { + $this->defaultProperties[$propertyName] = $defaultValue; + } + + public function getDefaultProperties(): array + { + $defaultProperties = $this->defaultProperties; + \ksort($defaultProperties); + + return $defaultProperties; + } + + /** @return list */ + public function getMethodsPHPDoc(): array + { + $methodsInPHPDoc = $this->methodsInPHPDoc; + \usort( + $methodsInPHPDoc, + static fn(MakeFactoryPHPDocMethod $m1, MakeFactoryPHPDocMethod $m2) => $m1->sortValue() <=> $m2->sortValue() + ); + + return $methodsInPHPDoc; + } +} diff --git a/src/Bundle/Maker/MakeFactoryPHPDocMethod.php b/src/Bundle/Maker/MakeFactoryPHPDocMethod.php new file mode 100644 index 000000000..017cf6967 --- /dev/null +++ b/src/Bundle/Maker/MakeFactoryPHPDocMethod.php @@ -0,0 +1,71 @@ + */ + public static function createAll(MakeFactoryData $makeFactoryData): array + { + $methods = []; + + $methods[] = new self($makeFactoryData->getObjectShortName(), 'create(array|callable $attributes = [])', returnsCollection: false, isStatic: false); + $methods[] = new self($makeFactoryData->getObjectShortName(), 'createOne(array $attributes = [])', returnsCollection: false); + + $methods[] = new self($makeFactoryData->getObjectShortName(), 'createMany(int $number, array|callable $attributes = [])', returnsCollection: true); + $methods[] = new self($makeFactoryData->getObjectShortName(), 'createSequence(array|callable $sequence)', returnsCollection: true); + + if ($makeFactoryData->isPersisted()) { + $methods[] = new self($makeFactoryData->getObjectShortName(), 'find(object|array|mixed $criteria)', returnsCollection: false); + $methods[] = new self($makeFactoryData->getObjectShortName(), 'findOrCreate(array $attributes)', returnsCollection: false); + $methods[] = new self($makeFactoryData->getObjectShortName(), 'first(string $sortedField = \'id\')', returnsCollection: false); + $methods[] = new self($makeFactoryData->getObjectShortName(), 'last(string $sortedField = \'id\')', returnsCollection: false); + $methods[] = new self($makeFactoryData->getObjectShortName(), 'random(array $attributes = [])', returnsCollection: false); + $methods[] = new self($makeFactoryData->getObjectShortName(), 'randomOrCreate(array $attributes = [])', returnsCollection: false); + + $methods[] = new self($makeFactoryData->getObjectShortName(), 'all()', returnsCollection: true); + $methods[] = new self($makeFactoryData->getObjectShortName(), 'findBy(array $attributes)', returnsCollection: true); + $methods[] = new self($makeFactoryData->getObjectShortName(), 'randomRange(int $min, int $max, array $attributes = [])', returnsCollection: true); + $methods[] = new self($makeFactoryData->getObjectShortName(), 'randomSet(int $number, array $attributes = [])', returnsCollection: true); + + if (null !== $makeFactoryData->getRepositoryShortName()) { + $methods[] = new self($makeFactoryData->getRepositoryShortName(), 'repository()', returnsCollection: false, isRepository: true); + } + } + + return $methods; + } + + public function toString(bool $withPHPStanEnabled = false): string + { + $annotation = $withPHPStanEnabled ? 'phpstan-method' : 'method'; + $static = $this->isStatic ? 'static' : ' '; + + $proxyType = $this->isRepository ? 'RepositoryProxy' : 'Proxy'; + + /** @phpstan-ignore-next-line */ + $returnType = match ([$this->returnsCollection, $withPHPStanEnabled]) { + [true, true] => "list<{$proxyType}<{$this->objectName}>>", + [true, false] => "{$this->objectName}[]|{$proxyType}[]", + [false, true] => "{$proxyType}<{$this->objectName}>", + [false, false] => "{$this->objectName}|{$proxyType}", + }; + + return " * @{$annotation} {$static} {$returnType} {$this->prototype}"; + } + + public function sortValue(): string + { + return \sprintf( + "returnsCollection:%s, prototype:{$this->prototype}", + $this->returnsCollection ? '1' : '0', + ); + } +} diff --git a/src/Bundle/Resources/skeleton/Factory.tpl.php b/src/Bundle/Resources/skeleton/Factory.tpl.php index a6143e727..34fa98d2f 100644 --- a/src/Bundle/Resources/skeleton/Factory.tpl.php +++ b/src/Bundle/Resources/skeleton/Factory.tpl.php @@ -1,66 +1,29 @@ - + -namespace ; +namespace ; -getName()};", -); +getUses() as $use) { + echo "use $use;\n"; +} +?> -if ($persisted && $repository) { - $uses[] = "use {$repository->getName()};"; +/** + * @extends ModelFactory<getObjectShortName() ?>> + * +getMethodsPHPDoc() as $methodPHPDoc) { + echo "{$methodPHPDoc->toString()}\n"; } -sort($uses); +if ($makeFactoryData->hasPHPStanEnabled()) { + echo " *\n"; -foreach ($uses as $use) { - echo "$use\n"; + foreach ($makeFactoryData->getMethodsPHPDoc() as $methodPHPDoc) { + echo "{$methodPHPDoc->toString(true)}\n"; + } } ?> - -/** - * @extends ModelFactory<getShortName() ?>> - * - * @method getShortName() ?>|Proxy create(array|callable $attributes = []) - * @method static getShortName() ?>|Proxy createOne(array $attributes = []) - * @method static getShortName() ?>|Proxy find(object|array|mixed $criteria) - * @method static getShortName() ?>|Proxy findOrCreate(array $attributes) - * @method static getShortName() ?>|Proxy first(string $sortedField = 'id') - * @method static getShortName() ?>|Proxy last(string $sortedField = 'id') - * @method static getShortName() ?>|Proxy random(array $attributes = []) - * @method static getShortName() ?>|Proxy randomOrCreate(array $attributes = []) - * @method static getShortName() ?>[]|Proxy[] all() - - * @method static getShortName() ?>[]|Proxy[] createMany(int $number, array|callable $attributes = []) - * @method static getShortName() ?>[]|Proxy[] createSequence(array|callable $sequence) - * @method static getShortName() ?>[]|Proxy[] findBy(array $attributes) - * @method static getShortName() ?>[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) - * @method static getShortName() ?>[]|Proxy[] randomSet(int $number, array $attributes = []) - * @method static getShortName() ?>|RepositoryProxy repository() - - - * - * @phpstan-method Proxy<getShortName() ?>> create(array|callable $attributes = []) - * @phpstan-method static Proxy<getShortName() ?>> createOne(array $attributes = []) - * @phpstan-method static Proxy<getShortName() ?>> find(object|array|mixed $criteria) - * @phpstan-method static Proxy<getShortName() ?>> findOrCreate(array $attributes) - * @phpstan-method static Proxy<getShortName() ?>> first(string $sortedField = 'id') - * @phpstan-method static Proxy<getShortName() ?>> last(string $sortedField = 'id') - * @phpstan-method static Proxy<getShortName() ?>> random(array $attributes = []) - * @phpstan-method static Proxy<getShortName() ?>> randomOrCreate(array $attributes = []) - * @phpstan-method static listgetShortName() ?>>> all() - - * @phpstan-method static listgetShortName() ?>>> createMany(int $number, array|callable $attributes = []) - * @phpstan-method static listgetShortName() ?>>> createSequence(array|callable $sequence) - - * @phpstan-method static listgetShortName() ?>>> findBy(array $attributes) - * @phpstan-method static listgetShortName() ?>>> randomRange(int $min, int $max, array $attributes = []) - * @phpstan-method static listgetShortName() ?>>> randomSet(int $number, array $attributes = []) - * @phpstan-method static RepositoryProxy<getShortName() ?>> repository() - - - */ final class extends ModelFactory { @@ -83,8 +46,8 @@ protected function getDefaults(): array { return [ $type) { - echo " '".$fieldname."' => ".$type."\n"; +foreach ($makeFactoryData->getDefaultProperties() as $propertyName => $value) { + echo " '{$propertyName}' => {$value}\n"; } ?> ]; @@ -96,15 +59,15 @@ protected function getDefaults(): array protected function initialize(): self { return $this - +isPersisted()): ?> ->withoutPersisting() - // ->afterInstantiate(function(getShortName() ?> $getShortName()) ?>): void {}) + // ->afterInstantiate(function(getObjectShortName() ?> $getObjectShortName()) ?>): void {}) ; } protected static function getClass(): string { - return getShortName() ?>::class; + return getObjectShortName() ?>::class; } } diff --git a/tests/Fixtures/Maker/expected/can_create_factory.php b/tests/Fixtures/Maker/expected/can_create_factory.php index af79def5e..2cc61d1ef 100644 --- a/tests/Fixtures/Maker/expected/can_create_factory.php +++ b/tests/Fixtures/Maker/expected/can_create_factory.php @@ -9,14 +9,14 @@ /** * @extends ModelFactory * - * @method Category|Proxy create(array|callable $attributes = []) - * @method static Category|Proxy createOne(array $attributes = []) - * @method static Category|Proxy find(object|array|mixed $criteria) - * @method static Category|Proxy findOrCreate(array $attributes) - * @method static Category|Proxy first(string $sortedField = 'id') - * @method static Category|Proxy last(string $sortedField = 'id') - * @method static Category|Proxy random(array $attributes = []) - * @method static Category|Proxy randomOrCreate(array $attributes = []) + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) + * @method static Category|Proxy find(object|array|mixed $criteria) + * @method static Category|Proxy findOrCreate(array $attributes) + * @method static Category|Proxy first(string $sortedField = 'id') + * @method static Category|Proxy last(string $sortedField = 'id') + * @method static Category|Proxy random(array $attributes = []) + * @method static Category|Proxy randomOrCreate(array $attributes = []) * @method static Category[]|Proxy[] all() * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Category[]|Proxy[] createSequence(array|callable $sequence) diff --git a/tests/Fixtures/Maker/expected/can_create_factory_for_entity_with_repository.php b/tests/Fixtures/Maker/expected/can_create_factory_for_entity_with_repository.php new file mode 100644 index 000000000..fee36fd04 --- /dev/null +++ b/tests/Fixtures/Maker/expected/can_create_factory_for_entity_with_repository.php @@ -0,0 +1,87 @@ + + * + * @method Post|Proxy create(array|callable $attributes = []) + * @method static Post|Proxy createOne(array $attributes = []) + * @method static Post|Proxy find(object|array|mixed $criteria) + * @method static Post|Proxy findOrCreate(array $attributes) + * @method static Post|Proxy first(string $sortedField = 'id') + * @method static Post|Proxy last(string $sortedField = 'id') + * @method static Post|Proxy random(array $attributes = []) + * @method static Post|Proxy randomOrCreate(array $attributes = []) + * @method static PostRepository|RepositoryProxy repository() + * @method static Post[]|Proxy[] all() + * @method static Post[]|Proxy[] createMany(int $number, array|callable $attributes = []) + * @method static Post[]|Proxy[] createSequence(array|callable $sequence) + * @method static Post[]|Proxy[] findBy(array $attributes) + * @method static Post[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) + * @method static Post[]|Proxy[] randomSet(int $number, array $attributes = []) + * + * @phpstan-method Proxy create(array|callable $attributes = []) + * @phpstan-method static Proxy createOne(array $attributes = []) + * @phpstan-method static Proxy find(object|array|mixed $criteria) + * @phpstan-method static Proxy findOrCreate(array $attributes) + * @phpstan-method static Proxy first(string $sortedField = 'id') + * @phpstan-method static Proxy last(string $sortedField = 'id') + * @phpstan-method static Proxy random(array $attributes = []) + * @phpstan-method static Proxy randomOrCreate(array $attributes = []) + * @phpstan-method static RepositoryProxy repository() + * @phpstan-method static list> all() + * @phpstan-method static list> createMany(int $number, array|callable $attributes = []) + * @phpstan-method static list> createSequence(array|callable $sequence) + * @phpstan-method static list> findBy(array $attributes) + * @phpstan-method static list> randomRange(int $min, int $max, array $attributes = []) + * @phpstan-method static list> randomSet(int $number, array $attributes = []) + */ +final class PostFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'body' => self::faker()->text(), + 'createdAt' => null, // TODO add DATETIME ORM type manually + 'title' => self::faker()->text(255), + 'viewCount' => self::faker()->randomNumber(), + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(Post $post): void {}) + ; + } + + protected static function getClass(): string + { + return Post::class; + } +} diff --git a/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class.php b/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class.php index 58effd4d8..f0b052388 100644 --- a/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class.php @@ -9,8 +9,8 @@ /** * @extends ModelFactory * - * @method SomeObject|Proxy create(array|callable $attributes = []) - * @method static SomeObject|Proxy createOne(array $attributes = []) + * @method SomeObject|Proxy create(array|callable $attributes = []) + * @method static SomeObject|Proxy createOne(array $attributes = []) * @method static SomeObject[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static SomeObject[]|Proxy[] createSequence(array|callable $sequence) */ diff --git a/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class_interactively.php b/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class_interactively.php index fba13b481..442a0b4a1 100644 --- a/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class_interactively.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_for_not_persisted_class_interactively.php @@ -9,8 +9,8 @@ /** * @extends ModelFactory * - * @method SomeObject|Proxy create(array|callable $attributes = []) - * @method static SomeObject|Proxy createOne(array $attributes = []) + * @method SomeObject|Proxy create(array|callable $attributes = []) + * @method static SomeObject|Proxy createOne(array $attributes = []) * @method static SomeObject[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static SomeObject[]|Proxy[] createSequence(array|callable $sequence) */ diff --git a/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_document.php b/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_document.php index 041a04bf5..e247b5879 100644 --- a/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_document.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_document.php @@ -9,14 +9,14 @@ /** * @extends ModelFactory * - * @method Post|Proxy create(array|callable $attributes = []) - * @method static Post|Proxy createOne(array $attributes = []) - * @method static Post|Proxy find(object|array|mixed $criteria) - * @method static Post|Proxy findOrCreate(array $attributes) - * @method static Post|Proxy first(string $sortedField = 'id') - * @method static Post|Proxy last(string $sortedField = 'id') - * @method static Post|Proxy random(array $attributes = []) - * @method static Post|Proxy randomOrCreate(array $attributes = []) + * @method Post|Proxy create(array|callable $attributes = []) + * @method static Post|Proxy createOne(array $attributes = []) + * @method static Post|Proxy find(object|array|mixed $criteria) + * @method static Post|Proxy findOrCreate(array $attributes) + * @method static Post|Proxy first(string $sortedField = 'id') + * @method static Post|Proxy last(string $sortedField = 'id') + * @method static Post|Proxy random(array $attributes = []) + * @method static Post|Proxy randomOrCreate(array $attributes = []) * @method static Post[]|Proxy[] all() * @method static Post[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Post[]|Proxy[] createSequence(array|callable $sequence) diff --git a/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_embedded_document.php b/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_embedded_document.php index 3417058f6..d3bfb5856 100644 --- a/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_embedded_document.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_for_odm_with_data_set_embedded_document.php @@ -9,14 +9,14 @@ /** * @extends ModelFactory * - * @method Comment|Proxy create(array|callable $attributes = []) - * @method static Comment|Proxy createOne(array $attributes = []) - * @method static Comment|Proxy find(object|array|mixed $criteria) - * @method static Comment|Proxy findOrCreate(array $attributes) - * @method static Comment|Proxy first(string $sortedField = 'id') - * @method static Comment|Proxy last(string $sortedField = 'id') - * @method static Comment|Proxy random(array $attributes = []) - * @method static Comment|Proxy randomOrCreate(array $attributes = []) + * @method Comment|Proxy create(array|callable $attributes = []) + * @method static Comment|Proxy createOne(array $attributes = []) + * @method static Comment|Proxy find(object|array|mixed $criteria) + * @method static Comment|Proxy findOrCreate(array $attributes) + * @method static Comment|Proxy first(string $sortedField = 'id') + * @method static Comment|Proxy last(string $sortedField = 'id') + * @method static Comment|Proxy random(array $attributes = []) + * @method static Comment|Proxy randomOrCreate(array $attributes = []) * @method static Comment[]|Proxy[] all() * @method static Comment[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Comment[]|Proxy[] createSequence(array|callable $sequence) diff --git a/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir.php b/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir.php index 17c8d8b75..cd127a9a1 100644 --- a/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir.php @@ -9,14 +9,14 @@ /** * @extends ModelFactory * - * @method Category|Proxy create(array|callable $attributes = []) - * @method static Category|Proxy createOne(array $attributes = []) - * @method static Category|Proxy find(object|array|mixed $criteria) - * @method static Category|Proxy findOrCreate(array $attributes) - * @method static Category|Proxy first(string $sortedField = 'id') - * @method static Category|Proxy last(string $sortedField = 'id') - * @method static Category|Proxy random(array $attributes = []) - * @method static Category|Proxy randomOrCreate(array $attributes = []) + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) + * @method static Category|Proxy find(object|array|mixed $criteria) + * @method static Category|Proxy findOrCreate(array $attributes) + * @method static Category|Proxy first(string $sortedField = 'id') + * @method static Category|Proxy last(string $sortedField = 'id') + * @method static Category|Proxy random(array $attributes = []) + * @method static Category|Proxy randomOrCreate(array $attributes = []) * @method static Category[]|Proxy[] all() * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Category[]|Proxy[] createSequence(array|callable $sequence) diff --git a/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir_interactively.php b/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir_interactively.php index baf3d01d2..061f2e009 100644 --- a/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir_interactively.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_in_test_dir_interactively.php @@ -9,14 +9,14 @@ /** * @extends ModelFactory * - * @method Tag|Proxy create(array|callable $attributes = []) - * @method static Tag|Proxy createOne(array $attributes = []) - * @method static Tag|Proxy find(object|array|mixed $criteria) - * @method static Tag|Proxy findOrCreate(array $attributes) - * @method static Tag|Proxy first(string $sortedField = 'id') - * @method static Tag|Proxy last(string $sortedField = 'id') - * @method static Tag|Proxy random(array $attributes = []) - * @method static Tag|Proxy randomOrCreate(array $attributes = []) + * @method Tag|Proxy create(array|callable $attributes = []) + * @method static Tag|Proxy createOne(array $attributes = []) + * @method static Tag|Proxy find(object|array|mixed $criteria) + * @method static Tag|Proxy findOrCreate(array $attributes) + * @method static Tag|Proxy first(string $sortedField = 'id') + * @method static Tag|Proxy last(string $sortedField = 'id') + * @method static Tag|Proxy random(array $attributes = []) + * @method static Tag|Proxy randomOrCreate(array $attributes = []) * @method static Tag[]|Proxy[] all() * @method static Tag[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Tag[]|Proxy[] createSequence(array|callable $sequence) diff --git a/tests/Fixtures/Maker/expected/can_create_factory_interactively.php b/tests/Fixtures/Maker/expected/can_create_factory_interactively.php index ac8f4f6d6..f80535f5e 100644 --- a/tests/Fixtures/Maker/expected/can_create_factory_interactively.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_interactively.php @@ -9,14 +9,14 @@ /** * @extends ModelFactory * - * @method Tag|Proxy create(array|callable $attributes = []) - * @method static Tag|Proxy createOne(array $attributes = []) - * @method static Tag|Proxy find(object|array|mixed $criteria) - * @method static Tag|Proxy findOrCreate(array $attributes) - * @method static Tag|Proxy first(string $sortedField = 'id') - * @method static Tag|Proxy last(string $sortedField = 'id') - * @method static Tag|Proxy random(array $attributes = []) - * @method static Tag|Proxy randomOrCreate(array $attributes = []) + * @method Tag|Proxy create(array|callable $attributes = []) + * @method static Tag|Proxy createOne(array $attributes = []) + * @method static Tag|Proxy find(object|array|mixed $criteria) + * @method static Tag|Proxy findOrCreate(array $attributes) + * @method static Tag|Proxy first(string $sortedField = 'id') + * @method static Tag|Proxy last(string $sortedField = 'id') + * @method static Tag|Proxy random(array $attributes = []) + * @method static Tag|Proxy randomOrCreate(array $attributes = []) * @method static Tag[]|Proxy[] all() * @method static Tag[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Tag[]|Proxy[] createSequence(array|callable $sequence) diff --git a/tests/Fixtures/Maker/expected/can_create_factory_with_auto_activated_not_persisted_option.php b/tests/Fixtures/Maker/expected/can_create_factory_with_auto_activated_not_persisted_option.php index 0b53f2119..32274c8d9 100644 --- a/tests/Fixtures/Maker/expected/can_create_factory_with_auto_activated_not_persisted_option.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_with_auto_activated_not_persisted_option.php @@ -9,8 +9,8 @@ /** * @extends ModelFactory * - * @method Category|Proxy create(array|callable $attributes = []) - * @method static Category|Proxy createOne(array $attributes = []) + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Category[]|Proxy[] createSequence(array|callable $sequence) */ diff --git a/tests/Fixtures/Maker/expected/can_create_factory_with_phpstan_annotations.php b/tests/Fixtures/Maker/expected/can_create_factory_with_phpstan_annotations.php index 87ce43907..c7ae26f67 100644 --- a/tests/Fixtures/Maker/expected/can_create_factory_with_phpstan_annotations.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_with_phpstan_annotations.php @@ -9,14 +9,14 @@ /** * @extends ModelFactory * - * @method Category|Proxy create(array|callable $attributes = []) - * @method static Category|Proxy createOne(array $attributes = []) - * @method static Category|Proxy find(object|array|mixed $criteria) - * @method static Category|Proxy findOrCreate(array $attributes) - * @method static Category|Proxy first(string $sortedField = 'id') - * @method static Category|Proxy last(string $sortedField = 'id') - * @method static Category|Proxy random(array $attributes = []) - * @method static Category|Proxy randomOrCreate(array $attributes = []) + * @method Category|Proxy create(array|callable $attributes = []) + * @method static Category|Proxy createOne(array $attributes = []) + * @method static Category|Proxy find(object|array|mixed $criteria) + * @method static Category|Proxy findOrCreate(array $attributes) + * @method static Category|Proxy first(string $sortedField = 'id') + * @method static Category|Proxy last(string $sortedField = 'id') + * @method static Category|Proxy random(array $attributes = []) + * @method static Category|Proxy randomOrCreate(array $attributes = []) * @method static Category[]|Proxy[] all() * @method static Category[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static Category[]|Proxy[] createSequence(array|callable $sequence) @@ -24,14 +24,14 @@ * @method static Category[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) * @method static Category[]|Proxy[] randomSet(int $number, array $attributes = []) * - * @phpstan-method Proxy create(array|callable $attributes = []) - * @phpstan-method static Proxy createOne(array $attributes = []) - * @phpstan-method static Proxy find(object|array|mixed $criteria) - * @phpstan-method static Proxy findOrCreate(array $attributes) - * @phpstan-method static Proxy first(string $sortedField = 'id') - * @phpstan-method static Proxy last(string $sortedField = 'id') - * @phpstan-method static Proxy random(array $attributes = []) - * @phpstan-method static Proxy randomOrCreate(array $attributes = []) + * @phpstan-method Proxy create(array|callable $attributes = []) + * @phpstan-method static Proxy createOne(array $attributes = []) + * @phpstan-method static Proxy find(object|array|mixed $criteria) + * @phpstan-method static Proxy findOrCreate(array $attributes) + * @phpstan-method static Proxy first(string $sortedField = 'id') + * @phpstan-method static Proxy last(string $sortedField = 'id') + * @phpstan-method static Proxy random(array $attributes = []) + * @phpstan-method static Proxy randomOrCreate(array $attributes = []) * @phpstan-method static list> all() * @phpstan-method static list> createMany(int $number, array|callable $attributes = []) * @phpstan-method static list> createSequence(array|callable $sequence) diff --git a/tests/Fixtures/Maker/expected/can_create_factory_with_relation_defaults.php b/tests/Fixtures/Maker/expected/can_create_factory_with_relation_defaults.php index eebe3dc99..9478cb701 100644 --- a/tests/Fixtures/Maker/expected/can_create_factory_with_relation_defaults.php +++ b/tests/Fixtures/Maker/expected/can_create_factory_with_relation_defaults.php @@ -5,18 +5,19 @@ use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Proxy; use Zenstruck\Foundry\Tests\Fixtures\Entity\EntityWithRelations; +use Zenstruck\Foundry\Tests\Fixtures\Factories\CategoryFactory; /** * @extends ModelFactory * - * @method EntityWithRelations|Proxy create(array|callable $attributes = []) - * @method static EntityWithRelations|Proxy createOne(array $attributes = []) - * @method static EntityWithRelations|Proxy find(object|array|mixed $criteria) - * @method static EntityWithRelations|Proxy findOrCreate(array $attributes) - * @method static EntityWithRelations|Proxy first(string $sortedField = 'id') - * @method static EntityWithRelations|Proxy last(string $sortedField = 'id') - * @method static EntityWithRelations|Proxy random(array $attributes = []) - * @method static EntityWithRelations|Proxy randomOrCreate(array $attributes = []) + * @method EntityWithRelations|Proxy create(array|callable $attributes = []) + * @method static EntityWithRelations|Proxy createOne(array $attributes = []) + * @method static EntityWithRelations|Proxy find(object|array|mixed $criteria) + * @method static EntityWithRelations|Proxy findOrCreate(array $attributes) + * @method static EntityWithRelations|Proxy first(string $sortedField = 'id') + * @method static EntityWithRelations|Proxy last(string $sortedField = 'id') + * @method static EntityWithRelations|Proxy random(array $attributes = []) + * @method static EntityWithRelations|Proxy randomOrCreate(array $attributes = []) * @method static EntityWithRelations[]|Proxy[] all() * @method static EntityWithRelations[]|Proxy[] createMany(int $number, array|callable $attributes = []) * @method static EntityWithRelations[]|Proxy[] createSequence(array|callable $sequence) @@ -44,9 +45,9 @@ public function __construct() protected function getDefaults(): array { return [ - 'manyToOne' => \Zenstruck\Foundry\Tests\Fixtures\Factories\CategoryFactory::new(), + 'manyToOne' => CategoryFactory::new(), 'manyToOneWithNotExistingFactory' => null, // TODO add Zenstruck\Foundry\Tests\Fixtures\Entity\Cascade\Brand ORM type manually - 'oneToOne' => \Zenstruck\Foundry\Tests\Fixtures\Factories\CategoryFactory::new(), + 'oneToOne' => CategoryFactory::new(), ]; } diff --git a/tests/Functional/Bundle/Maker/MakeFactoryTest.php b/tests/Functional/Bundle/Maker/MakeFactoryTest.php index 8f780b67b..8737ea1ee 100644 --- a/tests/Functional/Bundle/Maker/MakeFactoryTest.php +++ b/tests/Functional/Bundle/Maker/MakeFactoryTest.php @@ -6,9 +6,10 @@ use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; use Symfony\Component\Console\Tester\CommandTester; use Zenstruck\Foundry\Tests\Fixtures\Document\Comment; -use Zenstruck\Foundry\Tests\Fixtures\Document\Post; +use Zenstruck\Foundry\Tests\Fixtures\Document\Post as ODMPost; use Zenstruck\Foundry\Tests\Fixtures\Entity\Category; use Zenstruck\Foundry\Tests\Fixtures\Entity\EntityWithRelations; +use Zenstruck\Foundry\Tests\Fixtures\Entity\Post as ORMPost; use Zenstruck\Foundry\Tests\Fixtures\Entity\Tag; use Zenstruck\Foundry\Tests\Fixtures\Kernel; use Zenstruck\Foundry\Tests\Fixtures\Object\SomeObject; @@ -124,8 +125,7 @@ public function can_create_factory_with_phpstan_annotations(): void self::markTestSkipped('doctrine/orm not enabled.'); } - \mkdir(\dirname(self::PHPSTAN_PATH), 0777, true); - \touch(self::PHPSTAN_PATH); + $this->emulatePHPStanEnabled(); $tester = new CommandTester((new Application(self::bootKernel()))->find('make:factory')); @@ -136,6 +136,26 @@ public function can_create_factory_with_phpstan_annotations(): void $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/CategoryFactory.php')); } + /** + * @test + */ + public function can_create_factory_for_entity_with_repository(): void + { + if (!\getenv('USE_ORM')) { + self::markTestSkipped('doctrine/orm not enabled.'); + } + + $this->emulatePHPStanEnabled(); + + $tester = new CommandTester((new Application(self::bootKernel()))->find('make:factory')); + + $this->assertFileDoesNotExist(self::tempFile('src/Factory/PostFactory.php')); + + $tester->execute(['class' => ORMPost::class]); + + $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/PostFactory.php')); + } + /** * @test */ @@ -320,7 +340,7 @@ public function can_create_factory_for_odm(string $class, string $file): void public function documentProvider(): iterable { - yield 'document' => [Post::class, 'PostFactory']; + yield 'document' => [ODMPost::class, 'PostFactory']; yield 'embedded document' => [Comment::class, 'CommentFactory']; } @@ -364,4 +384,10 @@ public function can_create_factory_with_relation_defaults(): void $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/EntityWithRelationsFactory.php')); } + + private function emulatePHPStanEnabled(): void + { + \mkdir(\dirname(self::PHPSTAN_PATH), 0777, true); + \touch(self::PHPSTAN_PATH); + } } From d8eca8887a794478f4010ee16bfc00780f2c9226 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Thu, 24 Nov 2022 10:05:55 -0500 Subject: [PATCH 25/30] chore(ci): test on Symfony 6.2 (#359) --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ab78263b..ae357d4f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: php: [8.0, 8.1] - symfony: [4.4.*, 5.4.*, 6.0.*, 6.1.*] + symfony: [4.4.*, 5.4.*, 6.0.*, 6.1.*, 6.2.*] deps: [highest] use-orm: [1] use-odm: [1] @@ -24,6 +24,7 @@ jobs: - {use-orm: 0, use-dama: 1} # cannot happen # conflicts - {php: 8.0, symfony: 6.1.*} + - {php: 8.0, symfony: 6.2.*} include: - {php: 8.0, symfony: 4.4.*, use-orm: 1, use-odm: 0, use-dama: 0, deps: lowest} - {php: 8.0, symfony: 4.4.*, use-orm: 1, use-odm: 1, use-dama: 0, deps: lowest} From a003bac5f563e8ad4ec2dd9bea508d3197013c59 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Thu, 24 Nov 2022 18:29:58 +0100 Subject: [PATCH 26/30] refactor(make:factory): split command with DefaultPropertiesGuesser (#357) --- .../Factory/DefaultPropertiesGuesser.php | 13 ++ ...DoctrineObjectDefaultPropertiesGuesser.php | 121 ++++++++++++ src/Bundle/Maker/Factory/FactoryFinder.php | 53 +++++ .../Maker/{ => Factory}/MakeFactoryData.php | 2 +- .../{ => Factory}/MakeFactoryPHPDocMethod.php | 2 +- .../ObjectDefaultPropertiesGuesser.php | 48 +++++ src/Bundle/Maker/MakeFactory.php | 182 +++--------------- src/Bundle/Resources/config/services.xml | 18 +- src/Bundle/Resources/skeleton/Factory.tpl.php | 2 +- src/Test/GlobalStateRegistry.php | 2 - 10 files changed, 278 insertions(+), 165 deletions(-) create mode 100644 src/Bundle/Maker/Factory/DefaultPropertiesGuesser.php create mode 100644 src/Bundle/Maker/Factory/DoctrineObjectDefaultPropertiesGuesser.php create mode 100644 src/Bundle/Maker/Factory/FactoryFinder.php rename src/Bundle/Maker/{ => Factory}/MakeFactoryData.php (98%) rename src/Bundle/Maker/{ => Factory}/MakeFactoryPHPDocMethod.php (98%) create mode 100644 src/Bundle/Maker/Factory/ObjectDefaultPropertiesGuesser.php diff --git a/src/Bundle/Maker/Factory/DefaultPropertiesGuesser.php b/src/Bundle/Maker/Factory/DefaultPropertiesGuesser.php new file mode 100644 index 000000000..a5476529e --- /dev/null +++ b/src/Bundle/Maker/Factory/DefaultPropertiesGuesser.php @@ -0,0 +1,13 @@ + '[],', + 'ASCII_STRING' => 'self::faker()->text({length}),', + 'BIGINT' => 'self::faker()->randomNumber(),', + 'BLOB' => 'self::faker()->text(),', + 'BOOLEAN' => 'self::faker()->boolean(),', + 'DATE' => 'self::faker()->dateTime(),', + 'DATE_MUTABLE' => 'self::faker()->dateTime(),', + 'DATE_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', + 'DATETIME_MUTABLE' => 'self::faker()->dateTime(),', + 'DATETIME_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', + 'DATETIMETZ_MUTABLE' => 'self::faker()->dateTime(),', + 'DATETIMETZ_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', + 'DECIMAL' => 'self::faker()->randomFloat(),', + 'FLOAT' => 'self::faker()->randomFloat(),', + 'INTEGER' => 'self::faker()->randomNumber(),', + 'JSON' => '[],', + 'JSON_ARRAY' => '[],', + 'SIMPLE_ARRAY' => '[],', + 'SMALLINT' => 'self::faker()->numberBetween(1, 32767),', + 'STRING' => 'self::faker()->text({length}),', + 'TEXT' => 'self::faker()->text({length}),', + 'TIME_MUTABLE' => 'self::faker()->datetime(),', + 'TIME_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->datetime()),', + ]; + + public function __construct(private ManagerRegistry $managerRegistry, private FactoryFinder $factoryFinder) + { + } + + public function __invoke(MakeFactoryData $makeFactoryData, bool $allFields): void + { + $class = $makeFactoryData->getObjectFullyQualifiedClassName(); + + $em = $this->managerRegistry->getManagerForClass($class); + + if (!$em instanceof ObjectManager) { + return; + } + + /** @var ORMClassMetadata|ODMClassMetadata $metadata */ + $metadata = $em->getClassMetadata($class); + + $dbType = $em instanceof EntityManagerInterface ? 'ORM' : 'ODM'; + + $this->guessDefaultValueForAssociativeFields($makeFactoryData, $metadata, $dbType, $allFields); + $this->guessDefaultValueForScalarFields($makeFactoryData, $metadata, $dbType, $allFields); + } + + public function supports(bool $persisted): bool + { + return true === $persisted; + } + + private function guessDefaultValueForAssociativeFields(MakeFactoryData $makeFactoryData, ODMClassMetadata|ORMClassMetadata $metadata, string $dbType, bool $allFields): void + { + foreach ($metadata->associationMappings as $item) { + // if joinColumns is not written entity is default nullable ($nullable = true;) + if (!\array_key_exists('joinColumns', $item)) { + continue; + } + + if (!\array_key_exists('nullable', $item['joinColumns'][0] ?? [])) { + continue; + } + + if (true === $item['joinColumns'][0]['nullable']) { + continue; + } + + $fieldName = $item['fieldName']; + + if (!$factoryClass = $this->factoryFinder->getFactoryForClass($item['targetEntity'])) { + $makeFactoryData->addDefaultProperty(\lcfirst($fieldName), "null, // TODO add {$item['targetEntity']} {$dbType} type manually"); + + continue; + } + + $factory = new \ReflectionClass($factoryClass); + $makeFactoryData->addUse($factory->getName()); + $makeFactoryData->addDefaultProperty(\lcfirst($fieldName), "{$factory->getShortName()}::new(),"); + } + } + + private function guessDefaultValueForScalarFields(MakeFactoryData $makeFactoryData, ODMClassMetadata|ORMClassMetadata $metadata, string $dbType, bool $allFields): void + { + $ids = $metadata->getIdentifierFieldNames(); + + foreach ($metadata->fieldMappings as $property) { + // ignore identifiers and nullable fields + if ((!$allFields && ($property['nullable'] ?? false)) || \in_array($property['fieldName'], $ids, true)) { + continue; + } + + $type = \mb_strtoupper($property['type']); + $value = "null, // TODO add {$type} {$dbType} type manually"; + $length = $property['length'] ?? ''; + + if (\array_key_exists($type, self::DEFAULTS)) { + $value = self::DEFAULTS[$type]; + } + + $makeFactoryData->addDefaultProperty($property['fieldName'], \str_replace('{length}', (string) $length, $value)); + } + } +} diff --git a/src/Bundle/Maker/Factory/FactoryFinder.php b/src/Bundle/Maker/Factory/FactoryFinder.php new file mode 100644 index 000000000..2e81bc1f6 --- /dev/null +++ b/src/Bundle/Maker/Factory/FactoryFinder.php @@ -0,0 +1,53 @@ + factory classes as keys, object class as values + */ + private array $classesWithFactories; + + /** @param \Traversable $factories */ + public function __construct(\Traversable $factories) + { + /** @phpstan-ignore-next-line */ + $this->classesWithFactories = \array_unique( + \array_reduce( + \iterator_to_array($factories, preserve_keys: true), + static function(array $carry, ModelFactory $factory): array { + $carry[\get_class($factory)] = $factory::getEntityClass(); + + return $carry; + }, + [] + ) + ); + } + + /** @param class-string $class */ + public function classHasFactory(string $class): bool + { + return \in_array($class, $this->classesWithFactories, true); + } + + /** + * @param class-string $class + * + * @return class-string|null + */ + public function getFactoryForClass(string $class): ?string + { + $factories = \array_flip($this->classesWithFactories); + + return $factories[$class] ?? null; + } +} diff --git a/src/Bundle/Maker/MakeFactoryData.php b/src/Bundle/Maker/Factory/MakeFactoryData.php similarity index 98% rename from src/Bundle/Maker/MakeFactoryData.php rename to src/Bundle/Maker/Factory/MakeFactoryData.php index eeb3dc55b..d255b2c2b 100644 --- a/src/Bundle/Maker/MakeFactoryData.php +++ b/src/Bundle/Maker/Factory/MakeFactoryData.php @@ -1,6 +1,6 @@ '[],', + 'string' => 'self::faker()->sentence(),', + 'int' => 'self::faker()->randomNumber(),', + 'float' => 'self::faker()->randomFloat(),', + 'bool' => 'self::faker()->boolean(),', + \DateTime::class => 'self::faker()->dateTime(),', + \DateTimeImmutable::class => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', + ]; + + public function __invoke(MakeFactoryData $makeFactoryData, bool $allFields): void + { + foreach ($makeFactoryData->getObject()->getProperties() as $property) { + // ignore identifiers and nullable fields + if (!$allFields && ($property->hasDefaultValue() || !$property->hasType() || $property->getType()?->allowsNull())) { + continue; + } + + $type = null; + $reflectionType = $property->getType(); + if ($reflectionType instanceof \ReflectionNamedType) { + $type = $reflectionType->getName(); + } + + $value = \sprintf('null, // TODO add %svalue manually', $type ? "{$type} " : ''); + + if (\array_key_exists($type ?? '', self::DEFAULTS_FOR_NOT_PERSISTED)) { + $value = self::DEFAULTS_FOR_NOT_PERSISTED[$type]; + } + + $makeFactoryData->addDefaultProperty($property->getName(), $value); + } + } + + public function supports(bool $persisted): bool + { + return false === $persisted; + } +} diff --git a/src/Bundle/Maker/MakeFactory.php b/src/Bundle/Maker/MakeFactory.php index 0121aa7e9..63ab793e5 100644 --- a/src/Bundle/Maker/MakeFactory.php +++ b/src/Bundle/Maker/MakeFactory.php @@ -2,11 +2,7 @@ namespace Zenstruck\Foundry\Bundle\Maker; -use Doctrine\ODM\MongoDB\Mapping\ClassMetadata as ODMClassMetadata; -use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Mapping\ClassMetadataInfo as ORMClassMetadata; use Doctrine\Persistence\ManagerRegistry; -use Doctrine\Persistence\ObjectManager; use Symfony\Bundle\MakerBundle\ConsoleStyle; use Symfony\Bundle\MakerBundle\DependencyBuilder; use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; @@ -18,66 +14,18 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\HttpKernel\KernelInterface; -use Zenstruck\Foundry\ModelFactory; +use Zenstruck\Foundry\Bundle\Maker\Factory\DefaultPropertiesGuesser; +use Zenstruck\Foundry\Bundle\Maker\Factory\FactoryFinder; +use Zenstruck\Foundry\Bundle\Maker\Factory\MakeFactoryData; /** * @author Kevin Bond */ final class MakeFactory extends AbstractMaker { - private const DEFAULTS_FOR_PERSISTED = [ - 'ARRAY' => '[],', - 'ASCII_STRING' => 'self::faker()->text({length}),', - 'BIGINT' => 'self::faker()->randomNumber(),', - 'BLOB' => 'self::faker()->text(),', - 'BOOLEAN' => 'self::faker()->boolean(),', - 'DATE' => 'self::faker()->dateTime(),', - 'DATE_MUTABLE' => 'self::faker()->dateTime(),', - 'DATE_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', - 'DATETIME_MUTABLE' => 'self::faker()->dateTime(),', - 'DATETIME_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', - 'DATETIMETZ_MUTABLE' => 'self::faker()->dateTime(),', - 'DATETIMETZ_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', - 'DECIMAL' => 'self::faker()->randomFloat(),', - 'FLOAT' => 'self::faker()->randomFloat(),', - 'INTEGER' => 'self::faker()->randomNumber(),', - 'JSON' => '[],', - 'JSON_ARRAY' => '[],', - 'SIMPLE_ARRAY' => '[],', - 'SMALLINT' => 'self::faker()->numberBetween(1, 32767),', - 'STRING' => 'self::faker()->text({length}),', - 'TEXT' => 'self::faker()->text({length}),', - 'TIME_MUTABLE' => 'self::faker()->datetime(),', - 'TIME_IMMUTABLE' => '\DateTimeImmutable::createFromMutable(self::faker()->datetime()),', - ]; - - private const DEFAULTS_FOR_NOT_PERSISTED = [ - 'array' => '[],', - 'string' => 'self::faker()->sentence(),', - 'int' => 'self::faker()->randomNumber(),', - 'float' => 'self::faker()->randomFloat(),', - 'bool' => 'self::faker()->boolean(),', - \DateTime::class => 'self::faker()->dateTime(),', - \DateTimeImmutable::class => '\DateTimeImmutable::createFromMutable(self::faker()->dateTime()),', - ]; - - /** @var array */ - private array $entitiesWithFactories; - - public function __construct(private ManagerRegistry $managerRegistry, \Traversable $factories, private string $projectDir, private KernelInterface $kernel) + /** @param \Traversable $defaultPropertiesGuessers */ + public function __construct(private ManagerRegistry $managerRegistry, private FactoryFinder $factoryFinder, private KernelInterface $kernel, private \Traversable $defaultPropertiesGuessers) { - /** @phpstan-ignore-next-line */ - $this->entitiesWithFactories = \array_unique( - \array_reduce( - \iterator_to_array($factories, preserve_keys: true), - static function(array $carry, ModelFactory $factory): array { - $carry[\get_class($factory)] = $factory::getEntityClass(); - - return $carry; - }, - [] - ) - ); } public static function getCommandName(): string @@ -175,7 +123,7 @@ private function generateFactory(string $class, InputInterface $input, ConsoleSt throw new RuntimeCommandException(\sprintf('Class "%s" not found.', $input->getArgument('class'))); } - $makeFactoryData = $this->createMakeFactoryData($generator, $class, !$input->getOption('not-persisted')); + $makeFactoryData = $this->createMakeFactoryData($class, !$input->getOption('not-persisted')); $factory = $generator->createClassNameDetails( $makeFactoryData->getObjectShortName(), @@ -183,9 +131,10 @@ private function generateFactory(string $class, InputInterface $input, ConsoleSt 'Factory' ); - $input->getOption('not-persisted') - ? $this->defaultPropertiesForNotPersistedObject($makeFactoryData, $input->getOption('all-fields')) - : $this->defaultPropertiesForPersistedObject($makeFactoryData, $input->getOption('all-fields')); + $this->defaultPropertiesGuesser(!$input->getOption('not-persisted'))( + $makeFactoryData, + $input->getOption('all-fields') + ); $generator->generateClass( $factory->getFullName(), @@ -218,7 +167,7 @@ private function entityChoices(): array continue; } - if (!\in_array($metadata->getName(), $this->entitiesWithFactories, true)) { + if (!$this->factoryFinder->classHasFactory($metadata->getName())) { $choices[] = $metadata->getName(); } } @@ -233,95 +182,9 @@ private function entityChoices(): array return $choices; } - private function defaultPropertiesForPersistedObject(MakeFactoryData $makeFactoryData, bool $allFields): void - { - $class = $makeFactoryData->getObjectFullyQualifiedClassName(); - - $em = $this->managerRegistry->getManagerForClass($class); - - if (!$em instanceof ObjectManager) { - return; - } - - /** @var ORMClassMetadata|ODMClassMetadata $metadata */ - $metadata = $em->getClassMetadata($class); - $ids = $metadata->getIdentifierFieldNames(); - - $dbType = $em instanceof EntityManagerInterface ? 'ORM' : 'ODM'; - - // If Factory exist for related entities populate too with auto defaults - foreach ($metadata->associationMappings as $item) { - // if joinColumns is not written entity is default nullable ($nullable = true;) - if (!\array_key_exists('joinColumns', $item)) { - continue; - } - - if (!\array_key_exists('nullable', $item['joinColumns'][0] ?? [])) { - continue; - } - - if (true === $item['joinColumns'][0]['nullable']) { - continue; - } - - $fieldName = $item['fieldName']; - - if (!$factoryClass = $this->getFactoryForClass($item['targetEntity'])) { - $makeFactoryData->addDefaultProperty(\lcfirst($fieldName), "null, // TODO add {$item['targetEntity']} {$dbType} type manually"); - - continue; - } - - $factory = new \ReflectionClass($factoryClass); - $makeFactoryData->addUse($factory->getName()); - $makeFactoryData->addDefaultProperty(\lcfirst($fieldName), "{$factory->getShortName()}::new(),"); - } - - foreach ($metadata->fieldMappings as $property) { - // ignore identifiers and nullable fields - if ((!$allFields && ($property['nullable'] ?? false)) || \in_array($property['fieldName'], $ids, true)) { - continue; - } - - $type = \mb_strtoupper($property['type']); - $value = "null, // TODO add {$type} {$dbType} type manually"; - $length = $property['length'] ?? ''; - - if (\array_key_exists($type, self::DEFAULTS_FOR_PERSISTED)) { - $value = self::DEFAULTS_FOR_PERSISTED[$type]; - } - - $makeFactoryData->addDefaultProperty($property['fieldName'], \str_replace('{length}', (string) $length, $value)); - } - } - - private function defaultPropertiesForNotPersistedObject(MakeFactoryData $makeFactoryData, bool $allFields): void - { - foreach ($makeFactoryData->getObject()->getProperties() as $property) { - // ignore identifiers and nullable fields - if (!$allFields && ($property->hasDefaultValue() || !$property->hasType() || $property->getType()?->allowsNull())) { - continue; - } - - $type = null; - $reflectionType = $property->getType(); - if ($reflectionType instanceof \ReflectionNamedType) { - $type = $reflectionType->getName(); - } - - $value = \sprintf('null, // TODO add %svalue manually', $type ? "{$type} " : ''); - - if (\array_key_exists($type ?? '', self::DEFAULTS_FOR_NOT_PERSISTED)) { - $value = self::DEFAULTS_FOR_NOT_PERSISTED[$type]; - } - - $makeFactoryData->addDefaultProperty($property->getName(), $value); - } - } - private function phpstanEnabled(): bool { - return \file_exists("{$this->projectDir}/vendor/phpstan/phpstan/phpstan"); + return \file_exists("{$this->kernel->getProjectDir()}/vendor/phpstan/phpstan/phpstan"); } private function doctrineEnabled(): bool @@ -345,18 +208,10 @@ private function doctrineEnabled(): bool return $ormEnabled || $odmEnabled; } - /** @return class-string|null */ - private function getFactoryForClass(string $class): ?string - { - $factories = \array_flip($this->entitiesWithFactories); - - return $factories[$class] ?? null; - } - /** * @param class-string $class */ - private function createMakeFactoryData(Generator $generator, string $class, bool $persisted): MakeFactoryData + private function createMakeFactoryData(string $class, bool $persisted): MakeFactoryData { $object = new \ReflectionClass($class); @@ -388,4 +243,15 @@ private function guessNamespace(Generator $generator, string $namespace, bool $t return $namespace; } + + private function defaultPropertiesGuesser(bool $persisted): DefaultPropertiesGuesser + { + foreach ($this->defaultPropertiesGuessers as $defaultPropertiesGuesser) { + if ($defaultPropertiesGuesser->supports($persisted)) { + return $defaultPropertiesGuesser; + } + } + + throw new \LogicException(\sprintf('Cannot find default properties guesser based ($persisted: %s)', $persisted ? 'true' : 'false')); + } } diff --git a/src/Bundle/Resources/config/services.xml b/src/Bundle/Resources/config/services.xml index 1a26ac5c7..b569912f8 100644 --- a/src/Bundle/Resources/config/services.xml +++ b/src/Bundle/Resources/config/services.xml @@ -42,12 +42,22 @@ - - %kernel.project_dir% + + + + + + + + + + + + @@ -58,5 +68,9 @@ + + + + diff --git a/src/Bundle/Resources/skeleton/Factory.tpl.php b/src/Bundle/Resources/skeleton/Factory.tpl.php index 34fa98d2f..be71e17b9 100644 --- a/src/Bundle/Resources/skeleton/Factory.tpl.php +++ b/src/Bundle/Resources/skeleton/Factory.tpl.php @@ -1,4 +1,4 @@ - + namespace ; diff --git a/src/Test/GlobalStateRegistry.php b/src/Test/GlobalStateRegistry.php index ae736c01f..89e55d4e3 100644 --- a/src/Test/GlobalStateRegistry.php +++ b/src/Test/GlobalStateRegistry.php @@ -1,7 +1,5 @@ Date: Thu, 24 Nov 2022 13:05:16 -0500 Subject: [PATCH 27/30] feat: add `RepositoryProxy::inner()` (#362) --- docs/index.rst | 1 + src/RepositoryProxy.php | 8 ++++++++ tests/Unit/RepositoryProxyTest.php | 12 ++++++++++++ 3 files changed, 21 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index e52ee693d..60c9c569f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1353,6 +1353,7 @@ This library provides a *Repository Proxy* that wraps your object repositories t $repository = repository(Post::class); // helpful methods - all returned object(s) are proxied + $repository->inner(); // the real "wrapped" repository $repository->count(); // number of rows in the database table count($repository); // equivalent to above (RepositoryProxy implements \Countable) $repository->first(); // get the first object (assumes an auto-incremented "id" column) diff --git a/src/RepositoryProxy.php b/src/RepositoryProxy.php index aec429187..a9afe16e3 100644 --- a/src/RepositoryProxy.php +++ b/src/RepositoryProxy.php @@ -30,6 +30,14 @@ public function __call(string $method, array $arguments) return $this->proxyResult($this->repository->{$method}(...$arguments)); } + /** + * @return ObjectRepository + */ + public function inner(): ObjectRepository + { + return $this->repository; + } + public function count(): int { if ($this->repository instanceof EntityRepository) { diff --git a/tests/Unit/RepositoryProxyTest.php b/tests/Unit/RepositoryProxyTest.php index c3fcf13c1..9c951cc37 100644 --- a/tests/Unit/RepositoryProxyTest.php +++ b/tests/Unit/RepositoryProxyTest.php @@ -50,6 +50,18 @@ public function findOneBy(array $criteria, ?string $orderBy = null): void } })]; } + + /** + * @test + */ + public function can_get_inner_repository(): void + { + $inner = $this->createMock(ObjectRepository::class); + + $repository = new RepositoryProxy($inner); + + $this->assertSame($inner, $repository->inner()); + } } abstract class RepositoryStub implements ObjectRepository From 730c0d9a53d50f493cae8870ad81fc01c1673828 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Thu, 24 Nov 2022 13:33:45 -0500 Subject: [PATCH 28/30] chore: rename service ids (#363) --- .../ChainManagerRegistryPass.php | 5 +- .../DependencyInjection/GlobalStatePass.php | 6 +- .../ZenstruckFoundryExtension.php | 19 +++--- src/Bundle/Resources/config/services.xml | 43 ++++++------ src/Test/DatabaseResetter.php | 6 +- src/Test/ResetDatabase.php | 4 +- src/Test/TestState.php | 4 +- src/ZenstruckFoundryBundle.php | 2 +- .../ChainManagerRegistryPassTest.php | 9 ++- .../ZenstruckFoundryExtensionTest.php | 68 +++++++++---------- 10 files changed, 77 insertions(+), 89 deletions(-) diff --git a/src/Bundle/DependencyInjection/ChainManagerRegistryPass.php b/src/Bundle/DependencyInjection/ChainManagerRegistryPass.php index b9517d415..4b65944ec 100644 --- a/src/Bundle/DependencyInjection/ChainManagerRegistryPass.php +++ b/src/Bundle/DependencyInjection/ChainManagerRegistryPass.php @@ -5,13 +5,12 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; -use Zenstruck\Foundry\ChainManagerRegistry; final class ChainManagerRegistryPass implements CompilerPassInterface { public function process(ContainerBuilder $container): void { - if (!$container->hasDefinition(ChainManagerRegistry::class)) { + if (!$container->hasDefinition('.zenstruck_foundry.chain_manager_registry')) { return; } @@ -25,7 +24,7 @@ public function process(ContainerBuilder $container): void $managerRegistries[] = new Reference('doctrine_mongodb'); } - $container->getDefinition(ChainManagerRegistry::class) + $container->getDefinition('.zenstruck_foundry.chain_manager_registry') ->setArgument('$managerRegistries', $managerRegistries) ; } diff --git a/src/Bundle/DependencyInjection/GlobalStatePass.php b/src/Bundle/DependencyInjection/GlobalStatePass.php index b46841c67..230f5f7fc 100644 --- a/src/Bundle/DependencyInjection/GlobalStatePass.php +++ b/src/Bundle/DependencyInjection/GlobalStatePass.php @@ -6,19 +6,17 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; -use Zenstruck\Foundry\Configuration as FoundryConfiguration; use Zenstruck\Foundry\Story; -use Zenstruck\Foundry\Test\GlobalStateRegistry; final class GlobalStatePass implements CompilerPassInterface { public function process(ContainerBuilder $container): void { - if (!$container->hasDefinition(FoundryConfiguration::class)) { + if (!$container->hasDefinition('.zenstruck_foundry.configuration')) { return; } - $globalStateRegistryDefinition = $container->getDefinition(GlobalStateRegistry::class); + $globalStateRegistryDefinition = $container->getDefinition('.zenstruck_foundry.global_state_registry'); foreach ($this->getBundleConfiguration($container)['global_state'] as $globalStateItem) { if ($this->isStoryAsService($container, $globalStateItem)) { diff --git a/src/Bundle/DependencyInjection/ZenstruckFoundryExtension.php b/src/Bundle/DependencyInjection/ZenstruckFoundryExtension.php index 00be6bde9..aac20249d 100644 --- a/src/Bundle/DependencyInjection/ZenstruckFoundryExtension.php +++ b/src/Bundle/DependencyInjection/ZenstruckFoundryExtension.php @@ -11,7 +11,6 @@ use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension; use Zenstruck\Foundry\Bundle\Command\StubMakeFactory; use Zenstruck\Foundry\Bundle\Command\StubMakeStory; -use Zenstruck\Foundry\Configuration; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Story; use Zenstruck\Foundry\Test\ORMDatabaseResetter; @@ -40,26 +39,26 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container $this->configureDatabaseResetter($mergedConfig['database_resetter'], $container); if (true === $mergedConfig['auto_refresh_proxies']) { - $container->getDefinition(Configuration::class)->addMethodCall('enableDefaultProxyAutoRefresh'); + $container->getDefinition('.zenstruck_foundry.configuration')->addMethodCall('enableDefaultProxyAutoRefresh'); } elseif (false === $mergedConfig['auto_refresh_proxies']) { - $container->getDefinition(Configuration::class)->addMethodCall('disableDefaultProxyAutoRefresh'); + $container->getDefinition('.zenstruck_foundry.configuration')->addMethodCall('disableDefaultProxyAutoRefresh'); } if (!\class_exists(AbstractMaker::class)) { - $container->register(StubMakeFactory::class)->addTag('console.command'); - $container->register(StubMakeStory::class)->addTag('console.command'); + $container->register('.zenstruck_foundry.maker.factory_stub', StubMakeFactory::class)->addTag('console.command'); + $container->register('.zenstruck_foundry.maker.story_stub', StubMakeStory::class)->addTag('console.command'); } } private function configureFaker(array $config, ContainerBuilder $container): void { if ($config['service']) { - $container->setAlias('zenstruck_foundry.faker', $config['service']); + $container->setAlias('.zenstruck_foundry.faker', $config['service']); return; } - $definition = $container->getDefinition('zenstruck_foundry.faker'); + $definition = $container->getDefinition('.zenstruck_foundry.faker'); if ($config['locale']) { $definition->addArgument($config['locale']); @@ -73,12 +72,12 @@ private function configureFaker(array $config, ContainerBuilder $container): voi private function configureDefaultInstantiator(array $config, ContainerBuilder $container): void { if ($config['service']) { - $container->setAlias('zenstruck_foundry.default_instantiator', $config['service']); + $container->setAlias('.zenstruck_foundry.default_instantiator', $config['service']); return; } - $definition = $container->getDefinition('zenstruck_foundry.default_instantiator'); + $definition = $container->getDefinition('.zenstruck_foundry.default_instantiator'); if ($config['without_constructor']) { $definition->addMethodCall('withoutConstructor'); @@ -95,7 +94,7 @@ private function configureDefaultInstantiator(array $config, ContainerBuilder $c private function configureDatabaseResetter(array $config, ContainerBuilder $container): void { - $configurationDefinition = $container->getDefinition(Configuration::class); + $configurationDefinition = $container->getDefinition('.zenstruck_foundry.configuration'); if (false === $config['enabled']) { $configurationDefinition->addMethodCall('disableDatabaseReset'); diff --git a/src/Bundle/Resources/config/services.xml b/src/Bundle/Resources/config/services.xml index b569912f8..0990cb5c9 100644 --- a/src/Bundle/Resources/config/services.xml +++ b/src/Bundle/Resources/config/services.xml @@ -5,71 +5,70 @@ https://symfony.com/schema/dic/services/services-1.0.xsd"> - - + + - + - + - + - + - + - + - + - + - - - + + + - - - + + + - + - + - + - - + - + diff --git a/src/Test/DatabaseResetter.php b/src/Test/DatabaseResetter.php index 85e6cb804..d557b84da 100644 --- a/src/Test/DatabaseResetter.php +++ b/src/Test/DatabaseResetter.php @@ -82,7 +82,7 @@ private static function bootFoundry(KernelInterface $kernel): void } TestState::flushGlobalState( - $container->has(GlobalStateRegistry::class) ? $container->get(GlobalStateRegistry::class) : null + $container->has('.zenstruck_foundry.global_state_registry') ? $container->get('.zenstruck_foundry.global_state_registry') : null ); } @@ -122,8 +122,8 @@ private static function createODMSchemaResetter(Application $application, Kernel private static function getConfiguration(ContainerInterface $container): ?Configuration { - if ($container->has(Configuration::class)) { - return $container->get(Configuration::class); + if ($container->has('.zenstruck_foundry.configuration')) { + return $container->get('.zenstruck_foundry.configuration'); } trigger_deprecation('zenstruck\foundry', '1.23', 'Usage of foundry without the bundle is deprecated and will not be possible anymore in 2.0.'); diff --git a/src/Test/ResetDatabase.php b/src/Test/ResetDatabase.php index 3af6f834e..b654994fe 100644 --- a/src/Test/ResetDatabase.php +++ b/src/Test/ResetDatabase.php @@ -89,8 +89,8 @@ private static function shouldReset(KernelInterface $kernel): bool private static function getConfiguration(ContainerInterface $container): ?Configuration { - if ($container->has(Configuration::class)) { - return $container->get(Configuration::class); + if ($container->has('.zenstruck_foundry.configuration')) { + return $container->get('.zenstruck_foundry.configuration'); } trigger_deprecation('zenstruck\foundry', '1.23', 'Usage of foundry without the bundle is deprecated and will not be possible anymore in 2.0.'); diff --git a/src/Test/TestState.php b/src/Test/TestState.php index 3fb2a8835..bca72dc67 100644 --- a/src/Test/TestState.php +++ b/src/Test/TestState.php @@ -153,8 +153,8 @@ public static function bootFactory(Configuration $configuration): Configuration */ public static function bootFromContainer(ContainerInterface $container): void { - if ($container->has(Configuration::class)) { - self::bootFoundry($container->get(Configuration::class)); + if ($container->has('.zenstruck_foundry.configuration')) { + self::bootFoundry($container->get('.zenstruck_foundry.configuration')); return; } diff --git a/src/ZenstruckFoundryBundle.php b/src/ZenstruckFoundryBundle.php index 00721c7a5..1a6d8fde2 100644 --- a/src/ZenstruckFoundryBundle.php +++ b/src/ZenstruckFoundryBundle.php @@ -19,7 +19,7 @@ final class ZenstruckFoundryBundle extends Bundle public function boot(): void { if (!Factory::isBooted()) { - Factory::boot($this->container->get(Configuration::class)); + Factory::boot($this->container->get('.zenstruck_foundry.configuration')); } } diff --git a/tests/Unit/Bundle/DependencyInjection/ChainManagerRegistryPassTest.php b/tests/Unit/Bundle/DependencyInjection/ChainManagerRegistryPassTest.php index 590ca7e09..d79018077 100644 --- a/tests/Unit/Bundle/DependencyInjection/ChainManagerRegistryPassTest.php +++ b/tests/Unit/Bundle/DependencyInjection/ChainManagerRegistryPassTest.php @@ -7,7 +7,6 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Zenstruck\Foundry\Bundle\DependencyInjection\ChainManagerRegistryPass; -use Zenstruck\Foundry\ChainManagerRegistry; class ChainManagerRegistryPassTest extends AbstractCompilerPassTestCase { @@ -16,7 +15,7 @@ class ChainManagerRegistryPassTest extends AbstractCompilerPassTestCase */ public function add_both_odm_and_orm_if_present(): void { - $this->setDefinition(ChainManagerRegistry::class, new Definition()); + $this->setDefinition('.zenstruck_foundry.chain_manager_registry', new Definition()); $this->setDefinition('doctrine', new Definition()); $this->setDefinition('doctrine_mongodb', new Definition()); @@ -24,7 +23,7 @@ public function add_both_odm_and_orm_if_present(): void $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithArgument( - ChainManagerRegistry::class, + '.zenstruck_foundry.chain_manager_registry', '$managerRegistries', [new Reference('doctrine'), new Reference('doctrine_mongodb')] ); @@ -35,14 +34,14 @@ public function add_both_odm_and_orm_if_present(): void */ public function only_add_orm(): void { - $this->setDefinition(ChainManagerRegistry::class, new Definition()); + $this->setDefinition('.zenstruck_foundry.chain_manager_registry', new Definition()); $this->setDefinition('doctrine', new Definition()); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithArgument( - ChainManagerRegistry::class, + '.zenstruck_foundry.chain_manager_registry', '$managerRegistries', [new Reference('doctrine')] ); diff --git a/tests/Unit/Bundle/DependencyInjection/ZenstruckFoundryExtensionTest.php b/tests/Unit/Bundle/DependencyInjection/ZenstruckFoundryExtensionTest.php index cb2f7932f..9be4bb51a 100644 --- a/tests/Unit/Bundle/DependencyInjection/ZenstruckFoundryExtensionTest.php +++ b/tests/Unit/Bundle/DependencyInjection/ZenstruckFoundryExtensionTest.php @@ -6,13 +6,7 @@ use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Zenstruck\Foundry\Bundle\DependencyInjection\ZenstruckFoundryExtension; -use Zenstruck\Foundry\Bundle\Maker\MakeFactory; -use Zenstruck\Foundry\Bundle\Maker\MakeStory; -use Zenstruck\Foundry\ChainManagerRegistry; -use Zenstruck\Foundry\Configuration; use Zenstruck\Foundry\Instantiator; -use Zenstruck\Foundry\ModelFactoryManager; -use Zenstruck\Foundry\StoryManager; /** * @author Kevin Bond @@ -26,21 +20,21 @@ public function default_config(): void { $this->load(); - $this->assertContainerBuilderHasService(Configuration::class); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall(Configuration::class, 'setInstantiator', ['zenstruck_foundry.default_instantiator']); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall(Configuration::class, 'setFaker', ['zenstruck_foundry.faker']); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall(Configuration::class, 'setManagerRegistry', [ChainManagerRegistry::class]); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall(Configuration::class, 'setStoryManager', [StoryManager::class]); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall(Configuration::class, 'setModelFactoryManager', [ModelFactoryManager::class]); - $this->assertCount(5, $this->container->findDefinition(Configuration::class)->getMethodCalls()); - $this->assertTrue($this->container->getDefinition(Configuration::class)->isPublic()); - $this->assertContainerBuilderHasService('zenstruck_foundry.default_instantiator', Instantiator::class); - $this->assertEmpty($this->container->getDefinition('zenstruck_foundry.default_instantiator')->getMethodCalls()); - $this->assertContainerBuilderHasService('zenstruck_foundry.faker', Generator::class); - $this->assertEmpty($this->container->getDefinition('zenstruck_foundry.faker')->getArguments()); - $this->assertContainerBuilderHasService(StoryManager::class); - $this->assertContainerBuilderHasServiceDefinitionWithTag(MakeFactory::class, 'maker.command'); - $this->assertContainerBuilderHasServiceDefinitionWithTag(MakeStory::class, 'maker.command'); + $this->assertContainerBuilderHasService('.zenstruck_foundry.configuration'); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.configuration', 'setInstantiator', ['.zenstruck_foundry.default_instantiator']); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.configuration', 'setFaker', ['.zenstruck_foundry.faker']); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.configuration', 'setManagerRegistry', ['.zenstruck_foundry.chain_manager_registry']); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.configuration', 'setStoryManager', ['.zenstruck_foundry.story_manager']); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.configuration', 'setModelFactoryManager', ['.zenstruck_foundry.model_factory_manager']); + $this->assertCount(5, $this->container->findDefinition('.zenstruck_foundry.configuration')->getMethodCalls()); + $this->assertTrue($this->container->getDefinition('.zenstruck_foundry.configuration')->isPublic()); + $this->assertContainerBuilderHasService('.zenstruck_foundry.default_instantiator', Instantiator::class); + $this->assertEmpty($this->container->getDefinition('.zenstruck_foundry.default_instantiator')->getMethodCalls()); + $this->assertContainerBuilderHasService('.zenstruck_foundry.faker', Generator::class); + $this->assertEmpty($this->container->getDefinition('.zenstruck_foundry.faker')->getArguments()); + $this->assertContainerBuilderHasService('.zenstruck_foundry.story_manager'); + $this->assertContainerBuilderHasServiceDefinitionWithTag('.zenstruck_foundry.maker.factory', 'maker.command'); + $this->assertContainerBuilderHasServiceDefinitionWithTag('.zenstruck_foundry.maker.story', 'maker.command'); } /** @@ -50,7 +44,7 @@ public function custom_faker_locale(): void { $this->load(['faker' => ['locale' => 'fr_FR']]); - $this->assertContainerBuilderHasServiceDefinitionWithArgument('zenstruck_foundry.faker', 0, 'fr_FR'); + $this->assertContainerBuilderHasServiceDefinitionWithArgument('.zenstruck_foundry.faker', 0, 'fr_FR'); } /** @@ -60,7 +54,7 @@ public function custom_faker_seed(): void { $this->load(['faker' => ['seed' => 1234]]); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('zenstruck_foundry.faker', 'seed', [1234]); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.faker', 'seed', [1234]); } /** @@ -70,9 +64,9 @@ public function custom_faker_service(): void { $this->load(['faker' => ['service' => 'my_faker']]); - $this->assertContainerBuilderHasService(Configuration::class); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall(Configuration::class, 'setFaker', ['zenstruck_foundry.faker']); - $this->assertContainerBuilderHasAlias('zenstruck_foundry.faker', 'my_faker'); + $this->assertContainerBuilderHasService('.zenstruck_foundry.configuration'); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.configuration', 'setFaker', ['.zenstruck_foundry.faker']); + $this->assertContainerBuilderHasAlias('.zenstruck_foundry.faker', 'my_faker'); } /** @@ -108,9 +102,9 @@ public function custom_instantiator_config(): void 'always_force_properties' => true, ]]); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('zenstruck_foundry.default_instantiator', 'withoutConstructor'); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('zenstruck_foundry.default_instantiator', 'allowExtraAttributes'); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('zenstruck_foundry.default_instantiator', 'alwaysForceProperties'); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.default_instantiator', 'withoutConstructor'); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.default_instantiator', 'allowExtraAttributes'); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.default_instantiator', 'alwaysForceProperties'); } /** @@ -120,8 +114,8 @@ public function custom_instantiator_service(): void { $this->load(['instantiator' => ['service' => 'my_instantiator']]); - $this->assertContainerBuilderHasService(Configuration::class); - $this->assertContainerBuilderHasAlias('zenstruck_foundry.default_instantiator', 'my_instantiator'); + $this->assertContainerBuilderHasService('.zenstruck_foundry.configuration'); + $this->assertContainerBuilderHasAlias('.zenstruck_foundry.default_instantiator', 'my_instantiator'); } /** @@ -164,9 +158,9 @@ public function can_enable_auto_refresh_proxies(): void { $this->load(['auto_refresh_proxies' => true]); - $this->assertContainerBuilderHasService(Configuration::class); - $this->assertCount(6, $this->container->findDefinition(Configuration::class)->getMethodCalls()); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall(Configuration::class, 'enableDefaultProxyAutoRefresh', []); + $this->assertContainerBuilderHasService('.zenstruck_foundry.configuration'); + $this->assertCount(6, $this->container->findDefinition('.zenstruck_foundry.configuration')->getMethodCalls()); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.configuration', 'enableDefaultProxyAutoRefresh', []); } /** @@ -176,9 +170,9 @@ public function can_disable_auto_refresh_proxies(): void { $this->load(['auto_refresh_proxies' => false]); - $this->assertContainerBuilderHasService(Configuration::class); - $this->assertCount(6, $this->container->findDefinition(Configuration::class)->getMethodCalls()); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall(Configuration::class, 'disableDefaultProxyAutoRefresh', []); + $this->assertContainerBuilderHasService('.zenstruck_foundry.configuration'); + $this->assertCount(6, $this->container->findDefinition('.zenstruck_foundry.configuration')->getMethodCalls()); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('.zenstruck_foundry.configuration', 'disableDefaultProxyAutoRefresh', []); } /** From f5e9eaeb52eceb234f3042b163d3ac3cd3a07a38 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Fri, 25 Nov 2022 16:57:11 +0100 Subject: [PATCH 29/30] minor: use --no-persistence instead of --not-persisted (#365) --- .github/workflows/ci.yml | 2 +- src/Bundle/Maker/MakeFactory.php | 14 +++++++------- tests/Functional/Bundle/Maker/MakeFactoryTest.php | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae357d4f7..b450af04c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -205,7 +205,7 @@ jobs: with: composer-options: --prefer-dist - - name: Install PhpStan + - name: Install PHPStan run: composer bin phpstan install - name: Run static analysis diff --git a/src/Bundle/Maker/MakeFactory.php b/src/Bundle/Maker/MakeFactory.php index 63ab793e5..91cd8bebd 100644 --- a/src/Bundle/Maker/MakeFactory.php +++ b/src/Bundle/Maker/MakeFactory.php @@ -51,7 +51,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf ->addOption('namespace', null, InputOption::VALUE_REQUIRED, 'Customize the namespace for generated factories', 'Factory') ->addOption('test', null, InputOption::VALUE_NONE, 'Create in tests/ instead of src/') ->addOption('all-fields', null, InputOption::VALUE_NONE, 'Create defaults for all entity fields, not only required fields') - ->addOption('not-persisted', null, InputOption::VALUE_NONE, 'Create a factory for an object not managed by Doctrine') + ->addOption('no-persistence', null, InputOption::VALUE_NONE, 'Create a factory for an object not managed by Doctrine') ; $inputConfig->setArgumentAsNonInteractive('class'); @@ -59,11 +59,11 @@ public function configureCommand(Command $command, InputConfiguration $inputConf public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void { - if (!$this->doctrineEnabled() && !$input->getOption('not-persisted')) { - $io->text('// Note: Doctrine not enabled: auto-activating --not-persisted option.'); + if (!$this->doctrineEnabled() && !$input->getOption('no-persistence')) { + $io->text('// Note: Doctrine not enabled: auto-activating --no-persistence option.'); $io->newLine(); - $input->setOption('not-persisted', true); + $input->setOption('no-persistence', true); } if ($input->getArgument('class')) { @@ -80,7 +80,7 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma $io->newLine(); } - if ($input->getOption('not-persisted')) { + if ($input->getOption('no-persistence')) { $class = $io->ask( 'Not persisted class to create a factory for', validator: static function(string $class) { @@ -123,7 +123,7 @@ private function generateFactory(string $class, InputInterface $input, ConsoleSt throw new RuntimeCommandException(\sprintf('Class "%s" not found.', $input->getArgument('class'))); } - $makeFactoryData = $this->createMakeFactoryData($class, !$input->getOption('not-persisted')); + $makeFactoryData = $this->createMakeFactoryData($class, !$input->getOption('no-persistence')); $factory = $generator->createClassNameDetails( $makeFactoryData->getObjectShortName(), @@ -131,7 +131,7 @@ private function generateFactory(string $class, InputInterface $input, ConsoleSt 'Factory' ); - $this->defaultPropertiesGuesser(!$input->getOption('not-persisted'))( + $this->defaultPropertiesGuesser(!$input->getOption('no-persistence'))( $makeFactoryData, $input->getOption('all-fields') ); diff --git a/tests/Functional/Bundle/Maker/MakeFactoryTest.php b/tests/Functional/Bundle/Maker/MakeFactoryTest.php index 8737ea1ee..e380554e7 100644 --- a/tests/Functional/Bundle/Maker/MakeFactoryTest.php +++ b/tests/Functional/Bundle/Maker/MakeFactoryTest.php @@ -186,7 +186,7 @@ public function can_create_factory_for_not_persisted_class(): void $this->assertFileDoesNotExist(self::tempFile('src/Factory/SomeObjectFactory.php')); - $tester->execute(['class' => SomeObject::class, '--not-persisted' => true, '--all-fields' => true]); + $tester->execute(['class' => SomeObject::class, '--no-persistence' => true, '--all-fields' => true]); $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/SomeObjectFactory.php')); } @@ -201,7 +201,7 @@ public function can_create_factory_for_not_persisted_class_interactively(): void $this->assertFileDoesNotExist(self::tempFile('src/Factory/SomeObjectFactory.php')); $tester->setInputs(['Foo', SomeObject::class]); // "Foo" will generate a validation error - $tester->execute(['--not-persisted' => true]); + $tester->execute(['--no-persistence' => true]); $output = $tester->getDisplay(); @@ -362,7 +362,7 @@ public function can_create_factory_with_auto_activated_not_persisted_option(): v $tester->execute(['class' => Category::class]); $output = $tester->getDisplay(); - $this->assertStringContainsString('Note: Doctrine not enabled: auto-activating --not-persisted option.', $output); + $this->assertStringContainsString('Note: Doctrine not enabled: auto-activating --no-persistence option.', $output); $this->assertFileFromMakerSameAsExpectedFile(self::tempFile('src/Factory/CategoryFactory.php')); } From 1711ddf0957870a688472dd2569b3825a5a16ea2 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Fri, 25 Nov 2022 11:03:04 -0500 Subject: [PATCH 30/30] [changelog] update changelog [skip ci] --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5491ee6fa..fb58b38bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,39 @@ # CHANGELOG +## [v1.24.0](https://github.com/zenstruck/foundry/releases/tag/v1.24.0) + +November 25th, 2022 - [v1.23.0...v1.24.0](https://github.com/zenstruck/foundry/compare/v1.23.0...v1.24.0) + +* f5e9eae minor: use --no-persistence instead of --not-persisted (#365) by @nikophil +* 730c0d9 chore: rename service ids (#363) by @kbond +* 19acc72 feat: add `RepositoryProxy::inner()` (#362) by @kbond +* a003bac refactor(make:factory): split command with DefaultPropertiesGuesser (#357) by @nikophil +* d8eca88 chore(ci): test on Symfony 6.2 (#359) by @kbond +* 20ac349 refactor(make:factory): use value object to render template (#354) by @nikophil +* 4e5f9d9 feat(make:factory): use factories to default non-nullable relationships (#351) by @nikophil, @benblub +* 8332956 feat: make `Story::get()` static (implies `Story::load()->get()`) (#253) by @kbond +* b89bcff minor(make:factory): misc enhancements of maker (#345) by @nikophil +* 3cc95a5 minor: remove php 7.4 related tests (#349) by @nikophil +* 8a055b0 chore: fix docker cache (#350) by @nikophil +* 18ea4fb feat(make:factory): create factory for not-persisted objects (#343) by @nikophil +* 64786fc fix: typo in docs (#348) by @nikophil +* 1a98fc4 chore: Use composer 2.4 (#346) by @OskarStark +* 96c4cbe minor(make:factory): Use `@see`/`@todo` annoations (#344) by @OskarStark +* cbeb2ce fix: adjust docblocks to remove PhpStorm errors (#341) by @kbond +* cd1e394 fix: use orm limit length in factory (#294) by @MrYamous +* b1d7ce3 [feature] add default for Mongo properties in (#340) by @nikophil +* 778607a [chore] use cache for docker CI (#339) by @nikophil +* c662eb3 [feature] auto add phpstan annotations in make:factory (#338) by @nikophil +* 2bd046f [docs] sort phpstan-method annotations (#333) by @OskarStark +* 0460741 [docs] remove obsolete section (#335) by @nikophil +* 2c34baf [bug] Typos in Makefile (#330) by @OskarStark +* 65924b2 [bug] typos in docs (#331) by @OskarStark +* 6b48878 [chore] upgrade ci actions (#329) by @kbond +* 210faff [chore] use phpstan instead of psalm (#328) by @nikophil +* 51f1bc0 [refactor] modernize code with rector (#327) by @nikophil +* 8423b75 [chore] adjust `.symfony.bundle.yaml` for new branch (#325) by @kbond +* 5a05513 [feature] require php8+ (#327) by @kbond + ## [v1.23.0](https://github.com/zenstruck/foundry/releases/tag/v1.23.0) November 10th, 2022 - [v1.22.1...v1.23.0](https://github.com/zenstruck/foundry/compare/v1.22.1...v1.23.0)