Skip to content

Commit a523440

Browse files
committed
Initial commit
1 parent e89b826 commit a523440

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+3092
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/composer.lock

composer.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "fakerphp/core",
3+
"type": "library",
4+
"description": "Faker Core contains the reference implementations for extensions",
5+
"keywords": [
6+
"faker",
7+
"core",
8+
"interface"
9+
],
10+
"license": "MIT",
11+
"authors": [
12+
{
13+
"name": "bram",
14+
"email": "[email protected]"
15+
}
16+
],
17+
"require": {
18+
"php": "^8.0",
19+
"psr/container": "^2.0"
20+
},
21+
"require-dev": {
22+
"bamarni/composer-bin-plugin": "^1.6"
23+
},
24+
"autoload": {
25+
"psr-4": {
26+
"Faker\\Core\\": "src/"
27+
}
28+
},
29+
"config": {
30+
"sort-packages": true,
31+
"allow-plugins": {
32+
"bamarni/composer-bin-plugin": true
33+
}
34+
}
35+
}

phpstan-baseline.neon

Whitespace-only changes.

phpstan.neon.dist

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
includes:
2+
- phpstan-baseline.neon
3+
4+
parameters:
5+
level: 8
6+
paths:
7+
- src
8+
tmpDir: .build/phpstan/

src/ChanceGenerator.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Faker\Core;
4+
5+
use Faker\Core\Extension\Extension;
6+
7+
/**
8+
* This generator returns a default value for all called properties
9+
* and methods. It works with Faker\Generator::optional().
10+
*
11+
* @mixin Generator
12+
*/
13+
class ChanceGenerator
14+
{
15+
private $generator;
16+
private $weight;
17+
protected $default;
18+
19+
/**
20+
* @param Extension|Generator $generator
21+
*/
22+
public function __construct($generator, float $weight, $default = null)
23+
{
24+
$this->default = $default;
25+
$this->generator = $generator;
26+
$this->weight = $weight;
27+
}
28+
29+
public function ext(string $id)
30+
{
31+
return new self($this->generator->ext($id), $this->weight, $this->default);
32+
}
33+
34+
/**
35+
* @param string $name
36+
* @param array $arguments
37+
*/
38+
public function __call($name, $arguments)
39+
{
40+
if (mt_rand(1, 100) <= (100 * $this->weight)) {
41+
return call_user_func_array([$this->generator, $name], $arguments);
42+
}
43+
44+
return $this->default;
45+
}
46+
}

src/Container/Container.php

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Faker\Core\Container;
6+
7+
use Faker\Core\Extension\Extension;
8+
9+
/**
10+
* A simple implementation of a container.
11+
*
12+
* @experimental This class is experimental and does not fall under our BC promise
13+
*/
14+
final class Container implements ContainerInterface
15+
{
16+
/**
17+
* @var array<string, callable|object|string>
18+
*/
19+
private $definitions;
20+
21+
private $services = [];
22+
23+
/**
24+
* Create a container object with a set of definitions. The array value MUST
25+
* produce an object that implements Extension.
26+
*
27+
* @param array<string, callable|object|string> $definitions
28+
*/
29+
public function __construct(array $definitions)
30+
{
31+
$this->definitions = $definitions;
32+
}
33+
34+
/**
35+
* Retrieve a definition from the container.
36+
*
37+
* @param string $id
38+
*
39+
* @throws \InvalidArgumentException
40+
* @throws \RuntimeException
41+
* @throws ContainerException
42+
* @throws NotInContainerException
43+
*/
44+
public function get($id): Extension
45+
{
46+
if (!is_string($id)) {
47+
throw new \InvalidArgumentException(sprintf(
48+
'First argument of %s::get() must be string',
49+
self::class
50+
));
51+
}
52+
53+
if (array_key_exists($id, $this->services)) {
54+
return $this->services[$id];
55+
}
56+
57+
if (!$this->has($id)) {
58+
throw new NotInContainerException(sprintf(
59+
'There is not service with id "%s" in the container.',
60+
$id
61+
));
62+
}
63+
64+
$definition = $this->definitions[$id];
65+
66+
$service = $this->services[$id] = $this->getService($id, $definition);
67+
68+
if (!$service instanceof Extension) {
69+
throw new \RuntimeException(sprintf(
70+
'Service resolved for identifier "%s" does not implement the %s" interface.',
71+
$id,
72+
Extension::class
73+
));
74+
}
75+
76+
return $service;
77+
}
78+
79+
/**
80+
* Get the service from a definition.
81+
*
82+
* @param callable|object|string $definition
83+
*/
84+
private function getService($id, $definition)
85+
{
86+
if (is_callable($definition)) {
87+
try {
88+
return $definition();
89+
} catch (\Throwable $e) {
90+
throw new ContainerException(
91+
sprintf('Error while invoking callable for "%s"', $id),
92+
0,
93+
$e
94+
);
95+
}
96+
} elseif (is_object($definition)) {
97+
return $definition;
98+
} elseif (is_string($definition)) {
99+
if (class_exists($definition)) {
100+
try {
101+
return new $definition();
102+
} catch (\Throwable $e) {
103+
throw new ContainerException(sprintf('Could not instantiate class "%s"', $id), 0, $e);
104+
}
105+
}
106+
107+
throw new ContainerException(sprintf(
108+
'Could not instantiate class "%s". Class was not found.',
109+
$id
110+
));
111+
} else {
112+
throw new ContainerException(sprintf(
113+
'Invalid type for definition with id "%s"',
114+
$id
115+
));
116+
}
117+
}
118+
119+
/**
120+
* Check if the container contains a given identifier.
121+
*
122+
* @param string $id
123+
*
124+
* @throws \InvalidArgumentException
125+
*/
126+
public function has($id): bool
127+
{
128+
if (!is_string($id)) {
129+
throw new \InvalidArgumentException(sprintf(
130+
'First argument of %s::get() must be string',
131+
self::class
132+
));
133+
}
134+
135+
return array_key_exists($id, $this->definitions);
136+
}
137+
138+
/**
139+
* Get the bindings between Extension interfaces and implementations.
140+
*/
141+
public function getDefinitions(): array
142+
{
143+
return $this->definitions;
144+
}
145+
}

