Skip to content

Commit

Permalink
refactor: deprecate stuff in Instantiator
Browse files Browse the repository at this point in the history
  • Loading branch information
nikophil committed Nov 6, 2023
1 parent 8dac262 commit 1f042a6
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 44 deletions.
2 changes: 1 addition & 1 deletion phpunit.dama.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<ini name="error_reporting" value="-1"/>
<env name="KERNEL_CLASS" value="Zenstruck\Foundry\Tests\Fixtures\Kernel"/>
<env name="USE_DAMA_DOCTRINE_TEST_BUNDLE" value="1"/>
<env name="SYMFONY_DEPRECATIONS_HELPER" value="max[self]=0&amp;max[direct]=0&amp;quiet[]=indirect&amp;quiet[]=other"/>
<env name="SYMFONY_DEPRECATIONS_HELPER" value="/will be private in Foundry/"/>
<env name="SHELL_VERBOSITY" value="0"/>
</php>

Expand Down
3 changes: 2 additions & 1 deletion src/Bundle/DependencyInjection/ZenstruckFoundryExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
use Zenstruck\Foundry\Bundle\Command\StubMakeFactory;
use Zenstruck\Foundry\Bundle\Command\StubMakeStory;
use Zenstruck\Foundry\Instantiator;
use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory;
use Zenstruck\Foundry\Story;
use Zenstruck\Foundry\Test\ORMDatabaseResetter;
Expand Down Expand Up @@ -87,7 +88,7 @@ private function configureDefaultInstantiator(array $config, ContainerBuilder $c
$definition = $container->getDefinition('.zenstruck_foundry.default_instantiator');

if ($config['without_constructor']) {
$definition->addMethodCall('withoutConstructor');
$definition->setFactory([Instantiator::class, 'withoutConstructor']);
}

if ($config['allow_extra_attributes']) {
Expand Down
4 changes: 3 additions & 1 deletion src/Bundle/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
https://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id=".zenstruck_foundry.default_instantiator" class="Zenstruck\Foundry\Instantiator" />
<service id=".zenstruck_foundry.default_instantiator" class="Zenstruck\Foundry\Instantiator">
<factory class="Zenstruck\Foundry\Instantiator" method="withConstructor" />
</service>
<service id=".zenstruck_foundry.faker" class="Faker\Generator">
<factory class="Faker\Factory" method="create" />
</service>
Expand Down
2 changes: 1 addition & 1 deletion src/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function __construct(private array $ormConnectionsToReset, private array
$this->stories = new StoryManager([]);
$this->factories = new ModelFactoryManager([]);
$this->faker = Faker\Factory::create();
$this->instantiator = new Instantiator();
$this->instantiator = Instantiator::withConstructor();
}

public function stories(): StoryManager
Expand Down
43 changes: 39 additions & 4 deletions src/Instantiator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

/**
* @author Kevin Bond <[email protected]>
*
* @method static self withoutConstructor()
* @method self withoutConstructor()
*/
final class Instantiator
{
Expand All @@ -35,6 +38,13 @@ final class Instantiator
/** @var string[] */
private array $forceProperties = [];

public function __construct(bool $calledInternally = false)
{
if (!$calledInternally) {
trigger_deprecation('zenstruck\foundry', '1.37.0', sprintf('%1$s constructor will be private in Foundry 2.0. Use either "%1$s::withConstructor()" or "%1$s::withoutConstructor()"', self::class));
}
}

/**
* @param class-string $class
*/
Expand Down Expand Up @@ -90,16 +100,37 @@ public function __invoke(array $attributes, string $class): object
return $object;
}

/**
* Instantiate objects without calling the constructor.
*/
public function withoutConstructor(): self
public function __call(string $name, array $arguments): self
{
if ('withoutConstructor' !== $name) {
throw new \BadMethodCallException(\sprintf('Call to undefined method "%s::%s".', static::class, $name));
}

trigger_deprecation('zenstruck/foundry', '1.37.0', 'Calling instance method "%1$s::withoutConstructor()" is deprecated and will be removed in 2.0. Use static call instead: "%1$s::withoutConstructor()" instead.', static::class);

$this->withoutConstructor = true;

return $this;
}

public static function __callStatic(string $name, array $arguments): self
{
if ('withoutConstructor' !== $name) {
throw new \BadMethodCallException(\sprintf('Call to undefined method "%s::%s".', static::class, $name));
}

$instance = new self(calledInternally: true);

$instance->withoutConstructor = true;

return $instance;
}

public static function withConstructor(): self
{
return new self(calledInternally: true);
}

/**
* Ignore attributes that can't be set to object.
*
Expand Down Expand Up @@ -240,6 +271,10 @@ private function instantiate(string $class, array &$attributes): object
$constructor = $class->getConstructor();

if ($this->withoutConstructor || !$constructor || !$constructor->isPublic()) {
if (!$this->withoutConstructor && $constructor && !$constructor->isPublic()) {
trigger_deprecation('zenstruck\foundry', '1.37.0', 'Instantiator was created to instantiate "%s" by calling the constructor whereas the constructor is not public. This is deprecated and will throw an exception in Foundry 2.0. Use "%s::withoutConstructor()" instead or make constructor public.', $class->getName(), self::class);
}

return $class->newInstanceWithoutConstructor();
}

Expand Down
2 changes: 1 addition & 1 deletion tests/Fixtures/Factories/CategoryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ protected function initialize(): static
{
return $this
->instantiateWith(
(new Instantiator())->allowExtraAttributes(['extraPostsBeforeInstantiate', 'extraPostsAfterInstantiate'])
Instantiator::withConstructor()->allowExtraAttributes(['extraPostsBeforeInstantiate', 'extraPostsAfterInstantiate'])
)
->beforeInstantiate(function (array $attributes): array {
if (isset($attributes['extraPostsBeforeInstantiate'])) {
Expand Down
2 changes: 1 addition & 1 deletion tests/Fixtures/Factories/PostFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected function initialize(): static
{
return $this
->instantiateWith(
(new Instantiator())->allowExtraAttributes(['extraCategoryBeforeInstantiate', 'extraCategoryAfterInstantiate'])
Instantiator::withConstructor()->allowExtraAttributes(['extraCategoryBeforeInstantiate', 'extraCategoryAfterInstantiate'])
)
->beforeInstantiate(function (array $attributes): array {
if (isset($attributes['extraCategoryBeforeInstantiate'])) {
Expand Down
12 changes: 6 additions & 6 deletions tests/Functional/FactoryDoctrineCascadeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function many_to_one_relationship(): void
])->instantiateWith(function(array $attributes, string $class): object {
$this->assertNull($attributes['brand']->getId());

return (new Instantiator())($attributes, $class);
return Instantiator::withConstructor()($attributes, $class);
})->create();

$this->assertNotNull($product->getBrand()->getId());
Expand All @@ -68,7 +68,7 @@ public function one_to_many_relationship(): void
])->instantiateWith(function(array $attributes, string $class): object {
// $this->assertNull($attributes['products'][0]->getId());

return (new Instantiator())($attributes, $class);
return Instantiator::withConstructor()($attributes, $class);
})->create();

$this->assertCount(2, $brand->getProducts());
Expand All @@ -87,7 +87,7 @@ public function many_to_many_relationship(): void
])->instantiateWith(function(array $attributes, string $class): object {
$this->assertNull($attributes['tags'][0]->getId());

return (new Instantiator())($attributes, $class);
return Instantiator::withConstructor()($attributes, $class);
})->create();

$this->assertCount(1, $product->getTags());
Expand All @@ -106,7 +106,7 @@ public function many_to_many_reverse_relationship(): void
])->instantiateWith(function(array $attributes, string $class): object {
$this->assertNull($attributes['categories'][0]->getId());

return (new Instantiator())($attributes, $class);
return Instantiator::withConstructor()($attributes, $class);
})->create();

$this->assertCount(1, $product->getCategories());
Expand All @@ -125,7 +125,7 @@ public function one_to_one_relationship(): void
])->instantiateWith(function(array $attributes, string $class): object {
$this->assertNull($attributes['review']->getId());

return (new Instantiator())($attributes, $class);
return Instantiator::withConstructor()($attributes, $class);
})->create();

$this->assertNotNull($product->getReview()->getId());
Expand All @@ -143,7 +143,7 @@ public function one_to_one_reverse_relationship(): void
])->instantiateWith(function(array $attributes, string $class): object {
$this->assertNull($attributes['review']->getId());

return (new Instantiator())($attributes, $class);
return Instantiator::withConstructor()($attributes, $class);
})->create();

$this->assertNotNull($product->getReview()->getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,15 @@ 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');

$instantiator = $this->container->get('.zenstruck_foundry.default_instantiator');

// matthiasnoback/symfony-dependency-injection-test cannot assert if a service is created through a factory.
// so, we're checking that private property "Instantiator::$withoutConstructor" was set to true.
$withoutConstructor = \Closure::bind(static function (Instantiator $instantiator){return $instantiator->withoutConstructor;}, null, Instantiator::class)($instantiator);
self::assertTrue($withoutConstructor);
}

/**
Expand Down
Loading

0 comments on commit 1f042a6

Please sign in to comment.