• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemsdk / docker-symfony-api / #82

pending completion
#82

push

DKravtsov
Updated composer dependencies, refactoring.

45 of 45 new or added lines in 12 files covered. (100.0%)

1483 of 2844 relevant lines covered (52.14%)

22.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/General/Application/Utils/Tests/PhpUnitUtil.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App\General\Application\Utils\Tests;
6

7
use App\General\Domain\Doctrine\DBAL\Types\Types as AppTypes;
8
use App\General\Domain\Enum\Language;
9
use App\Role\Domain\Entity\Role;
10
use DateTime;
11
use DateTimeImmutable;
12
use Doctrine\DBAL\Types\Type;
13
use Exception;
14
use LogicException;
15
use Ramsey\Uuid\UuidInterface;
16
use RecursiveDirectoryIterator;
17
use RecursiveIteratorIterator;
18
use ReflectionClass;
19
use ReflectionException;
20
use ReflectionMethod;
21
use RegexIterator;
22
use stdClass;
23
use Symfony\Bundle\FrameworkBundle\Console\Application;
24
use Symfony\Component\Console\Input\ArrayInput;
25
use Symfony\Component\Console\Output\ConsoleOutput;
26
use Symfony\Component\HttpKernel\KernelInterface;
27
use Throwable;
28

29
use function array_key_exists;
30
use function explode;
31
use function sprintf;
32
use function str_contains;
33
use function substr_count;
34

35
/**
36
 * Class PHPUnitUtil
37
 *
38
 * @package App\General
39
 */
