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