Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.43% covered (success)
91.43%
64 / 70
50.00% covered (danger)
50.00%
3 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApieObjectFaker
91.43% covered (success)
91.43%
64 / 70
50.00% covered (danger)
50.00%
3 / 6
29.53
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 createWithDefaultFakers
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
1
 fakeClass
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 fakeArgumentsOfMethod
72.73% covered (warning)
72.73%
8 / 11
0.00% covered (danger)
0.00%
0 / 1
6.73
 fakeMixed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fakeFromType
92.59% covered (success)
92.59%
25 / 27
0.00% covered (danger)
0.00%
0 / 1
17.12
1<?php
2namespace Apie\Faker;
3
4use Apie\Core\Exceptions\InvalidTypeException;
5use Apie\Core\Identifiers\IdentifierInterface;
6use Apie\Faker\Exceptions\ClassCanNotBeFakedException;
7use Apie\Faker\Fakers\CheckBaseClassFaker;
8use Apie\Faker\Fakers\CompositeObjectFaker;
9use Apie\Faker\Fakers\DateValueObjectFaker;
10use Apie\Faker\Fakers\DurationFaker;
11use Apie\Faker\Fakers\EnumFaker;
12use Apie\Faker\Fakers\ItemHashmapFaker;
13use Apie\Faker\Fakers\ItemListFaker;
14use Apie\Faker\Fakers\ItemSetFaker;
15use Apie\Faker\Fakers\LimitedOptionsFaker;
16use Apie\Faker\Fakers\PasswordValueObjectFaker;
17use Apie\Faker\Fakers\PhpDateTimeObjectFaker;
18use Apie\Faker\Fakers\PolymorphicEntityFaker;
19use Apie\Faker\Fakers\StringableFaker;
20use Apie\Faker\Fakers\StringValueObjectWithRegexFaker;
21use Apie\Faker\Fakers\UploadedFileFaker;
22use Apie\Faker\Fakers\UriFaker;
23use Apie\Faker\Fakers\UseConstructorFaker;
24use Apie\Faker\Fakers\UseFakeMethodFaker;
25use Apie\Faker\Interfaces\ApieClassFaker;
26use Apie\TypeConverter\ReflectionTypeFactory;
27use Faker\Generator;
28use Faker\Provider\Base;
29use ReflectionClass;
30use ReflectionIntersectionType;
31use ReflectionMethod;
32use ReflectionType;
33use ReflectionUnionType;
34
35/**
36 * This is a stub class
37 */
38final class ApieObjectFaker extends Base
39{
40    /**
41     * @var array<ApieClassFaker<object>> $fakers
42     */
43    private array $fakers;
44
45    /**
46     * @param ApieClassFaker<object> $fakers
47     */
48    public function __construct(Generator $generator, ApieClassFaker... $fakers)
49    {
50        $this->fakers = $fakers;
51        parent::__construct($generator);
52    }
53
54    /**
55     * @param ApieClassFaker<object> $additional
56     */
57    public static function createWithDefaultFakers(Generator $generator, ApieClassFaker... $additional): self
58    {
59        return new self(
60            $generator,
61            ...[
62                ...$additional,
63                new UseFakeMethodFaker(),
64                new CheckBaseClassFaker(new ReflectionClass(IdentifierInterface::class)),
65                new UploadedFileFaker(),
66                new PolymorphicEntityFaker(),
67                new ItemListFaker(),
68                new ItemHashmapFaker(),
69                new ItemSetFaker(),
70                new PasswordValueObjectFaker(),
71                new DateValueObjectFaker(),
72                new StringValueObjectWithRegexFaker(),
73                new EnumFaker(),
74                new LimitedOptionsFaker(),
75                new CompositeObjectFaker(),
76                new DurationFaker(),
77                new UriFaker(),
78                new PhpDateTimeObjectFaker(),
79                new StringableFaker(),
80                new UseConstructorFaker(),
81            ]
82        );
83    }
84
85    /**
86     * @template T of object
87     * @param class-string<T> $className
88     * @return T
89     */
90    public function fakeClass(string $className): object
91    {
92        $refl = new ReflectionClass($className);
93        foreach ($this->fakers as $faker) {
94            if ($faker->supports($refl)) {
95                return $faker->fakeFor($this->generator, $refl);
96            }
97        }
98
99        throw new ClassCanNotBeFakedException($refl);
100    }
101
102    /**
103     * @return array<int, mixed>
104     */
105    public function fakeArgumentsOfMethod(ReflectionMethod $method): array
106    {
107        $arguments = [];
108        foreach ($method->getParameters() as $parameter) {
109            $type = $parameter->getType();
110            if ($parameter->isVariadic()) {
111                $rand = $this->generator->numberBetween(0, 4);
112                for ($i = 0; $i < $rand; $i++) {
113                    $arguments[] = $this->generator->fakeFromType($type);
114                }
115            } elseif ($parameter->allowsNull() && 1 === $this->generator->numberBetween(0, 4)) {
116                $arguments[] = null;
117            } else {
118                $arguments[] = $this->generator->fakeFromType($type);
119            }
120        }
121
122        return $arguments;
123    }
124
125    public function fakeMixed(): mixed
126    {
127        return $this->fakeFromType(ReflectionTypeFactory::createReflectionType('array|null|bool|float|string|int'));
128    }
129
130    public function fakeFromType(?ReflectionType $typehint): mixed
131    {
132        if ($typehint === null) {
133            return $this->fakeMixed();
134        }
135        if ($typehint instanceof ReflectionIntersectionType) {
136            throw new InvalidTypeException($typehint, 'ReflectionUnionType|ReflectionNamedType');
137        }
138        $types = $typehint instanceof ReflectionUnionType ? $typehint->getTypes() : [$typehint];
139        $type = $this->generator->randomElement($types);
140        if ($type->getName() === Generator::class) {
141            return $this->generator;
142        }
143        if ($type->allowsNull()) {
144            if ($this->generator->boolean()) {
145                return null;
146            }
147        }
148        return match ($type->getName()) {
149            'array' => [
150                $this->generator->word() => $this->generator->fakeMixed(),
151                $this->generator->word() => $this->generator->fakeMixed(),
152                $this->generator->word() => $this->generator->fakeMixed(),
153            ],
154            'null' => null,
155            'false' => false,
156            'true' => true,
157            'bool' => $this->generator->randomElement([true, false]),
158            'float' => $this->generator->randomFloat(),
159            'string' => $this->generator->word(),
160            'int' => $this->generator->numberBetween(-2147483648, 2147483647), // compatible with integers in Mysql
161            'mixed' => $this->fakeMixed(),
162            default => $this->fakeClass($type->getName()),
163        };
164    }
165}