-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A FakerProvider for code bases with a PSR-11 compliant DI container (#1)
* FakerProvider that pulls the Generator from a PSR-11 container. As it stands, this code only supports pulling the container itself from a PHP file. That file may either return the container object directly, or set it into a defined variable name. The provider will include the PHP file defined to get the container, then return the Generator from the container by using the defined ID. By using the Generator from the DI container we ensure that all custom providers are added to the Generator first and so making them available for analysis. As an example... if using Symfony, something similar to: $provider = new PsrContainerFakerProvider( './var/cache/dev/App_KernelDevDebugContainer.php', null, 'Faker\Generator' ); will give us a provider that, when asked for the Faker Generator, will include the App_KernelDevDebugContainer.php file, using the return value as the container. From that container the provider will then pull a service with the ID 'Faker\Generator' and return it. * A factory that can provide a PsrContainerFakerProvider. To allow for the service that provides \CalebDW\Fakerstan\FakerProvider to call the static 'create' method on the defined factory, this factory takes parameters for its constructor and then copies those values onto static member variables. The static 'create' method then has access to the values that were given to the constructor. * Add information about PsrContainerFakerProviderFactory to documentation. * Linting. * Import classes instead of using FQCNs. * Call TestCase methods on $this, instead of self. * Use it* instead of getFaker*. * Remove unnecessary comments. * Just require the container file, instead of using require_once. This removes the issue of the changing return value when calling require_once multiple times. From that, it makes our tests easier to set up and tear down, too. * Use the arguments key in example definition. * If there is a problem loading the configured container file, include the configured path in the error message. * Configuration settings for the PSRContainerFakerProvider. Default values allow users that are not using the provider to not have to set values. These defaults either match the default values that are already used in constructors or, in the case of the $phpContainerPath, have a default value that will at least be useful when it is not overridden by the user. * Updated documentation. * Remove the static variables, now that I understand the configuration file. * Predefine the PsrContainerFakerProviderFactory service with its constructor parameters. Now that we have default values for all the constructor parameters, we can define the factory service ourselves. This stops the user from having to say "I'm using this factory with these parameters" and then having to also say "now give me that service with those parameters". * Change namespace of parameters to fakerstan > psrProvider. * Allow the container path to be null until we absolutely decide that we want to create a PsrContainerFakerProvider. * Update src/PsrContainerFakerProviderFactory.php Co-authored-by: Caleb White <[email protected]> --------- Co-authored-by: Caleb White <[email protected]>
- Loading branch information
Showing
9 changed files
with
300 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CalebDW\Fakerstan; | ||
|
||
use Faker\Generator; | ||
use Psr\Container\ContainerInterface; | ||
use RuntimeException; | ||
|
||
final class PsrContainerFakerProvider implements FakerProvider | ||
{ | ||
private ?Generator $generatorFromContainer = null; | ||
|
||
public function __construct( | ||
private string $phpContainerPath, | ||
private ?string $setsVariable, | ||
private string $containerFakerId, | ||
) { | ||
} | ||
|
||
public function getFaker(): Generator | ||
{ | ||
if (is_null($this->generatorFromContainer)) { | ||
$this->generatorFromContainer = $this->getGeneratorFromContainer(); | ||
} | ||
|
||
return $this->generatorFromContainer; | ||
} | ||
|
||
private function getGeneratorFromContainer(): Generator | ||
{ | ||
if (! is_readable($this->phpContainerPath)) { | ||
throw new RuntimeException('Could not read container PHP file ('.$this->phpContainerPath.')'); | ||
} | ||
|
||
$maybeContainer = require $this->phpContainerPath; | ||
|
||
if (is_null($this->setsVariable) && ($maybeContainer === 1)) { | ||
throw new RuntimeException('Container file was expected to return the container, but it returned nothing'); | ||
} | ||
|
||
if (is_string($this->setsVariable)) { | ||
$definedVariables = get_defined_vars(); | ||
if (! array_key_exists($this->setsVariable, $definedVariables)) { | ||
throw new RuntimeException('Container file does not set variable '.$this->setsVariable); | ||
} | ||
|
||
$maybeContainer = $definedVariables[$this->setsVariable]; | ||
} | ||
|
||
if (! $maybeContainer instanceof ContainerInterface) { | ||
throw new RuntimeException('Retrieved container is not a '.ContainerInterface::class); | ||
} | ||
|
||
if (! $maybeContainer->has($this->containerFakerId)) { | ||
throw new RuntimeException('Container does not have entry with ID '.$this->containerFakerId); | ||
} | ||
|
||
$containerFaker = $maybeContainer->get($this->containerFakerId); | ||
if (! $containerFaker instanceof Generator) { | ||
throw new RuntimeException('Container entry with ID '.$this->containerFakerId.' is not a '.Generator::class); | ||
} | ||
|
||
return $containerFaker; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CalebDW\Fakerstan; | ||
|
||
use Faker\Generator; | ||
use RuntimeException; | ||
|
||
class PsrContainerFakerProviderFactory | ||
{ | ||
public function __construct( | ||
private ?string $phpContainerPath = null, | ||
private ?string $setsVariable = null, | ||
private string $containerFakerId = Generator::class, | ||
) { | ||
} | ||
|
||
public function create(): FakerProvider | ||
{ | ||
if (is_null($this->phpContainerPath)) { | ||
throw new RuntimeException(self::class.' requires a value for the "fakerstan.psrProvider.phpContainerPath" parameter'); | ||
} | ||
|
||
return new PsrContainerFakerProvider( | ||
$this->phpContainerPath, | ||
$this->setsVariable, | ||
$this->containerFakerId, | ||
); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
tests/Fixtures/PsrContainerFakerProviderTest/ContainerClass.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CalebDW\Fakerstan\Tests\Fixtures\PsrContainerFakerProviderTest; | ||
|
||
use Exception; | ||
use Faker\Generator; | ||
use Psr\Container\ContainerInterface; | ||
use Psr\Container\NotFoundExceptionInterface; | ||
use stdClass; | ||
|
||
class ContainerClass implements ContainerInterface | ||
{ | ||
public function get(string $id): mixed | ||
{ | ||
return match ($id) { | ||
'generatorId' => new Generator(), | ||
'notGeneratorId' => new stdClass(), | ||
default => throw new class() extends Exception implements NotFoundExceptionInterface { | ||
}, | ||
}; | ||
} | ||
|
||
public function has(string $id): bool | ||
{ | ||
return match ($id) { | ||
'generatorId', 'notGeneratorId' => true, | ||
default => false, | ||
}; | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
tests/Fixtures/PsrContainerFakerProviderTest/EmptyContainerFile.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CalebDW\Fakerstan\Tests\Fixtures\PsrContainerFakerProviderTest; |
7 changes: 7 additions & 0 deletions
7
tests/Fixtures/PsrContainerFakerProviderTest/ReturningContainerFile.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CalebDW\Fakerstan\Tests\Fixtures\PsrContainerFakerProviderTest; | ||
|
||
return new ContainerClass(); |
10 changes: 10 additions & 0 deletions
10
tests/Fixtures/PsrContainerFakerProviderTest/VariableSettingContainerFile.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CalebDW\Fakerstan\Tests\Fixtures\PsrContainerFakerProviderTest; | ||
|
||
use stdClass; | ||
|
||
$containerVariable = new ContainerClass(); | ||
$nonContainerVariable = new stdClass(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CalebDW\Fakerstan\Tests; | ||
|
||
use CalebDW\Fakerstan\PsrContainerFakerProvider; | ||
use Faker\Generator; | ||
use PHPUnit\Framework\Attributes\CoversClass; | ||
use PHPUnit\Framework\Attributes\Test; | ||
use PHPUnit\Framework\TestCase; | ||
use RuntimeException; | ||
|
||
#[CoversClass(PsrContainerFakerProvider::class)] | ||
class PsrContainerFakerProviderTest extends TestCase | ||
{ | ||
#[Test] | ||
public function itUsesContainerReturnedFromFile() | ||
{ | ||
$containerFilename = __DIR__.'/Fixtures/PsrContainerFakerProviderTest/ReturningContainerFile.php'; | ||
$sut = new PsrContainerFakerProvider($containerFilename, null, 'generatorId'); | ||
$faker = $sut->getFaker(); | ||
|
||
$this->assertInstanceOf(Generator::class, $faker); | ||
} | ||
|
||
#[Test] | ||
public function itUsesContainerSetInVariable() | ||
{ | ||
$containerFilename = __DIR__.'/Fixtures/PsrContainerFakerProviderTest/VariableSettingContainerFile.php'; | ||
$sut = new PsrContainerFakerProvider($containerFilename, 'containerVariable', 'generatorId'); | ||
$faker = $sut->getFaker(); | ||
|
||
$this->assertInstanceOf(Generator::class, $faker); | ||
} | ||
|
||
#[Test] | ||
public function itThrowsExceptionWhenUnableToReadContainerFile() | ||
{ | ||
$this->expectException(RuntimeException::class); | ||
|
||
$containerFilename = __DIR__.'/a-file-that-does-not-exist'; | ||
$sut = new PsrContainerFakerProvider($containerFilename, null, 'generatorId'); | ||
$sut->getFaker(); | ||
} | ||
|
||
#[Test] | ||
public function itThrowsExceptionWhenTheContainerFileIsExpectedToReturnContainerButDoesNot() | ||
{ | ||
$this->expectException(RuntimeException::class); | ||
|
||
$containerFilename = __DIR__.'/Fixtures/PsrContainerFakerProviderTest/EmptyContainerFile.php'; | ||
$sut = new PsrContainerFakerProvider($containerFilename, null, 'generatorId'); | ||
$sut->getFaker(); | ||
} | ||
|
||
#[Test] | ||
public function itThrowsExceptionWhenTheContainerFileIsExpectedToSetAVariableButDoesNot() | ||
{ | ||
$this->expectException(RuntimeException::class); | ||
|
||
$containerFilename = __DIR__.'/Fixtures/PsrContainerFakerProviderTest/EmptyContainerFile.php'; | ||
$sut = new PsrContainerFakerProvider($containerFilename, 'containerVariable', 'generatorId'); | ||
$sut->getFaker(); | ||
} | ||
|
||
#[Test] | ||
public function itThrowsExceptionWhenTheDeterminedContainerIsNotActuallyAContainer() | ||
{ | ||
$this->expectException(RuntimeException::class); | ||
|
||
$containerFilename = __DIR__.'/Fixtures/PsrContainerFakerProviderTest/VariableSettingContainerFile.php'; | ||
$sut = new PsrContainerFakerProvider($containerFilename, 'nonContainerVariable', 'generatorId'); | ||
$sut->getFaker(); | ||
} | ||
|
||
#[Test] | ||
public function itThrowsExceptionWhenTheContainerDoesNotHaveServiceWithId() | ||
{ | ||
$this->expectException(RuntimeException::class); | ||
|
||
$containerFilename = __DIR__.'/Fixtures/PsrContainerFakerProviderTest/ReturningContainerFile.php'; | ||
$sut = new PsrContainerFakerProvider($containerFilename, null, 'id-not-in-container'); | ||
$sut->getFaker(); | ||
} | ||
|
||
#[Test] | ||
public function itThrowsExceptionWhenTheNamedServiceIsNotAGenerator() | ||
{ | ||
$this->expectException(RuntimeException::class); | ||
|
||
$containerFilename = __DIR__.'/Fixtures/PsrContainerFakerProviderTest/ReturningContainerFile.php'; | ||
$sut = new PsrContainerFakerProvider($containerFilename, null, 'notGeneratorId'); | ||
$sut->getFaker(); | ||
} | ||
} |