src/Container/ContainerBuilder.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Faker\Core\Container;
6+
7+
use Faker\Core\Implementation;
8+
use Faker\Core\Extension\BarcodeExtension;
9+
use Faker\Core\Extension\BloodExtension;
10+
use Faker\Core\Extension\ColorExtension;
11+
use Faker\Core\Extension\DateTimeExtension;
12+
use Faker\Core\Extension\FileExtension;
13+
use Faker\Core\Extension\NumberExtension;
14+
use Faker\Core\Extension\UuidExtension;
15+
use Faker\Core\Extension\VersionExtension;
16+
17+
/**
18+
* @experimental This class is experimental and does not fall under our BC promise
19+
*/
20+
final class ContainerBuilder
21+
{
22+
/**
23+
* @var array<string, callable|object|string>
24+
*/
25+
private $definitions = [];
26+
27+
/**
28+
* @param callable|object|string $value
29+
*
30+
* @throws \InvalidArgumentException
31+
*/
32+
public function add($value, string $name = null): self
33+
{
34+
if (!is_string($value) && !is_callable($value) && !is_object($value)) {
35+
throw new \InvalidArgumentException(sprintf(
36+
'First argument to "%s::add()" must be a string, callable or object.',
37+
self::class
38+
));
39+
}
40+
41+
if ($name === null) {
42+
if (is_string($value)) {
43+
$name = $value;
44+
} elseif (is_object($value)) {
45+
$name = get_class($value);
46+
} else {
47+
throw new \InvalidArgumentException(sprintf(
48+
'Second argument to "%s::add()" is required not passing a string or object as first argument',
49+
self::class
50+
));
51+
}
52+
}
53+
54+
$this->definitions[$name] = $value;
55+
56+
return $this;
57+
}
58+
59+
public function build(): ContainerInterface
60+
{
61+
return new Container($this->definitions);
62+
}
63+
64+
/**
65+
* Get an array with extension that represent the default English
66+
* functionality.
67+
*/
68+
public static function defaultExtensions(): array
69+
{
70+
return [
71+
BarcodeExtension::class => Implementation\Barcode::class,
72+
BloodExtension::class => Implementation\Blood::class,
73+
ColorExtension::class => Implementation\Color::class,
74+
DateTimeExtension::class => Implementation\DateTime::class,
75+
FileExtension::class => Implementation\File::class,
76+
NumberExtension::class => Implementation\Number::class,
77+
VersionExtension::class => Implementation\Version::class,
78+
UuidExtension::class => Implementation\Uuid::class,
79+
];
80+
}
81+
82+
public static function getDefault(): ContainerInterface
83+
{
84+
$instance = new self();
85+
86+
foreach (self::defaultExtensions() as $id => $definition) {
87+
$instance->add($definition, $id);
88+
}
89+
90+
return $instance->build();
91+
}
92+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Faker\Core\Container;
6+
7+
use Psr\Container\ContainerExceptionInterface;
8+
9+
/**
10+
* @experimental This class is experimental and does not fall under our BC promise
11+
*/
12+
final class ContainerException extends \RuntimeException implements ContainerExceptionInterface
13+
{
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Faker\Core\Container;
4+
5+
use Psr\Container\ContainerInterface as BaseContainerInterface;
6+
7+
interface ContainerInterface extends BaseContainerInterface
8+
{
9+
/**
10+
* Get the bindings between Extension interfaces and implementations.
11+
*/
12+
public function getDefinitions(): array;
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Faker\Core\Container;
6+
7+
use Psr\Container\NotFoundExceptionInterface;
8+
9+
/**
10+
* @experimental This class is experimental and does not fall under our BC promise
11+
*/
12+
final class NotInContainerException extends \RuntimeException implements NotFoundExceptionInterface
13+
{
14+
}

0 commit comments

Comments
 (0)