40
class PhpUnitUtil
41
{
42
    public const TYPE_INT = 'int';
43
    public const TYPE_INTEGER = 'integer';
44
    public const TYPE_STRING = 'string';
45
    public const TYPE_ARRAY = 'array';
46
    public const TYPE_JSON = 'json';
47
    public const TYPE_BOOL = 'bool';
48
    public const TYPE_BOOLEAN = 'boolean';
49
    public const TYPE_CUSTOM_CLASS = 'CustomClass';
50
    public const TYPE_ENUM = 'ENUM';
51

52
    /**
53
     * @var array<string, mixed>
54
     */
55
    private static array $validValueCache = [];
56

57
    /**
58
     * @var array<string, stdClass|DateTime|string>
59
     */
60
    private static array $invalidValueCache = [];
61

62
    /**
63
     * @codeCoverageIgnore
64
     *
65
     * @throws Exception
66
     */
67
    public static function loadFixtures(KernelInterface $kernel): void
68
    {
69
        $application = new Application($kernel);
70
        $application->setAutoExit(false);
71
        $input = new ArrayInput([
72
            'command' => 'doctrine:fixtures:load',
73
            '--no-interaction' => true,
74
            '--quiet' => true,
75
        ]);
76
        $input->setInteractive(false);
77
        $application->run($input, new ConsoleOutput(ConsoleOutput::VERBOSITY_QUIET));
78
    }
79

80
    /**
81
     * @codeCoverageIgnore
82
     *
83
     * @return array<int, string>
84
     */
85
    public static function recursiveFileSearch(string $folder, string $pattern): array
86
    {
87
        $dir = new RecursiveDirectoryIterator($folder);
88
        $ite = new RecursiveIteratorIterator($dir);
89
        /**
90
         * @phpstan-ignore-next-line
91
         *
92
         * @var array<int, string> $files
93
         */
94
        $files = new RegexIterator($ite, $pattern, RegexIterator::GET_MATCH);
95
        $fileList = [];
96

97
        foreach ($files as $file) {
98
            $fileList[] = $file[0];
99
        }
100

101
        return $fileList;
102
    }
103

104
    /**
105
     * Method to call specified 'protected' or 'private' method on given class.
106
     *
107
     * @param object $object The instantiated instance of your class
108
     * @param non-empty-string $name The name of your private/protected method
109
     * @param array<int, mixed> $args Method arguments
110
     *
111
     * @throws ReflectionException
112
     */
113
    public static function callMethod(object $object, string $name, array $args): mixed
114
    {
115
        return self::getMethod($object, $name)->invokeArgs($object, $args);
×
116
    }
117

118
    /**
119
     * Get a private or protected method for testing/documentation purposes.
120
     * How to use for MyClass->foo():
121
     *      $cls = new MyClass();
122
     *      $foo = PHPUnitUtil::getPrivateMethod($cls, 'foo');
123
     *      $foo->invoke($cls, $...);
124
     *
125
     * @param object $object The instantiated instance of your class
126
     * @param non-empty-string $name The name of your private/protected method
127
     *
128
     * @throws ReflectionException
129
     *
130
     * @return ReflectionMethod The method you asked for
131
     */
132
    public static function getMethod(object $object, string $name): ReflectionMethod
133
    {
134
        // Get reflection and make specified method accessible
135
        $class = new ReflectionClass($object);
×
136
        $method = $class->getMethod($name);
×
137
        $method->setAccessible(true);
×
138

139
        return $method;
×
140
    }
141

142
    /**
143
     * Helper method to get any property value from given class.
144
     *
145
     * @param non-empty-string $property
146
     *
147
     * @throws ReflectionException
148
     */
149
    public static function getProperty(string $property, object $object): mixed
150
    {
151
        $clazz = new ReflectionClass($object::class);
×
152
        $property = $clazz->getProperty($property);
×
153
        $property->setAccessible(true);
×
154

155
        return $property->getValue($object);
×
156
    }
157

158
    public static function getType(Type | string | null $type): string
159
    {
160
        $exception = new LogicException(
×
161
            sprintf(
×
162
                "Currently type '%s' is not supported within type normalizer",
×
163
                $type instanceof Type ? $type::class : (string)$type,
×
164
            ),
×
165
        );
×
166

167
        return match ($type) {
×
168
            'time', 'date', 'datetime' => DateTime::class,
×
169
            'time_immutable', 'date_immutable', 'datetime_immutable' => DateTimeImmutable::class,
×
170
            AppTypes::ENUM_LANGUAGE => Language::class,
×
171
            self::TYPE_INT, self::TYPE_INTEGER, 'bigint' => self::TYPE_INT,
×
172
            self::TYPE_STRING, 'text', 'EnumLocale', 'EnumLogLogin' => self::TYPE_STRING,
×
173
            self::TYPE_JSON => self::TYPE_JSON,
×
174
            self::TYPE_ARRAY => self::TYPE_ARRAY,
×
175
            self::TYPE_BOOL, self::TYPE_BOOLEAN => self::TYPE_BOOL,
×
176
            default => throw $exception,
×
177
        };
×
178
    }
179

180
    /**
181
     * Helper method to override any property value within given class.
182
     *
183
     * @param non-empty-string $property
184
     * @param UuidInterface|array<array-key, string>|null $value
185
     *
186
     * @throws ReflectionException
187
     */
188
    public static function setProperty(string $property, UuidInterface | array | null $value, object $object): void
189
    {
190
        $clazz = new ReflectionClass($object::class);
×
191
        $property = $clazz->getProperty($property);
×
192
        $property->setAccessible(true);
×
193
        $property->setValue($object, $value);
×
194
    }
195

196
    /**
197
     * Helper method to get valid value for specified type.
198
     *
199
     * @param array<string, string>|null $meta
200
     *
201
     * @throws Throwable
202
     */
203
    public static function getValidValueForType(string $type, ?array $meta = null): mixed
204
    {
205
        $cacheKey = $type . serialize($meta);
×
206

207
        if (!array_key_exists($cacheKey, self::$validValueCache)) {
×
208
            self::$validValueCache[$cacheKey] = self::getValidValue($meta, $type);
×
209
        }
210

211
        return self::$validValueCache[$cacheKey];
×
212
    }
213

214
    /**
215
     * Helper method to get invalid value for specified type.
216
     *
217
     * @throws Throwable
218
     */
219
    public static function getInvalidValueForType(string $type): DateTime | stdClass | string
220
    {
221
        if ($type !== stdClass::class && substr_count($type, '\\') > 1) {
×
222
            $type = self::TYPE_CUSTOM_CLASS;
×
223
        }
224

225
        if (!array_key_exists($type, self::$invalidValueCache)) {
×
226
            if (str_contains($type, '|')) {
×
227
                $output = self::getInvalidValueForType(explode('|', $type)[0]);
×
228
            } elseif (str_contains($type, '[]')) {
×
229
                $output = self::getInvalidValueForType(self::TYPE_ARRAY);
×
230
            } else {
231
                $output = match ($type) {
×
232
                    stdClass::class, DateTimeImmutable::class => new DateTime(),
×
233
                    self::TYPE_CUSTOM_CLASS, self::TYPE_INT, self::TYPE_INTEGER, self::TYPE_STRING, self::TYPE_ARRAY,
×
234
                    self::TYPE_BOOL, self::TYPE_BOOLEAN, DateTime::class, 'enumLanguage', 'enumLocale', 'enumLogLogin'
×
235
                        => new stdClass(),
×
236
                    default => throw new LogicException(sprintf("Cannot create invalid value for type '%s'.", $type)),
×
237
                };
×
238
            }
239

240
            self::$invalidValueCache[$type] = $output;
×
241
        }
242

243
        return self::$invalidValueCache[$type];
×
244
    }
245

246
    /**
247
     * @param array<string, string>|null $meta
248
     *
249
     * @throws Throwable
250
     */
251
    private static function getValidValue(
252
        ?array $meta,
253
        string $type
254
    ): mixed {
255
        $meta ??= [];
×
256

257
        $class = stdClass::class;
×
258
        $params = [null];
×
259

260
        if (substr_count($type, '\\') > 1 && !str_contains($type, '|')) {
×
261
            /** @var class-string $class */
262
            $class = $meta !== [] && array_key_exists('targetEntity', $meta) ? $meta['targetEntity'] : $type;
×
263

264
            $type = self::TYPE_CUSTOM_CLASS;
×
265

266
            if ((new ReflectionClass($class))->isEnum()) {
×
267
                $type = self::TYPE_ENUM;
×
268
            } else {
269
                /** @var class-string $class */
270
                $class = $class[0] === '\\' ? ltrim($class, '\\') : $class;
×
271
            }
272

273
            if ($class === Role::class) {
×
274
                $params = ['Some Role'];
×
275
            }
276
        }
277

278
        $output = match ($type) {
×
279
            self::TYPE_ENUM => current($class::cases()), // TODO: fix this
×
280
            self::TYPE_CUSTOM_CLASS => new $class(...$params),
×
281
            self::TYPE_INT, self::TYPE_INTEGER => 666,
×
282
            self::TYPE_STRING => 'Some text here',
×
283
            self::TYPE_ARRAY => ['some', self::TYPE_ARRAY, 'here'],
×
284
            self::TYPE_BOOL, self::TYPE_BOOLEAN => true,
×
285
            DateTime::class => new DateTime(),
×
286
            DateTimeImmutable::class => new DateTimeImmutable(),
×
287
            default => null,
×
288
        };
×
289

290
        if (str_contains($type, '|')) {
×
291
            $output = self::getValidValueForType(explode('|', $type)[0], $meta);
×
292
        } elseif (str_contains($type, '[]')) {
×
293
            /** @var array<mixed, object> $output */
294
            $output = self::getValidValueForType(self::TYPE_ARRAY, $meta);
×
295
        }
296

297
        return $output ?? throw new LogicException(sprintf("Cannot create valid value for type '%s'.", $type));
×
298
    }
299
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc