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