Skip to content

Commit 2507f18

Browse files
authored
Add the factory keyword (nelmio#729)
1 parent f596140 commit 2507f18

File tree

17 files changed

+870
-132
lines changed

17 files changed

+870
-132
lines changed

doc/complete-reference.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ Nelmio\Entity\User:
208208

209209
## Using a factory
210210

211-
**[TODO] Status: unimplemented; usable in with `__construct` in `__factory` but this will be deprecated once
212-
`__factory` is out available and will be removed in 4.0**
211+
**Note**: the following also applies to `__construct`. However using `__construct` for factories has been deprecated as of
212+
3.0.0 and will be removed in 4.0.0. Use `__factory` instead.
213213

214214
If you want to call a static factory method instead of a constructor, you can
215215
specify a hash as the constructor:

phpunit.xml.dist

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
beStrictAboutTestsThatDoNotTestAnything="false"
1919
>
2020

21+
<php>
22+
<env name="SYMFONY_DEPRECATIONS_HELPER" value="0" />
23+
</php>
24+
25+
<listeners>
26+
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
27+
</listeners>
28+
2129
<testsuites>
2230
<testsuite name="Dependent tests">
2331
<file>tests/Definition/PropertyTest.php</file>
@@ -66,8 +74,4 @@
6674
</exclude>
6775
</whitelist>
6876
</filter>
69-
70-
<php>
71-
<env name="SYMFONY_DEPRECATIONS_HELPER" value="0" />
72-
</php>
7377
</phpunit>

src/Bridge/Symfony/Resources/config/fixture_builder/denormalizer/fixture.xml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,21 @@
8787

8888
<!-- Specifications Constructors Denormalizer -->
8989
<service id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.constructor"
90-
alias="nelmio_alice.fixture_builder.denormalizer.fixture.specs.constructor.constructor_with_caller_denormalizer" />
90+
alias="nelmio_alice.fixture_builder.denormalizer.fixture.specs.constructor.legacy_constructor_denormalizer" />
9191

92-
<service id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.constructor.constructor_with_caller_denormalizer"
93-
class="Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Constructor\ConstructorWithCallerDenormalizer">
94-
<argument type="service" id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.constructor.simple_constructor_denormalizer" />
95-
<argument type="service" id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.arguments.simple_arguments_denormalizer" />
92+
<service id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.constructor.legacy_constructor_denormalizer"
93+
class="Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Constructor\LegacyConstructorDenormalizer">
94+
<argument type="service" id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.constructor.constructor_denormalizer" />
95+
<argument type="service" id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.constructor.factory_denormalizer" />
9696
</service>
9797

98-
<service id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.constructor.simple_constructor_denormalizer"
99-
class="Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Constructor\SimpleConstructorDenormalizer">
98+
<service id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.constructor.factory_denormalizer"
99+
class="Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Constructor\FactoryDenormalizer">
100+
<argument type="service" id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.arguments" />
101+
</service>
102+
103+
<service id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.constructor.constructor_denormalizer"
104+
class="Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Constructor\ConstructorDenormalizer">
100105
<argument type="service" id="nelmio_alice.fixture_builder.denormalizer.fixture.specs.arguments" />
101106
</service>
102107

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,14 @@
1515

1616
use Nelmio\Alice\Definition\MethodCall\SimpleMethodCall;
1717
use Nelmio\Alice\Definition\MethodCallInterface;
18-
use Nelmio\Alice\Throwable\Exception\FixtureBuilder\Denormalizer\DenormalizerExceptionFactory;
1918
use Nelmio\Alice\Throwable\Exception\FixtureBuilder\Denormalizer\UnexpectedValueException;
2019
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\ArgumentsDenormalizerInterface;
2120
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\ConstructorDenormalizerInterface;
2221
use Nelmio\Alice\FixtureBuilder\Denormalizer\FlagParserInterface;
2322
use Nelmio\Alice\FixtureInterface;
2423
use Nelmio\Alice\IsAServiceTrait;
2524

26-
final class SimpleConstructorDenormalizer implements ConstructorDenormalizerInterface
25+
final class ConstructorDenormalizer implements ConstructorDenormalizerInterface
2726
{
2827
use IsAServiceTrait;
2928

@@ -48,19 +47,9 @@ public function denormalize(
4847
array $unparsedConstructor
4948
): MethodCallInterface
5049
{
51-
/** @var int|string|null $firstKey */
52-
$firstKey = key($unparsedConstructor);
53-
if (null === $firstKey
54-
|| is_int($firstKey)
55-
|| count($unparsedConstructor) > 1
56-
|| (is_string($firstKey) && preg_match('/\(.*\)/', $firstKey))
57-
) {
58-
return new SimpleMethodCall(
59-
'__construct',
60-
$this->argumentDenormalizer->denormalize($scope, $parser, $unparsedConstructor)
61-
);
62-
}
63-
64-
throw DenormalizerExceptionFactory::createForUndenormalizableConstructor();
50+
return new SimpleMethodCall(
51+
'__construct',
52+
$this->argumentDenormalizer->denormalize($scope, $parser, $unparsedConstructor)
53+
);
6554
}
6655
}
Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,24 @@
1818
use Nelmio\Alice\Definition\ServiceReference\InstantiatedReference;
1919
use Nelmio\Alice\Definition\ServiceReference\StaticReference;
2020
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\ArgumentsDenormalizerInterface;
21-
use Nelmio\Alice\Throwable\Exception\FixtureBuilder\Denormalizer\UnexpectedValueException;
21+
use Nelmio\Alice\Throwable\Exception\FixtureBuilder\Denormalizer\DenormalizerExceptionFactory;
2222
use Nelmio\Alice\Throwable\Exception\InvalidArgumentExceptionFactory;
2323
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\ConstructorDenormalizerInterface;
2424
use Nelmio\Alice\FixtureBuilder\Denormalizer\FlagParserInterface;
2525
use Nelmio\Alice\FixtureInterface;
2626
use Nelmio\Alice\IsAServiceTrait;
2727

28-
final class ConstructorWithCallerDenormalizer implements ConstructorDenormalizerInterface
28+
final class FactoryDenormalizer implements ConstructorDenormalizerInterface
2929
{
3030
use IsAServiceTrait;
3131

32-
/**
33-
* @var SimpleConstructorDenormalizer
34-
*/
35-
private $simpleConstructorDenormalizer;
36-
3732
/**
3833
* @var ArgumentsDenormalizerInterface
3934
*/
4035
private $argumentsDenormalizer;
4136

42-
public function __construct(
43-
SimpleConstructorDenormalizer $simpleConstructorDenormalizer,
44-
ArgumentsDenormalizerInterface $argumentsDenormalizer
45-
) {
46-
$this->simpleConstructorDenormalizer = $simpleConstructorDenormalizer;
37+
public function __construct(ArgumentsDenormalizerInterface $argumentsDenormalizer)
38+
{
4739
$this->argumentsDenormalizer = $argumentsDenormalizer;
4840
}
4941

@@ -56,14 +48,22 @@ public function denormalize(
5648
array $unparsedConstructor
5749
): MethodCallInterface
5850
{
59-
try {
60-
return $this->simpleConstructorDenormalizer->denormalize($scope, $parser, $unparsedConstructor);
61-
} catch (UnexpectedValueException $exception) {
62-
// Continue
63-
}
64-
6551
/** @var string $firstKey */
6652
$firstKey = key($unparsedConstructor);
53+
54+
if (false === $firstKey
55+
|| false === is_string($firstKey)
56+
|| 1 !== count($unparsedConstructor)
57+
) {
58+
throw DenormalizerExceptionFactory::createForUndenormalizableFactory();
59+
}
60+
61+
$arguments = $unparsedConstructor[$firstKey];
62+
63+
if (false === is_array($arguments)) {
64+
throw DenormalizerExceptionFactory::createForUndenormalizableFactory();
65+
}
66+
6767
list($caller, $method) = $this->getCallerReference($scope, $firstKey);
6868
$arguments = $this->argumentsDenormalizer->denormalize($scope, $parser, $unparsedConstructor[$firstKey]);
6969

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Alice package.
5+
*
6+
* (c) Nelmio <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Constructor;
15+
16+
use Nelmio\Alice\Definition\MethodCallInterface;
17+
use Nelmio\Alice\Throwable\Exception\FixtureBuilder\Denormalizer\UnexpectedValueException;
18+
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\ConstructorDenormalizerInterface;
19+
use Nelmio\Alice\FixtureBuilder\Denormalizer\FlagParserInterface;
20+
use Nelmio\Alice\FixtureInterface;
21+
use Nelmio\Alice\IsAServiceTrait;
22+
23+
final class LegacyConstructorDenormalizer implements ConstructorDenormalizerInterface
24+
{
25+
use IsAServiceTrait;
26+
27+
/**
28+
* @var ConstructorDenormalizer
29+
*/
30+
private $constructorDenormalizer;
31+
32+
/**
33+
* @var ConstructorDenormalizer
34+
*/
35+
private $factoryDenormalizer;
36+
37+
public function __construct(
38+
ConstructorDenormalizerInterface $constructorDenormalizer,
39+
ConstructorDenormalizerInterface $factoryDenormalizer
40+
) {
41+
$this->constructorDenormalizer = $constructorDenormalizer;
42+
$this->factoryDenormalizer = $factoryDenormalizer;
43+
}
44+
45+
/**
46+
* @inheritdoc
47+
*/
48+
public function denormalize(
49+
FixtureInterface $scope,
50+
FlagParserInterface $parser,
51+
array $unparsedConstructor
52+
): MethodCallInterface
53+
{
54+
try {
55+
return $this->factoryDenormalizer->denormalize($scope, $parser, $unparsedConstructor);
56+
} catch (UnexpectedValueException $exception) {
57+
// Continue
58+
}
59+
60+
return $this->constructorDenormalizer->denormalize($scope, $parser, $unparsedConstructor);
61+
}
62+
}

src/FixtureBuilder/Denormalizer/Fixture/SpecificationBagDenormalizer/SimpleSpecificationsDenormalizer.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
use Nelmio\Alice\FixtureBuilder\Denormalizer\FlagParserInterface;
2323
use Nelmio\Alice\FixtureInterface;
2424
use Nelmio\Alice\Throwable\Error\TypeErrorFactory;
25+
use Nelmio\Alice\Throwable\Exception\FixtureBuilder\Denormalizer\DenormalizerExceptionFactory;
26+
use Nelmio\Alice\Throwable\Exception\LogicExceptionFactory;
2527

2628
final class SimpleSpecificationsDenormalizer implements SpecificationsDenormalizerInterface
2729
{
@@ -64,6 +66,24 @@ public function denormalize(FixtureInterface $scope, FlagParserInterface $parser
6466
if ('__construct' === $unparsedPropertyName) {
6567
$constructor = $this->denormalizeConstructor($value, $scope, $parser);
6668

69+
if (false === ($constructor instanceof NoMethodCall) && '__construct' !== $constructor->getMethod()) {
70+
@trigger_error(
71+
'Using factories with the fixture keyword "__construct" has been deprecated since '
72+
.'3.0.0 and will no longer be supported in 4.0.0. Use "__factory" instead.',
73+
E_USER_DEPRECATED
74+
);
75+
}
76+
77+
continue;
78+
}
79+
80+
if ('__factory' === $unparsedPropertyName) {
81+
if (null !== $constructor) {
82+
throw LogicExceptionFactory::createForCannotHaveBothConstructorAndFactory();
83+
}
84+
85+
$constructor = $this->denormalizeFactory($value, $scope, $parser);
86+
6787
continue;
6888
}
6989

@@ -91,6 +111,21 @@ private function denormalizeConstructor(
91111
;
92112
}
93113

114+
private function denormalizeFactory(
115+
$value,
116+
FixtureInterface $scope,
117+
FlagParserInterface $parser
118+
): MethodCallInterface
119+
{
120+
$factory = $this->denormalizeConstructor($value, $scope, $parser);
121+
122+
if ('__construct' === $factory->getMethod()) {
123+
throw DenormalizerExceptionFactory::createForUndenormalizableFactory();
124+
}
125+
126+
return $factory;
127+
}
128+
94129
private function denormalizeProperty(
95130
PropertyDenormalizerInterface $propertyDenormalizer,
96131
FlagParserInterface $flagParser,

src/Loader/NativeLoader.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\Chainable\NullListNameDenormalizer;
2121
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\Chainable\NullRangeNameDenormalizer;
2222
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\Chainable\SimpleCollectionDenormalizer;
23+
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Constructor\FactoryDenormalizer;
2324
use Nelmio\Alice\Generator\Resolver\Value\Chainable\FixtureMethodCallReferenceResolver;
2425
use Nelmio\Alice\Generator\Resolver\Value\Chainable\FunctionCallArgumentResolver;
2526
use Nelmio\Alice\Generator\Resolver\Value\Chainable\PhpFunctionCallValueResolver;
@@ -64,8 +65,8 @@
6465
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\ArgumentsDenormalizerInterface;
6566
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Calls\OptionalCallsDenormalizer;
6667
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\CallsDenormalizerInterface;
67-
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Constructor\ConstructorWithCallerDenormalizer;
68-
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Constructor\SimpleConstructorDenormalizer;
68+
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Constructor\LegacyConstructorDenormalizer;
69+
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Constructor\ConstructorDenormalizer;
6970
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\ConstructorDenormalizerInterface;
7071
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\Property\SimplePropertyDenormalizer;
7172
use Nelmio\Alice\FixtureBuilder\Denormalizer\Fixture\SpecificationBagDenormalizer\PropertyDenormalizerInterface;
@@ -342,8 +343,11 @@ protected function createFlagParser(): FlagParserInterface
342343

343344
protected function createConstructorDenormalizer(): ConstructorDenormalizerInterface
344345
{
345-
return new ConstructorWithCallerDenormalizer(
346-
new SimpleConstructorDenormalizer(
346+
return new LegacyConstructorDenormalizer(
347+
new ConstructorDenormalizer(
348+
$this->getArgumentsDenormalizer()
349+
),
350+
new FactoryDenormalizer(
347351
$this->getArgumentsDenormalizer()
348352
),
349353
$this->getArgumentsDenormalizer()

src/Throwable/Exception/FixtureBuilder/Denormalizer/DenormalizerExceptionFactory.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ public static function createForUndenormalizableConstructor(): UnexpectedValueEx
2323
return new UnexpectedValueException('Could not denormalize the given constructor.');
2424
}
2525

26+
public static function createForUndenormalizableFactory(): UnexpectedValueException
27+
{
28+
return new UnexpectedValueException('Could not denormalize the given factory.');
29+
}
30+
2631
public static function createForUnparsableValue(string $value, int $code = 0, \Throwable $previous): UnexpectedValueException
2732
{
2833
return new UnexpectedValueException(

src/Throwable/Exception/LogicExceptionFactory.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,11 @@ public static function createForCannotDenormalizerForChainableFixtureBuilderDeno
3838
)
3939
);
4040
}
41+
42+
public static function createForCannotHaveBothConstructorAndFactory(): \LogicException
43+
{
44+
return new \LogicException(
45+
'Cannot use the fixture property "__construct" and "__factory" together.'
46+
);
47+
}
4148
}

0 commit comments

Comments
 (0)