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