├── .editorconfig
├── .gitattributes
├── .gitignore
├── composer.json
├── composer.lock
├── license.md
├── phpcs.xml
├── phpunit.xml
├── readme.md
└── src
├── Attributes
└── PostMapping.php
├── Exceptions
├── CouldNotMapValueException.php
├── CouldNotResolveClassException.php
├── InvalidTypeException.php
├── UnexpectedNullValueException.php
└── UnexpectedTokenException.php
├── Mapper.php
├── MapperCommands.php
├── MapperConfig.php
├── MapsItself.php
├── Objects
├── ClassBluePrint.php
├── ClassBluePrinter.php
├── ClassResolver.php
├── DocBlock.php
├── DocBlockParser.php
└── ObjectMapper.php
└── Types
├── DataType.php
├── DataTypeCollection.php
└── DataTypeFactory.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | .github/ export-ignore
2 | tests/ export-ignore
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | vendor/
3 | jmapper_*
4 | .phpunit.result.cache
5 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jerodev/data-mapper",
3 | "description": "Maps raw data to a typed PHP object",
4 | "type": "library",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Jeroen Deviaene",
9 | "email": "jeroen@deviaene.eu"
10 | }
11 | ],
12 | "autoload": {
13 | "psr-4": {
14 | "Jerodev\\DataMapper\\": "src/"
15 | }
16 | },
17 | "autoload-dev": {
18 | "psr-4": {
19 | "Jerodev\\DataMapper\\Tests\\": "tests/"
20 | }
21 | },
22 | "repositories": [
23 | {
24 | "type": "vcs",
25 | "url": "git@github.com:jerodev/code-styles.git"
26 | }
27 | ],
28 | "require": {
29 | "php": "^8.1"
30 | },
31 | "require-dev": {
32 | "jerodev/code-styles": "dev-master",
33 | "phpunit/phpunit": "^9.4"
34 | },
35 | "minimum-stability": "dev",
36 | "prefer-stable": true,
37 | "config": {
38 | "sort-packages": true,
39 | "allow-plugins": {
40 | "dealerdirect/phpcodesniffer-composer-installer": true
41 | }
42 | },
43 | "scripts": {
44 | "post-autoload-dump": [
45 | "Jerodev\\DataMapper\\MapperCommands::clearCache"
46 | ]
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "7b010f8270a95cc28b36c706e8fddfeb",
8 | "packages": [],
9 | "packages-dev": [
10 | {
11 | "name": "dealerdirect/phpcodesniffer-composer-installer",
12 | "version": "v1.0.0",
13 | "source": {
14 | "type": "git",
15 | "url": "https://github.com/PHPCSStandards/composer-installer.git",
16 | "reference": "4be43904336affa5c2f70744a348312336afd0da"
17 | },
18 | "dist": {
19 | "type": "zip",
20 | "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da",
21 | "reference": "4be43904336affa5c2f70744a348312336afd0da",
22 | "shasum": ""
23 | },
24 | "require": {
25 | "composer-plugin-api": "^1.0 || ^2.0",
26 | "php": ">=5.4",
27 | "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
28 | },
29 | "require-dev": {
30 | "composer/composer": "*",
31 | "ext-json": "*",
32 | "ext-zip": "*",
33 | "php-parallel-lint/php-parallel-lint": "^1.3.1",
34 | "phpcompatibility/php-compatibility": "^9.0",
35 | "yoast/phpunit-polyfills": "^1.0"
36 | },
37 | "type": "composer-plugin",
38 | "extra": {
39 | "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
40 | },
41 | "autoload": {
42 | "psr-4": {
43 | "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
44 | }
45 | },
46 | "notification-url": "https://packagist.org/downloads/",
47 | "license": [
48 | "MIT"
49 | ],
50 | "authors": [
51 | {
52 | "name": "Franck Nijhof",
53 | "email": "franck.nijhof@dealerdirect.com",
54 | "homepage": "http://www.frenck.nl",
55 | "role": "Developer / IT Manager"
56 | },
57 | {
58 | "name": "Contributors",
59 | "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
60 | }
61 | ],
62 | "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
63 | "homepage": "http://www.dealerdirect.com",
64 | "keywords": [
65 | "PHPCodeSniffer",
66 | "PHP_CodeSniffer",
67 | "code quality",
68 | "codesniffer",
69 | "composer",
70 | "installer",
71 | "phpcbf",
72 | "phpcs",
73 | "plugin",
74 | "qa",
75 | "quality",
76 | "standard",
77 | "standards",
78 | "style guide",
79 | "stylecheck",
80 | "tests"
81 | ],
82 | "support": {
83 | "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
84 | "source": "https://github.com/PHPCSStandards/composer-installer"
85 | },
86 | "time": "2023-01-05T11:28:13+00:00"
87 | },
88 | {
89 | "name": "doctrine/instantiator",
90 | "version": "2.0.0",
91 | "source": {
92 | "type": "git",
93 | "url": "https://github.com/doctrine/instantiator.git",
94 | "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0"
95 | },
96 | "dist": {
97 | "type": "zip",
98 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
99 | "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
100 | "shasum": ""
101 | },
102 | "require": {
103 | "php": "^8.1"
104 | },
105 | "require-dev": {
106 | "doctrine/coding-standard": "^11",
107 | "ext-pdo": "*",
108 | "ext-phar": "*",
109 | "phpbench/phpbench": "^1.2",
110 | "phpstan/phpstan": "^1.9.4",
111 | "phpstan/phpstan-phpunit": "^1.3",
112 | "phpunit/phpunit": "^9.5.27",
113 | "vimeo/psalm": "^5.4"
114 | },
115 | "type": "library",
116 | "autoload": {
117 | "psr-4": {
118 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
119 | }
120 | },
121 | "notification-url": "https://packagist.org/downloads/",
122 | "license": [
123 | "MIT"
124 | ],
125 | "authors": [
126 | {
127 | "name": "Marco Pivetta",
128 | "email": "ocramius@gmail.com",
129 | "homepage": "https://ocramius.github.io/"
130 | }
131 | ],
132 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
133 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
134 | "keywords": [
135 | "constructor",
136 | "instantiate"
137 | ],
138 | "support": {
139 | "issues": "https://github.com/doctrine/instantiator/issues",
140 | "source": "https://github.com/doctrine/instantiator/tree/2.0.0"
141 | },
142 | "funding": [
143 | {
144 | "url": "https://www.doctrine-project.org/sponsorship.html",
145 | "type": "custom"
146 | },
147 | {
148 | "url": "https://www.patreon.com/phpdoctrine",
149 | "type": "patreon"
150 | },
151 | {
152 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
153 | "type": "tidelift"
154 | }
155 | ],
156 | "time": "2022-12-30T00:23:10+00:00"
157 | },
158 | {
159 | "name": "jerodev/code-styles",
160 | "version": "dev-master",
161 | "source": {
162 | "type": "git",
163 | "url": "https://github.com/jerodev/code-styles.git",
164 | "reference": "3a0d433404bc5d9f398619d28130affcf25eca76"
165 | },
166 | "dist": {
167 | "type": "zip",
168 | "url": "https://api.github.com/repos/jerodev/code-styles/zipball/3a0d433404bc5d9f398619d28130affcf25eca76",
169 | "reference": "3a0d433404bc5d9f398619d28130affcf25eca76",
170 | "shasum": ""
171 | },
172 | "require": {
173 | "slevomat/coding-standard": "^8.0",
174 | "squizlabs/php_codesniffer": "^3.6"
175 | },
176 | "default-branch": true,
177 | "type": "library",
178 | "license": [
179 | "private"
180 | ],
181 | "authors": [
182 | {
183 | "name": "jerodev",
184 | "email": "jeroen@deviaene.eu"
185 | }
186 | ],
187 | "description": "phpcs configurations for projects",
188 | "support": {
189 | "source": "https://github.com/jerodev/code-styles/tree/master",
190 | "issues": "https://github.com/jerodev/code-styles/issues"
191 | },
192 | "time": "2023-05-10T18:43:22+00:00"
193 | },
194 | {
195 | "name": "myclabs/deep-copy",
196 | "version": "1.11.1",
197 | "source": {
198 | "type": "git",
199 | "url": "https://github.com/myclabs/DeepCopy.git",
200 | "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
201 | },
202 | "dist": {
203 | "type": "zip",
204 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
205 | "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
206 | "shasum": ""
207 | },
208 | "require": {
209 | "php": "^7.1 || ^8.0"
210 | },
211 | "conflict": {
212 | "doctrine/collections": "<1.6.8",
213 | "doctrine/common": "<2.13.3 || >=3,<3.2.2"
214 | },
215 | "require-dev": {
216 | "doctrine/collections": "^1.6.8",
217 | "doctrine/common": "^2.13.3 || ^3.2.2",
218 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
219 | },
220 | "type": "library",
221 | "autoload": {
222 | "files": [
223 | "src/DeepCopy/deep_copy.php"
224 | ],
225 | "psr-4": {
226 | "DeepCopy\\": "src/DeepCopy/"
227 | }
228 | },
229 | "notification-url": "https://packagist.org/downloads/",
230 | "license": [
231 | "MIT"
232 | ],
233 | "description": "Create deep copies (clones) of your objects",
234 | "keywords": [
235 | "clone",
236 | "copy",
237 | "duplicate",
238 | "object",
239 | "object graph"
240 | ],
241 | "support": {
242 | "issues": "https://github.com/myclabs/DeepCopy/issues",
243 | "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
244 | },
245 | "funding": [
246 | {
247 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
248 | "type": "tidelift"
249 | }
250 | ],
251 | "time": "2023-03-08T13:26:56+00:00"
252 | },
253 | {
254 | "name": "nikic/php-parser",
255 | "version": "v5.0.0",
256 | "source": {
257 | "type": "git",
258 | "url": "https://github.com/nikic/PHP-Parser.git",
259 | "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc"
260 | },
261 | "dist": {
262 | "type": "zip",
263 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
264 | "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
265 | "shasum": ""
266 | },
267 | "require": {
268 | "ext-ctype": "*",
269 | "ext-json": "*",
270 | "ext-tokenizer": "*",
271 | "php": ">=7.4"
272 | },
273 | "require-dev": {
274 | "ircmaxell/php-yacc": "^0.0.7",
275 | "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
276 | },
277 | "bin": [
278 | "bin/php-parse"
279 | ],
280 | "type": "library",
281 | "extra": {
282 | "branch-alias": {
283 | "dev-master": "5.0-dev"
284 | }
285 | },
286 | "autoload": {
287 | "psr-4": {
288 | "PhpParser\\": "lib/PhpParser"
289 | }
290 | },
291 | "notification-url": "https://packagist.org/downloads/",
292 | "license": [
293 | "BSD-3-Clause"
294 | ],
295 | "authors": [
296 | {
297 | "name": "Nikita Popov"
298 | }
299 | ],
300 | "description": "A PHP parser written in PHP",
301 | "keywords": [
302 | "parser",
303 | "php"
304 | ],
305 | "support": {
306 | "issues": "https://github.com/nikic/PHP-Parser/issues",
307 | "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0"
308 | },
309 | "time": "2024-01-07T17:17:35+00:00"
310 | },
311 | {
312 | "name": "phar-io/manifest",
313 | "version": "2.0.3",
314 | "source": {
315 | "type": "git",
316 | "url": "https://github.com/phar-io/manifest.git",
317 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
318 | },
319 | "dist": {
320 | "type": "zip",
321 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
322 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
323 | "shasum": ""
324 | },
325 | "require": {
326 | "ext-dom": "*",
327 | "ext-phar": "*",
328 | "ext-xmlwriter": "*",
329 | "phar-io/version": "^3.0.1",
330 | "php": "^7.2 || ^8.0"
331 | },
332 | "type": "library",
333 | "extra": {
334 | "branch-alias": {
335 | "dev-master": "2.0.x-dev"
336 | }
337 | },
338 | "autoload": {
339 | "classmap": [
340 | "src/"
341 | ]
342 | },
343 | "notification-url": "https://packagist.org/downloads/",
344 | "license": [
345 | "BSD-3-Clause"
346 | ],
347 | "authors": [
348 | {
349 | "name": "Arne Blankerts",
350 | "email": "arne@blankerts.de",
351 | "role": "Developer"
352 | },
353 | {
354 | "name": "Sebastian Heuer",
355 | "email": "sebastian@phpeople.de",
356 | "role": "Developer"
357 | },
358 | {
359 | "name": "Sebastian Bergmann",
360 | "email": "sebastian@phpunit.de",
361 | "role": "Developer"
362 | }
363 | ],
364 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
365 | "support": {
366 | "issues": "https://github.com/phar-io/manifest/issues",
367 | "source": "https://github.com/phar-io/manifest/tree/2.0.3"
368 | },
369 | "time": "2021-07-20T11:28:43+00:00"
370 | },
371 | {
372 | "name": "phar-io/version",
373 | "version": "3.2.1",
374 | "source": {
375 | "type": "git",
376 | "url": "https://github.com/phar-io/version.git",
377 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
378 | },
379 | "dist": {
380 | "type": "zip",
381 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
382 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
383 | "shasum": ""
384 | },
385 | "require": {
386 | "php": "^7.2 || ^8.0"
387 | },
388 | "type": "library",
389 | "autoload": {
390 | "classmap": [
391 | "src/"
392 | ]
393 | },
394 | "notification-url": "https://packagist.org/downloads/",
395 | "license": [
396 | "BSD-3-Clause"
397 | ],
398 | "authors": [
399 | {
400 | "name": "Arne Blankerts",
401 | "email": "arne@blankerts.de",
402 | "role": "Developer"
403 | },
404 | {
405 | "name": "Sebastian Heuer",
406 | "email": "sebastian@phpeople.de",
407 | "role": "Developer"
408 | },
409 | {
410 | "name": "Sebastian Bergmann",
411 | "email": "sebastian@phpunit.de",
412 | "role": "Developer"
413 | }
414 | ],
415 | "description": "Library for handling version information and constraints",
416 | "support": {
417 | "issues": "https://github.com/phar-io/version/issues",
418 | "source": "https://github.com/phar-io/version/tree/3.2.1"
419 | },
420 | "time": "2022-02-21T01:04:05+00:00"
421 | },
422 | {
423 | "name": "phpstan/phpdoc-parser",
424 | "version": "1.25.0",
425 | "source": {
426 | "type": "git",
427 | "url": "https://github.com/phpstan/phpdoc-parser.git",
428 | "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240"
429 | },
430 | "dist": {
431 | "type": "zip",
432 | "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bd84b629c8de41aa2ae82c067c955e06f1b00240",
433 | "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240",
434 | "shasum": ""
435 | },
436 | "require": {
437 | "php": "^7.2 || ^8.0"
438 | },
439 | "require-dev": {
440 | "doctrine/annotations": "^2.0",
441 | "nikic/php-parser": "^4.15",
442 | "php-parallel-lint/php-parallel-lint": "^1.2",
443 | "phpstan/extension-installer": "^1.0",
444 | "phpstan/phpstan": "^1.5",
445 | "phpstan/phpstan-phpunit": "^1.1",
446 | "phpstan/phpstan-strict-rules": "^1.0",
447 | "phpunit/phpunit": "^9.5",
448 | "symfony/process": "^5.2"
449 | },
450 | "type": "library",
451 | "autoload": {
452 | "psr-4": {
453 | "PHPStan\\PhpDocParser\\": [
454 | "src/"
455 | ]
456 | }
457 | },
458 | "notification-url": "https://packagist.org/downloads/",
459 | "license": [
460 | "MIT"
461 | ],
462 | "description": "PHPDoc parser with support for nullable, intersection and generic types",
463 | "support": {
464 | "issues": "https://github.com/phpstan/phpdoc-parser/issues",
465 | "source": "https://github.com/phpstan/phpdoc-parser/tree/1.25.0"
466 | },
467 | "time": "2024-01-04T17:06:16+00:00"
468 | },
469 | {
470 | "name": "phpunit/php-code-coverage",
471 | "version": "9.2.30",
472 | "source": {
473 | "type": "git",
474 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
475 | "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089"
476 | },
477 | "dist": {
478 | "type": "zip",
479 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089",
480 | "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089",
481 | "shasum": ""
482 | },
483 | "require": {
484 | "ext-dom": "*",
485 | "ext-libxml": "*",
486 | "ext-xmlwriter": "*",
487 | "nikic/php-parser": "^4.18 || ^5.0",
488 | "php": ">=7.3",
489 | "phpunit/php-file-iterator": "^3.0.3",
490 | "phpunit/php-text-template": "^2.0.2",
491 | "sebastian/code-unit-reverse-lookup": "^2.0.2",
492 | "sebastian/complexity": "^2.0",
493 | "sebastian/environment": "^5.1.2",
494 | "sebastian/lines-of-code": "^1.0.3",
495 | "sebastian/version": "^3.0.1",
496 | "theseer/tokenizer": "^1.2.0"
497 | },
498 | "require-dev": {
499 | "phpunit/phpunit": "^9.3"
500 | },
501 | "suggest": {
502 | "ext-pcov": "PHP extension that provides line coverage",
503 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
504 | },
505 | "type": "library",
506 | "extra": {
507 | "branch-alias": {
508 | "dev-master": "9.2-dev"
509 | }
510 | },
511 | "autoload": {
512 | "classmap": [
513 | "src/"
514 | ]
515 | },
516 | "notification-url": "https://packagist.org/downloads/",
517 | "license": [
518 | "BSD-3-Clause"
519 | ],
520 | "authors": [
521 | {
522 | "name": "Sebastian Bergmann",
523 | "email": "sebastian@phpunit.de",
524 | "role": "lead"
525 | }
526 | ],
527 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
528 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
529 | "keywords": [
530 | "coverage",
531 | "testing",
532 | "xunit"
533 | ],
534 | "support": {
535 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
536 | "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
537 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30"
538 | },
539 | "funding": [
540 | {
541 | "url": "https://github.com/sebastianbergmann",
542 | "type": "github"
543 | }
544 | ],
545 | "time": "2023-12-22T06:47:57+00:00"
546 | },
547 | {
548 | "name": "phpunit/php-file-iterator",
549 | "version": "3.0.6",
550 | "source": {
551 | "type": "git",
552 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
553 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
554 | },
555 | "dist": {
556 | "type": "zip",
557 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
558 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
559 | "shasum": ""
560 | },
561 | "require": {
562 | "php": ">=7.3"
563 | },
564 | "require-dev": {
565 | "phpunit/phpunit": "^9.3"
566 | },
567 | "type": "library",
568 | "extra": {
569 | "branch-alias": {
570 | "dev-master": "3.0-dev"
571 | }
572 | },
573 | "autoload": {
574 | "classmap": [
575 | "src/"
576 | ]
577 | },
578 | "notification-url": "https://packagist.org/downloads/",
579 | "license": [
580 | "BSD-3-Clause"
581 | ],
582 | "authors": [
583 | {
584 | "name": "Sebastian Bergmann",
585 | "email": "sebastian@phpunit.de",
586 | "role": "lead"
587 | }
588 | ],
589 | "description": "FilterIterator implementation that filters files based on a list of suffixes.",
590 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
591 | "keywords": [
592 | "filesystem",
593 | "iterator"
594 | ],
595 | "support": {
596 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
597 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
598 | },
599 | "funding": [
600 | {
601 | "url": "https://github.com/sebastianbergmann",
602 | "type": "github"
603 | }
604 | ],
605 | "time": "2021-12-02T12:48:52+00:00"
606 | },
607 | {
608 | "name": "phpunit/php-invoker",
609 | "version": "3.1.1",
610 | "source": {
611 | "type": "git",
612 | "url": "https://github.com/sebastianbergmann/php-invoker.git",
613 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
614 | },
615 | "dist": {
616 | "type": "zip",
617 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
618 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
619 | "shasum": ""
620 | },
621 | "require": {
622 | "php": ">=7.3"
623 | },
624 | "require-dev": {
625 | "ext-pcntl": "*",
626 | "phpunit/phpunit": "^9.3"
627 | },
628 | "suggest": {
629 | "ext-pcntl": "*"
630 | },
631 | "type": "library",
632 | "extra": {
633 | "branch-alias": {
634 | "dev-master": "3.1-dev"
635 | }
636 | },
637 | "autoload": {
638 | "classmap": [
639 | "src/"
640 | ]
641 | },
642 | "notification-url": "https://packagist.org/downloads/",
643 | "license": [
644 | "BSD-3-Clause"
645 | ],
646 | "authors": [
647 | {
648 | "name": "Sebastian Bergmann",
649 | "email": "sebastian@phpunit.de",
650 | "role": "lead"
651 | }
652 | ],
653 | "description": "Invoke callables with a timeout",
654 | "homepage": "https://github.com/sebastianbergmann/php-invoker/",
655 | "keywords": [
656 | "process"
657 | ],
658 | "support": {
659 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
660 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
661 | },
662 | "funding": [
663 | {
664 | "url": "https://github.com/sebastianbergmann",
665 | "type": "github"
666 | }
667 | ],
668 | "time": "2020-09-28T05:58:55+00:00"
669 | },
670 | {
671 | "name": "phpunit/php-text-template",
672 | "version": "2.0.4",
673 | "source": {
674 | "type": "git",
675 | "url": "https://github.com/sebastianbergmann/php-text-template.git",
676 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
677 | },
678 | "dist": {
679 | "type": "zip",
680 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
681 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
682 | "shasum": ""
683 | },
684 | "require": {
685 | "php": ">=7.3"
686 | },
687 | "require-dev": {
688 | "phpunit/phpunit": "^9.3"
689 | },
690 | "type": "library",
691 | "extra": {
692 | "branch-alias": {
693 | "dev-master": "2.0-dev"
694 | }
695 | },
696 | "autoload": {
697 | "classmap": [
698 | "src/"
699 | ]
700 | },
701 | "notification-url": "https://packagist.org/downloads/",
702 | "license": [
703 | "BSD-3-Clause"
704 | ],
705 | "authors": [
706 | {
707 | "name": "Sebastian Bergmann",
708 | "email": "sebastian@phpunit.de",
709 | "role": "lead"
710 | }
711 | ],
712 | "description": "Simple template engine.",
713 | "homepage": "https://github.com/sebastianbergmann/php-text-template/",
714 | "keywords": [
715 | "template"
716 | ],
717 | "support": {
718 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
719 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
720 | },
721 | "funding": [
722 | {
723 | "url": "https://github.com/sebastianbergmann",
724 | "type": "github"
725 | }
726 | ],
727 | "time": "2020-10-26T05:33:50+00:00"
728 | },
729 | {
730 | "name": "phpunit/php-timer",
731 | "version": "5.0.3",
732 | "source": {
733 | "type": "git",
734 | "url": "https://github.com/sebastianbergmann/php-timer.git",
735 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
736 | },
737 | "dist": {
738 | "type": "zip",
739 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
740 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
741 | "shasum": ""
742 | },
743 | "require": {
744 | "php": ">=7.3"
745 | },
746 | "require-dev": {
747 | "phpunit/phpunit": "^9.3"
748 | },
749 | "type": "library",
750 | "extra": {
751 | "branch-alias": {
752 | "dev-master": "5.0-dev"
753 | }
754 | },
755 | "autoload": {
756 | "classmap": [
757 | "src/"
758 | ]
759 | },
760 | "notification-url": "https://packagist.org/downloads/",
761 | "license": [
762 | "BSD-3-Clause"
763 | ],
764 | "authors": [
765 | {
766 | "name": "Sebastian Bergmann",
767 | "email": "sebastian@phpunit.de",
768 | "role": "lead"
769 | }
770 | ],
771 | "description": "Utility class for timing",
772 | "homepage": "https://github.com/sebastianbergmann/php-timer/",
773 | "keywords": [
774 | "timer"
775 | ],
776 | "support": {
777 | "issues": "https://github.com/sebastianbergmann/php-timer/issues",
778 | "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
779 | },
780 | "funding": [
781 | {
782 | "url": "https://github.com/sebastianbergmann",
783 | "type": "github"
784 | }
785 | ],
786 | "time": "2020-10-26T13:16:10+00:00"
787 | },
788 | {
789 | "name": "phpunit/phpunit",
790 | "version": "9.6.16",
791 | "source": {
792 | "type": "git",
793 | "url": "https://github.com/sebastianbergmann/phpunit.git",
794 | "reference": "3767b2c56ce02d01e3491046f33466a1ae60a37f"
795 | },
796 | "dist": {
797 | "type": "zip",
798 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3767b2c56ce02d01e3491046f33466a1ae60a37f",
799 | "reference": "3767b2c56ce02d01e3491046f33466a1ae60a37f",
800 | "shasum": ""
801 | },
802 | "require": {
803 | "doctrine/instantiator": "^1.3.1 || ^2",
804 | "ext-dom": "*",
805 | "ext-json": "*",
806 | "ext-libxml": "*",
807 | "ext-mbstring": "*",
808 | "ext-xml": "*",
809 | "ext-xmlwriter": "*",
810 | "myclabs/deep-copy": "^1.10.1",
811 | "phar-io/manifest": "^2.0.3",
812 | "phar-io/version": "^3.0.2",
813 | "php": ">=7.3",
814 | "phpunit/php-code-coverage": "^9.2.28",
815 | "phpunit/php-file-iterator": "^3.0.5",
816 | "phpunit/php-invoker": "^3.1.1",
817 | "phpunit/php-text-template": "^2.0.3",
818 | "phpunit/php-timer": "^5.0.2",
819 | "sebastian/cli-parser": "^1.0.1",
820 | "sebastian/code-unit": "^1.0.6",
821 | "sebastian/comparator": "^4.0.8",
822 | "sebastian/diff": "^4.0.3",
823 | "sebastian/environment": "^5.1.3",
824 | "sebastian/exporter": "^4.0.5",
825 | "sebastian/global-state": "^5.0.1",
826 | "sebastian/object-enumerator": "^4.0.3",
827 | "sebastian/resource-operations": "^3.0.3",
828 | "sebastian/type": "^3.2",
829 | "sebastian/version": "^3.0.2"
830 | },
831 | "suggest": {
832 | "ext-soap": "To be able to generate mocks based on WSDL files",
833 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
834 | },
835 | "bin": [
836 | "phpunit"
837 | ],
838 | "type": "library",
839 | "extra": {
840 | "branch-alias": {
841 | "dev-master": "9.6-dev"
842 | }
843 | },
844 | "autoload": {
845 | "files": [
846 | "src/Framework/Assert/Functions.php"
847 | ],
848 | "classmap": [
849 | "src/"
850 | ]
851 | },
852 | "notification-url": "https://packagist.org/downloads/",
853 | "license": [
854 | "BSD-3-Clause"
855 | ],
856 | "authors": [
857 | {
858 | "name": "Sebastian Bergmann",
859 | "email": "sebastian@phpunit.de",
860 | "role": "lead"
861 | }
862 | ],
863 | "description": "The PHP Unit Testing framework.",
864 | "homepage": "https://phpunit.de/",
865 | "keywords": [
866 | "phpunit",
867 | "testing",
868 | "xunit"
869 | ],
870 | "support": {
871 | "issues": "https://github.com/sebastianbergmann/phpunit/issues",
872 | "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
873 | "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.16"
874 | },
875 | "funding": [
876 | {
877 | "url": "https://phpunit.de/sponsors.html",
878 | "type": "custom"
879 | },
880 | {
881 | "url": "https://github.com/sebastianbergmann",
882 | "type": "github"
883 | },
884 | {
885 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
886 | "type": "tidelift"
887 | }
888 | ],
889 | "time": "2024-01-19T07:03:14+00:00"
890 | },
891 | {
892 | "name": "sebastian/cli-parser",
893 | "version": "1.0.1",
894 | "source": {
895 | "type": "git",
896 | "url": "https://github.com/sebastianbergmann/cli-parser.git",
897 | "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
898 | },
899 | "dist": {
900 | "type": "zip",
901 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
902 | "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
903 | "shasum": ""
904 | },
905 | "require": {
906 | "php": ">=7.3"
907 | },
908 | "require-dev": {
909 | "phpunit/phpunit": "^9.3"
910 | },
911 | "type": "library",
912 | "extra": {
913 | "branch-alias": {
914 | "dev-master": "1.0-dev"
915 | }
916 | },
917 | "autoload": {
918 | "classmap": [
919 | "src/"
920 | ]
921 | },
922 | "notification-url": "https://packagist.org/downloads/",
923 | "license": [
924 | "BSD-3-Clause"
925 | ],
926 | "authors": [
927 | {
928 | "name": "Sebastian Bergmann",
929 | "email": "sebastian@phpunit.de",
930 | "role": "lead"
931 | }
932 | ],
933 | "description": "Library for parsing CLI options",
934 | "homepage": "https://github.com/sebastianbergmann/cli-parser",
935 | "support": {
936 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
937 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
938 | },
939 | "funding": [
940 | {
941 | "url": "https://github.com/sebastianbergmann",
942 | "type": "github"
943 | }
944 | ],
945 | "time": "2020-09-28T06:08:49+00:00"
946 | },
947 | {
948 | "name": "sebastian/code-unit",
949 | "version": "1.0.8",
950 | "source": {
951 | "type": "git",
952 | "url": "https://github.com/sebastianbergmann/code-unit.git",
953 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
954 | },
955 | "dist": {
956 | "type": "zip",
957 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
958 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
959 | "shasum": ""
960 | },
961 | "require": {
962 | "php": ">=7.3"
963 | },
964 | "require-dev": {
965 | "phpunit/phpunit": "^9.3"
966 | },
967 | "type": "library",
968 | "extra": {
969 | "branch-alias": {
970 | "dev-master": "1.0-dev"
971 | }
972 | },
973 | "autoload": {
974 | "classmap": [
975 | "src/"
976 | ]
977 | },
978 | "notification-url": "https://packagist.org/downloads/",
979 | "license": [
980 | "BSD-3-Clause"
981 | ],
982 | "authors": [
983 | {
984 | "name": "Sebastian Bergmann",
985 | "email": "sebastian@phpunit.de",
986 | "role": "lead"
987 | }
988 | ],
989 | "description": "Collection of value objects that represent the PHP code units",
990 | "homepage": "https://github.com/sebastianbergmann/code-unit",
991 | "support": {
992 | "issues": "https://github.com/sebastianbergmann/code-unit/issues",
993 | "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
994 | },
995 | "funding": [
996 | {
997 | "url": "https://github.com/sebastianbergmann",
998 | "type": "github"
999 | }
1000 | ],
1001 | "time": "2020-10-26T13:08:54+00:00"
1002 | },
1003 | {
1004 | "name": "sebastian/code-unit-reverse-lookup",
1005 | "version": "2.0.3",
1006 | "source": {
1007 | "type": "git",
1008 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
1009 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
1010 | },
1011 | "dist": {
1012 | "type": "zip",
1013 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
1014 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
1015 | "shasum": ""
1016 | },
1017 | "require": {
1018 | "php": ">=7.3"
1019 | },
1020 | "require-dev": {
1021 | "phpunit/phpunit": "^9.3"
1022 | },
1023 | "type": "library",
1024 | "extra": {
1025 | "branch-alias": {
1026 | "dev-master": "2.0-dev"
1027 | }
1028 | },
1029 | "autoload": {
1030 | "classmap": [
1031 | "src/"
1032 | ]
1033 | },
1034 | "notification-url": "https://packagist.org/downloads/",
1035 | "license": [
1036 | "BSD-3-Clause"
1037 | ],
1038 | "authors": [
1039 | {
1040 | "name": "Sebastian Bergmann",
1041 | "email": "sebastian@phpunit.de"
1042 | }
1043 | ],
1044 | "description": "Looks up which function or method a line of code belongs to",
1045 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
1046 | "support": {
1047 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
1048 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
1049 | },
1050 | "funding": [
1051 | {
1052 | "url": "https://github.com/sebastianbergmann",
1053 | "type": "github"
1054 | }
1055 | ],
1056 | "time": "2020-09-28T05:30:19+00:00"
1057 | },
1058 | {
1059 | "name": "sebastian/comparator",
1060 | "version": "4.0.8",
1061 | "source": {
1062 | "type": "git",
1063 | "url": "https://github.com/sebastianbergmann/comparator.git",
1064 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a"
1065 | },
1066 | "dist": {
1067 | "type": "zip",
1068 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a",
1069 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a",
1070 | "shasum": ""
1071 | },
1072 | "require": {
1073 | "php": ">=7.3",
1074 | "sebastian/diff": "^4.0",
1075 | "sebastian/exporter": "^4.0"
1076 | },
1077 | "require-dev": {
1078 | "phpunit/phpunit": "^9.3"
1079 | },
1080 | "type": "library",
1081 | "extra": {
1082 | "branch-alias": {
1083 | "dev-master": "4.0-dev"
1084 | }
1085 | },
1086 | "autoload": {
1087 | "classmap": [
1088 | "src/"
1089 | ]
1090 | },
1091 | "notification-url": "https://packagist.org/downloads/",
1092 | "license": [
1093 | "BSD-3-Clause"
1094 | ],
1095 | "authors": [
1096 | {
1097 | "name": "Sebastian Bergmann",
1098 | "email": "sebastian@phpunit.de"
1099 | },
1100 | {
1101 | "name": "Jeff Welch",
1102 | "email": "whatthejeff@gmail.com"
1103 | },
1104 | {
1105 | "name": "Volker Dusch",
1106 | "email": "github@wallbash.com"
1107 | },
1108 | {
1109 | "name": "Bernhard Schussek",
1110 | "email": "bschussek@2bepublished.at"
1111 | }
1112 | ],
1113 | "description": "Provides the functionality to compare PHP values for equality",
1114 | "homepage": "https://github.com/sebastianbergmann/comparator",
1115 | "keywords": [
1116 | "comparator",
1117 | "compare",
1118 | "equality"
1119 | ],
1120 | "support": {
1121 | "issues": "https://github.com/sebastianbergmann/comparator/issues",
1122 | "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8"
1123 | },
1124 | "funding": [
1125 | {
1126 | "url": "https://github.com/sebastianbergmann",
1127 | "type": "github"
1128 | }
1129 | ],
1130 | "time": "2022-09-14T12:41:17+00:00"
1131 | },
1132 | {
1133 | "name": "sebastian/complexity",
1134 | "version": "2.0.3",
1135 | "source": {
1136 | "type": "git",
1137 | "url": "https://github.com/sebastianbergmann/complexity.git",
1138 | "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a"
1139 | },
1140 | "dist": {
1141 | "type": "zip",
1142 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a",
1143 | "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a",
1144 | "shasum": ""
1145 | },
1146 | "require": {
1147 | "nikic/php-parser": "^4.18 || ^5.0",
1148 | "php": ">=7.3"
1149 | },
1150 | "require-dev": {
1151 | "phpunit/phpunit": "^9.3"
1152 | },
1153 | "type": "library",
1154 | "extra": {
1155 | "branch-alias": {
1156 | "dev-master": "2.0-dev"
1157 | }
1158 | },
1159 | "autoload": {
1160 | "classmap": [
1161 | "src/"
1162 | ]
1163 | },
1164 | "notification-url": "https://packagist.org/downloads/",
1165 | "license": [
1166 | "BSD-3-Clause"
1167 | ],
1168 | "authors": [
1169 | {
1170 | "name": "Sebastian Bergmann",
1171 | "email": "sebastian@phpunit.de",
1172 | "role": "lead"
1173 | }
1174 | ],
1175 | "description": "Library for calculating the complexity of PHP code units",
1176 | "homepage": "https://github.com/sebastianbergmann/complexity",
1177 | "support": {
1178 | "issues": "https://github.com/sebastianbergmann/complexity/issues",
1179 | "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3"
1180 | },
1181 | "funding": [
1182 | {
1183 | "url": "https://github.com/sebastianbergmann",
1184 | "type": "github"
1185 | }
1186 | ],
1187 | "time": "2023-12-22T06:19:30+00:00"
1188 | },
1189 | {
1190 | "name": "sebastian/diff",
1191 | "version": "4.0.5",
1192 | "source": {
1193 | "type": "git",
1194 | "url": "https://github.com/sebastianbergmann/diff.git",
1195 | "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131"
1196 | },
1197 | "dist": {
1198 | "type": "zip",
1199 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
1200 | "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
1201 | "shasum": ""
1202 | },
1203 | "require": {
1204 | "php": ">=7.3"
1205 | },
1206 | "require-dev": {
1207 | "phpunit/phpunit": "^9.3",
1208 | "symfony/process": "^4.2 || ^5"
1209 | },
1210 | "type": "library",
1211 | "extra": {
1212 | "branch-alias": {
1213 | "dev-master": "4.0-dev"
1214 | }
1215 | },
1216 | "autoload": {
1217 | "classmap": [
1218 | "src/"
1219 | ]
1220 | },
1221 | "notification-url": "https://packagist.org/downloads/",
1222 | "license": [
1223 | "BSD-3-Clause"
1224 | ],
1225 | "authors": [
1226 | {
1227 | "name": "Sebastian Bergmann",
1228 | "email": "sebastian@phpunit.de"
1229 | },
1230 | {
1231 | "name": "Kore Nordmann",
1232 | "email": "mail@kore-nordmann.de"
1233 | }
1234 | ],
1235 | "description": "Diff implementation",
1236 | "homepage": "https://github.com/sebastianbergmann/diff",
1237 | "keywords": [
1238 | "diff",
1239 | "udiff",
1240 | "unidiff",
1241 | "unified diff"
1242 | ],
1243 | "support": {
1244 | "issues": "https://github.com/sebastianbergmann/diff/issues",
1245 | "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5"
1246 | },
1247 | "funding": [
1248 | {
1249 | "url": "https://github.com/sebastianbergmann",
1250 | "type": "github"
1251 | }
1252 | ],
1253 | "time": "2023-05-07T05:35:17+00:00"
1254 | },
1255 | {
1256 | "name": "sebastian/environment",
1257 | "version": "5.1.5",
1258 | "source": {
1259 | "type": "git",
1260 | "url": "https://github.com/sebastianbergmann/environment.git",
1261 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed"
1262 | },
1263 | "dist": {
1264 | "type": "zip",
1265 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
1266 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
1267 | "shasum": ""
1268 | },
1269 | "require": {
1270 | "php": ">=7.3"
1271 | },
1272 | "require-dev": {
1273 | "phpunit/phpunit": "^9.3"
1274 | },
1275 | "suggest": {
1276 | "ext-posix": "*"
1277 | },
1278 | "type": "library",
1279 | "extra": {
1280 | "branch-alias": {
1281 | "dev-master": "5.1-dev"
1282 | }
1283 | },
1284 | "autoload": {
1285 | "classmap": [
1286 | "src/"
1287 | ]
1288 | },
1289 | "notification-url": "https://packagist.org/downloads/",
1290 | "license": [
1291 | "BSD-3-Clause"
1292 | ],
1293 | "authors": [
1294 | {
1295 | "name": "Sebastian Bergmann",
1296 | "email": "sebastian@phpunit.de"
1297 | }
1298 | ],
1299 | "description": "Provides functionality to handle HHVM/PHP environments",
1300 | "homepage": "http://www.github.com/sebastianbergmann/environment",
1301 | "keywords": [
1302 | "Xdebug",
1303 | "environment",
1304 | "hhvm"
1305 | ],
1306 | "support": {
1307 | "issues": "https://github.com/sebastianbergmann/environment/issues",
1308 | "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5"
1309 | },
1310 | "funding": [
1311 | {
1312 | "url": "https://github.com/sebastianbergmann",
1313 | "type": "github"
1314 | }
1315 | ],
1316 | "time": "2023-02-03T06:03:51+00:00"
1317 | },
1318 | {
1319 | "name": "sebastian/exporter",
1320 | "version": "4.0.5",
1321 | "source": {
1322 | "type": "git",
1323 | "url": "https://github.com/sebastianbergmann/exporter.git",
1324 | "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d"
1325 | },
1326 | "dist": {
1327 | "type": "zip",
1328 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
1329 | "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
1330 | "shasum": ""
1331 | },
1332 | "require": {
1333 | "php": ">=7.3",
1334 | "sebastian/recursion-context": "^4.0"
1335 | },
1336 | "require-dev": {
1337 | "ext-mbstring": "*",
1338 | "phpunit/phpunit": "^9.3"
1339 | },
1340 | "type": "library",
1341 | "extra": {
1342 | "branch-alias": {
1343 | "dev-master": "4.0-dev"
1344 | }
1345 | },
1346 | "autoload": {
1347 | "classmap": [
1348 | "src/"
1349 | ]
1350 | },
1351 | "notification-url": "https://packagist.org/downloads/",
1352 | "license": [
1353 | "BSD-3-Clause"
1354 | ],
1355 | "authors": [
1356 | {
1357 | "name": "Sebastian Bergmann",
1358 | "email": "sebastian@phpunit.de"
1359 | },
1360 | {
1361 | "name": "Jeff Welch",
1362 | "email": "whatthejeff@gmail.com"
1363 | },
1364 | {
1365 | "name": "Volker Dusch",
1366 | "email": "github@wallbash.com"
1367 | },
1368 | {
1369 | "name": "Adam Harvey",
1370 | "email": "aharvey@php.net"
1371 | },
1372 | {
1373 | "name": "Bernhard Schussek",
1374 | "email": "bschussek@gmail.com"
1375 | }
1376 | ],
1377 | "description": "Provides the functionality to export PHP variables for visualization",
1378 | "homepage": "https://www.github.com/sebastianbergmann/exporter",
1379 | "keywords": [
1380 | "export",
1381 | "exporter"
1382 | ],
1383 | "support": {
1384 | "issues": "https://github.com/sebastianbergmann/exporter/issues",
1385 | "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5"
1386 | },
1387 | "funding": [
1388 | {
1389 | "url": "https://github.com/sebastianbergmann",
1390 | "type": "github"
1391 | }
1392 | ],
1393 | "time": "2022-09-14T06:03:37+00:00"
1394 | },
1395 | {
1396 | "name": "sebastian/global-state",
1397 | "version": "5.0.6",
1398 | "source": {
1399 | "type": "git",
1400 | "url": "https://github.com/sebastianbergmann/global-state.git",
1401 | "reference": "bde739e7565280bda77be70044ac1047bc007e34"
1402 | },
1403 | "dist": {
1404 | "type": "zip",
1405 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34",
1406 | "reference": "bde739e7565280bda77be70044ac1047bc007e34",
1407 | "shasum": ""
1408 | },
1409 | "require": {
1410 | "php": ">=7.3",
1411 | "sebastian/object-reflector": "^2.0",
1412 | "sebastian/recursion-context": "^4.0"
1413 | },
1414 | "require-dev": {
1415 | "ext-dom": "*",
1416 | "phpunit/phpunit": "^9.3"
1417 | },
1418 | "suggest": {
1419 | "ext-uopz": "*"
1420 | },
1421 | "type": "library",
1422 | "extra": {
1423 | "branch-alias": {
1424 | "dev-master": "5.0-dev"
1425 | }
1426 | },
1427 | "autoload": {
1428 | "classmap": [
1429 | "src/"
1430 | ]
1431 | },
1432 | "notification-url": "https://packagist.org/downloads/",
1433 | "license": [
1434 | "BSD-3-Clause"
1435 | ],
1436 | "authors": [
1437 | {
1438 | "name": "Sebastian Bergmann",
1439 | "email": "sebastian@phpunit.de"
1440 | }
1441 | ],
1442 | "description": "Snapshotting of global state",
1443 | "homepage": "http://www.github.com/sebastianbergmann/global-state",
1444 | "keywords": [
1445 | "global state"
1446 | ],
1447 | "support": {
1448 | "issues": "https://github.com/sebastianbergmann/global-state/issues",
1449 | "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6"
1450 | },
1451 | "funding": [
1452 | {
1453 | "url": "https://github.com/sebastianbergmann",
1454 | "type": "github"
1455 | }
1456 | ],
1457 | "time": "2023-08-02T09:26:13+00:00"
1458 | },
1459 | {
1460 | "name": "sebastian/lines-of-code",
1461 | "version": "1.0.4",
1462 | "source": {
1463 | "type": "git",
1464 | "url": "https://github.com/sebastianbergmann/lines-of-code.git",
1465 | "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5"
1466 | },
1467 | "dist": {
1468 | "type": "zip",
1469 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5",
1470 | "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5",
1471 | "shasum": ""
1472 | },
1473 | "require": {
1474 | "nikic/php-parser": "^4.18 || ^5.0",
1475 | "php": ">=7.3"
1476 | },
1477 | "require-dev": {
1478 | "phpunit/phpunit": "^9.3"
1479 | },
1480 | "type": "library",
1481 | "extra": {
1482 | "branch-alias": {
1483 | "dev-master": "1.0-dev"
1484 | }
1485 | },
1486 | "autoload": {
1487 | "classmap": [
1488 | "src/"
1489 | ]
1490 | },
1491 | "notification-url": "https://packagist.org/downloads/",
1492 | "license": [
1493 | "BSD-3-Clause"
1494 | ],
1495 | "authors": [
1496 | {
1497 | "name": "Sebastian Bergmann",
1498 | "email": "sebastian@phpunit.de",
1499 | "role": "lead"
1500 | }
1501 | ],
1502 | "description": "Library for counting the lines of code in PHP source code",
1503 | "homepage": "https://github.com/sebastianbergmann/lines-of-code",
1504 | "support": {
1505 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
1506 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4"
1507 | },
1508 | "funding": [
1509 | {
1510 | "url": "https://github.com/sebastianbergmann",
1511 | "type": "github"
1512 | }
1513 | ],
1514 | "time": "2023-12-22T06:20:34+00:00"
1515 | },
1516 | {
1517 | "name": "sebastian/object-enumerator",
1518 | "version": "4.0.4",
1519 | "source": {
1520 | "type": "git",
1521 | "url": "https://github.com/sebastianbergmann/object-enumerator.git",
1522 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
1523 | },
1524 | "dist": {
1525 | "type": "zip",
1526 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
1527 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
1528 | "shasum": ""
1529 | },
1530 | "require": {
1531 | "php": ">=7.3",
1532 | "sebastian/object-reflector": "^2.0",
1533 | "sebastian/recursion-context": "^4.0"
1534 | },
1535 | "require-dev": {
1536 | "phpunit/phpunit": "^9.3"
1537 | },
1538 | "type": "library",
1539 | "extra": {
1540 | "branch-alias": {
1541 | "dev-master": "4.0-dev"
1542 | }
1543 | },
1544 | "autoload": {
1545 | "classmap": [
1546 | "src/"
1547 | ]
1548 | },
1549 | "notification-url": "https://packagist.org/downloads/",
1550 | "license": [
1551 | "BSD-3-Clause"
1552 | ],
1553 | "authors": [
1554 | {
1555 | "name": "Sebastian Bergmann",
1556 | "email": "sebastian@phpunit.de"
1557 | }
1558 | ],
1559 | "description": "Traverses array structures and object graphs to enumerate all referenced objects",
1560 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
1561 | "support": {
1562 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
1563 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
1564 | },
1565 | "funding": [
1566 | {
1567 | "url": "https://github.com/sebastianbergmann",
1568 | "type": "github"
1569 | }
1570 | ],
1571 | "time": "2020-10-26T13:12:34+00:00"
1572 | },
1573 | {
1574 | "name": "sebastian/object-reflector",
1575 | "version": "2.0.4",
1576 | "source": {
1577 | "type": "git",
1578 | "url": "https://github.com/sebastianbergmann/object-reflector.git",
1579 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
1580 | },
1581 | "dist": {
1582 | "type": "zip",
1583 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
1584 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
1585 | "shasum": ""
1586 | },
1587 | "require": {
1588 | "php": ">=7.3"
1589 | },
1590 | "require-dev": {
1591 | "phpunit/phpunit": "^9.3"
1592 | },
1593 | "type": "library",
1594 | "extra": {
1595 | "branch-alias": {
1596 | "dev-master": "2.0-dev"
1597 | }
1598 | },
1599 | "autoload": {
1600 | "classmap": [
1601 | "src/"
1602 | ]
1603 | },
1604 | "notification-url": "https://packagist.org/downloads/",
1605 | "license": [
1606 | "BSD-3-Clause"
1607 | ],
1608 | "authors": [
1609 | {
1610 | "name": "Sebastian Bergmann",
1611 | "email": "sebastian@phpunit.de"
1612 | }
1613 | ],
1614 | "description": "Allows reflection of object attributes, including inherited and non-public ones",
1615 | "homepage": "https://github.com/sebastianbergmann/object-reflector/",
1616 | "support": {
1617 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
1618 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
1619 | },
1620 | "funding": [
1621 | {
1622 | "url": "https://github.com/sebastianbergmann",
1623 | "type": "github"
1624 | }
1625 | ],
1626 | "time": "2020-10-26T13:14:26+00:00"
1627 | },
1628 | {
1629 | "name": "sebastian/recursion-context",
1630 | "version": "4.0.5",
1631 | "source": {
1632 | "type": "git",
1633 | "url": "https://github.com/sebastianbergmann/recursion-context.git",
1634 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1"
1635 | },
1636 | "dist": {
1637 | "type": "zip",
1638 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
1639 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
1640 | "shasum": ""
1641 | },
1642 | "require": {
1643 | "php": ">=7.3"
1644 | },
1645 | "require-dev": {
1646 | "phpunit/phpunit": "^9.3"
1647 | },
1648 | "type": "library",
1649 | "extra": {
1650 | "branch-alias": {
1651 | "dev-master": "4.0-dev"
1652 | }
1653 | },
1654 | "autoload": {
1655 | "classmap": [
1656 | "src/"
1657 | ]
1658 | },
1659 | "notification-url": "https://packagist.org/downloads/",
1660 | "license": [
1661 | "BSD-3-Clause"
1662 | ],
1663 | "authors": [
1664 | {
1665 | "name": "Sebastian Bergmann",
1666 | "email": "sebastian@phpunit.de"
1667 | },
1668 | {
1669 | "name": "Jeff Welch",
1670 | "email": "whatthejeff@gmail.com"
1671 | },
1672 | {
1673 | "name": "Adam Harvey",
1674 | "email": "aharvey@php.net"
1675 | }
1676 | ],
1677 | "description": "Provides functionality to recursively process PHP variables",
1678 | "homepage": "https://github.com/sebastianbergmann/recursion-context",
1679 | "support": {
1680 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
1681 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5"
1682 | },
1683 | "funding": [
1684 | {
1685 | "url": "https://github.com/sebastianbergmann",
1686 | "type": "github"
1687 | }
1688 | ],
1689 | "time": "2023-02-03T06:07:39+00:00"
1690 | },
1691 | {
1692 | "name": "sebastian/resource-operations",
1693 | "version": "3.0.3",
1694 | "source": {
1695 | "type": "git",
1696 | "url": "https://github.com/sebastianbergmann/resource-operations.git",
1697 | "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
1698 | },
1699 | "dist": {
1700 | "type": "zip",
1701 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
1702 | "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
1703 | "shasum": ""
1704 | },
1705 | "require": {
1706 | "php": ">=7.3"
1707 | },
1708 | "require-dev": {
1709 | "phpunit/phpunit": "^9.0"
1710 | },
1711 | "type": "library",
1712 | "extra": {
1713 | "branch-alias": {
1714 | "dev-master": "3.0-dev"
1715 | }
1716 | },
1717 | "autoload": {
1718 | "classmap": [
1719 | "src/"
1720 | ]
1721 | },
1722 | "notification-url": "https://packagist.org/downloads/",
1723 | "license": [
1724 | "BSD-3-Clause"
1725 | ],
1726 | "authors": [
1727 | {
1728 | "name": "Sebastian Bergmann",
1729 | "email": "sebastian@phpunit.de"
1730 | }
1731 | ],
1732 | "description": "Provides a list of PHP built-in functions that operate on resources",
1733 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
1734 | "support": {
1735 | "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
1736 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
1737 | },
1738 | "funding": [
1739 | {
1740 | "url": "https://github.com/sebastianbergmann",
1741 | "type": "github"
1742 | }
1743 | ],
1744 | "time": "2020-09-28T06:45:17+00:00"
1745 | },
1746 | {
1747 | "name": "sebastian/type",
1748 | "version": "3.2.1",
1749 | "source": {
1750 | "type": "git",
1751 | "url": "https://github.com/sebastianbergmann/type.git",
1752 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7"
1753 | },
1754 | "dist": {
1755 | "type": "zip",
1756 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
1757 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
1758 | "shasum": ""
1759 | },
1760 | "require": {
1761 | "php": ">=7.3"
1762 | },
1763 | "require-dev": {
1764 | "phpunit/phpunit": "^9.5"
1765 | },
1766 | "type": "library",
1767 | "extra": {
1768 | "branch-alias": {
1769 | "dev-master": "3.2-dev"
1770 | }
1771 | },
1772 | "autoload": {
1773 | "classmap": [
1774 | "src/"
1775 | ]
1776 | },
1777 | "notification-url": "https://packagist.org/downloads/",
1778 | "license": [
1779 | "BSD-3-Clause"
1780 | ],
1781 | "authors": [
1782 | {
1783 | "name": "Sebastian Bergmann",
1784 | "email": "sebastian@phpunit.de",
1785 | "role": "lead"
1786 | }
1787 | ],
1788 | "description": "Collection of value objects that represent the types of the PHP type system",
1789 | "homepage": "https://github.com/sebastianbergmann/type",
1790 | "support": {
1791 | "issues": "https://github.com/sebastianbergmann/type/issues",
1792 | "source": "https://github.com/sebastianbergmann/type/tree/3.2.1"
1793 | },
1794 | "funding": [
1795 | {
1796 | "url": "https://github.com/sebastianbergmann",
1797 | "type": "github"
1798 | }
1799 | ],
1800 | "time": "2023-02-03T06:13:03+00:00"
1801 | },
1802 | {
1803 | "name": "sebastian/version",
1804 | "version": "3.0.2",
1805 | "source": {
1806 | "type": "git",
1807 | "url": "https://github.com/sebastianbergmann/version.git",
1808 | "reference": "c6c1022351a901512170118436c764e473f6de8c"
1809 | },
1810 | "dist": {
1811 | "type": "zip",
1812 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
1813 | "reference": "c6c1022351a901512170118436c764e473f6de8c",
1814 | "shasum": ""
1815 | },
1816 | "require": {
1817 | "php": ">=7.3"
1818 | },
1819 | "type": "library",
1820 | "extra": {
1821 | "branch-alias": {
1822 | "dev-master": "3.0-dev"
1823 | }
1824 | },
1825 | "autoload": {
1826 | "classmap": [
1827 | "src/"
1828 | ]
1829 | },
1830 | "notification-url": "https://packagist.org/downloads/",
1831 | "license": [
1832 | "BSD-3-Clause"
1833 | ],
1834 | "authors": [
1835 | {
1836 | "name": "Sebastian Bergmann",
1837 | "email": "sebastian@phpunit.de",
1838 | "role": "lead"
1839 | }
1840 | ],
1841 | "description": "Library that helps with managing the version number of Git-hosted PHP projects",
1842 | "homepage": "https://github.com/sebastianbergmann/version",
1843 | "support": {
1844 | "issues": "https://github.com/sebastianbergmann/version/issues",
1845 | "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
1846 | },
1847 | "funding": [
1848 | {
1849 | "url": "https://github.com/sebastianbergmann",
1850 | "type": "github"
1851 | }
1852 | ],
1853 | "time": "2020-09-28T06:39:44+00:00"
1854 | },
1855 | {
1856 | "name": "slevomat/coding-standard",
1857 | "version": "8.14.1",
1858 | "source": {
1859 | "type": "git",
1860 | "url": "https://github.com/slevomat/coding-standard.git",
1861 | "reference": "fea1fd6f137cc84f9cba0ae30d549615dbc6a926"
1862 | },
1863 | "dist": {
1864 | "type": "zip",
1865 | "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/fea1fd6f137cc84f9cba0ae30d549615dbc6a926",
1866 | "reference": "fea1fd6f137cc84f9cba0ae30d549615dbc6a926",
1867 | "shasum": ""
1868 | },
1869 | "require": {
1870 | "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
1871 | "php": "^7.2 || ^8.0",
1872 | "phpstan/phpdoc-parser": "^1.23.1",
1873 | "squizlabs/php_codesniffer": "^3.7.1"
1874 | },
1875 | "require-dev": {
1876 | "phing/phing": "2.17.4",
1877 | "php-parallel-lint/php-parallel-lint": "1.3.2",
1878 | "phpstan/phpstan": "1.10.37",
1879 | "phpstan/phpstan-deprecation-rules": "1.1.4",
1880 | "phpstan/phpstan-phpunit": "1.3.14",
1881 | "phpstan/phpstan-strict-rules": "1.5.1",
1882 | "phpunit/phpunit": "8.5.21|9.6.8|10.3.5"
1883 | },
1884 | "type": "phpcodesniffer-standard",
1885 | "extra": {
1886 | "branch-alias": {
1887 | "dev-master": "8.x-dev"
1888 | }
1889 | },
1890 | "autoload": {
1891 | "psr-4": {
1892 | "SlevomatCodingStandard\\": "SlevomatCodingStandard/"
1893 | }
1894 | },
1895 | "notification-url": "https://packagist.org/downloads/",
1896 | "license": [
1897 | "MIT"
1898 | ],
1899 | "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
1900 | "keywords": [
1901 | "dev",
1902 | "phpcs"
1903 | ],
1904 | "support": {
1905 | "issues": "https://github.com/slevomat/coding-standard/issues",
1906 | "source": "https://github.com/slevomat/coding-standard/tree/8.14.1"
1907 | },
1908 | "funding": [
1909 | {
1910 | "url": "https://github.com/kukulich",
1911 | "type": "github"
1912 | },
1913 | {
1914 | "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
1915 | "type": "tidelift"
1916 | }
1917 | ],
1918 | "time": "2023-10-08T07:28:08+00:00"
1919 | },
1920 | {
1921 | "name": "squizlabs/php_codesniffer",
1922 | "version": "3.8.1",
1923 | "source": {
1924 | "type": "git",
1925 | "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
1926 | "reference": "14f5fff1e64118595db5408e946f3a22c75807f7"
1927 | },
1928 | "dist": {
1929 | "type": "zip",
1930 | "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/14f5fff1e64118595db5408e946f3a22c75807f7",
1931 | "reference": "14f5fff1e64118595db5408e946f3a22c75807f7",
1932 | "shasum": ""
1933 | },
1934 | "require": {
1935 | "ext-simplexml": "*",
1936 | "ext-tokenizer": "*",
1937 | "ext-xmlwriter": "*",
1938 | "php": ">=5.4.0"
1939 | },
1940 | "require-dev": {
1941 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
1942 | },
1943 | "bin": [
1944 | "bin/phpcbf",
1945 | "bin/phpcs"
1946 | ],
1947 | "type": "library",
1948 | "extra": {
1949 | "branch-alias": {
1950 | "dev-master": "3.x-dev"
1951 | }
1952 | },
1953 | "notification-url": "https://packagist.org/downloads/",
1954 | "license": [
1955 | "BSD-3-Clause"
1956 | ],
1957 | "authors": [
1958 | {
1959 | "name": "Greg Sherwood",
1960 | "role": "Former lead"
1961 | },
1962 | {
1963 | "name": "Juliette Reinders Folmer",
1964 | "role": "Current lead"
1965 | },
1966 | {
1967 | "name": "Contributors",
1968 | "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
1969 | }
1970 | ],
1971 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
1972 | "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
1973 | "keywords": [
1974 | "phpcs",
1975 | "standards",
1976 | "static analysis"
1977 | ],
1978 | "support": {
1979 | "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
1980 | "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
1981 | "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
1982 | "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
1983 | },
1984 | "funding": [
1985 | {
1986 | "url": "https://github.com/PHPCSStandards",
1987 | "type": "github"
1988 | },
1989 | {
1990 | "url": "https://github.com/jrfnl",
1991 | "type": "github"
1992 | },
1993 | {
1994 | "url": "https://opencollective.com/php_codesniffer",
1995 | "type": "open_collective"
1996 | }
1997 | ],
1998 | "time": "2024-01-11T20:47:48+00:00"
1999 | },
2000 | {
2001 | "name": "theseer/tokenizer",
2002 | "version": "1.2.2",
2003 | "source": {
2004 | "type": "git",
2005 | "url": "https://github.com/theseer/tokenizer.git",
2006 | "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
2007 | },
2008 | "dist": {
2009 | "type": "zip",
2010 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
2011 | "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
2012 | "shasum": ""
2013 | },
2014 | "require": {
2015 | "ext-dom": "*",
2016 | "ext-tokenizer": "*",
2017 | "ext-xmlwriter": "*",
2018 | "php": "^7.2 || ^8.0"
2019 | },
2020 | "type": "library",
2021 | "autoload": {
2022 | "classmap": [
2023 | "src/"
2024 | ]
2025 | },
2026 | "notification-url": "https://packagist.org/downloads/",
2027 | "license": [
2028 | "BSD-3-Clause"
2029 | ],
2030 | "authors": [
2031 | {
2032 | "name": "Arne Blankerts",
2033 | "email": "arne@blankerts.de",
2034 | "role": "Developer"
2035 | }
2036 | ],
2037 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
2038 | "support": {
2039 | "issues": "https://github.com/theseer/tokenizer/issues",
2040 | "source": "https://github.com/theseer/tokenizer/tree/1.2.2"
2041 | },
2042 | "funding": [
2043 | {
2044 | "url": "https://github.com/theseer",
2045 | "type": "github"
2046 | }
2047 | ],
2048 | "time": "2023-11-20T00:12:19+00:00"
2049 | }
2050 | ],
2051 | "aliases": [],
2052 | "minimum-stability": "dev",
2053 | "stability-flags": {
2054 | "jerodev/code-styles": 20
2055 | },
2056 | "prefer-stable": true,
2057 | "prefer-lowest": false,
2058 | "platform": {
2059 | "php": "^8.1"
2060 | },
2061 | "platform-dev": [],
2062 | "plugin-api-version": "2.3.0"
2063 | }
2064 |
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2023 Jerodev
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19 | OR OTHER DEALINGS IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | src
6 | tests
7 |
8 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Data Mapper
2 |  [](https://packagist.org/packages/jerodev/data-mapper)
3 |
4 | This package will map any raw data into a predefined strong-typed PHP object.
5 |
6 | ## Installation
7 | The mapper has no external dependencies apart from PHP8.1 or higher. It can be installed using composer:
8 |
9 | ```bash
10 | composer require jerodev/data-mapper
11 | ```
12 |
13 | ## Basic mapping
14 | Let's start with the basics. The mapper will map data directly to public properties on objects. If these properties have
15 | types defined either using types introduced in PHP7.4 or through [PHPDoc](https://phpstan.org/writing-php-code/phpdoc-types), the mapper will attempt to cast the data to these
16 | types.
17 |
18 | For example: imagine having an `Entity` class with the public properties `$id` and `$name`:
19 |
20 | ```php
21 | class User
22 | {
23 | public int $id;
24 | public string $name;
25 | }
26 | ```
27 |
28 | To map data from an array we simply pass the class name and an array with data to the mapper.
29 |
30 | ```php
31 | $mapper = new \Jerodev\DataMapper\Mapper();
32 | $entity = $mapper->map(User::class, [
33 | 'id' => '5',
34 | 'name' => 'John Doe',
35 | ]);
36 |
37 | // User {
38 | // +id: 5,
39 | // +name: "John Doe",
40 | // }
41 | ```
42 |
43 | This is a simple example, but the mapper can also map nested objects, arrays of objects, keyed arrays, and even multi-level arrays.
44 |
45 | ## Documentation
46 | More information about mapping, configuration and best practices can be found in [the documentation](https://docs.deviaene.eu/data-mapper/).
47 |
48 | ## License
49 | This library is licensed under the MIT License (MIT). Please see [License File](license.md) for more information.
50 |
--------------------------------------------------------------------------------
/src/Attributes/PostMapping.php:
--------------------------------------------------------------------------------
1 | dataTypeFactory = new DataTypeFactory();
22 | $this->objectMapper = new ObjectMapper(
23 | $this,
24 | $this->dataTypeFactory,
25 | );
26 | }
27 |
28 | /**
29 | * Map anything!
30 | *
31 | * @param DataTypeCollection|string $typeCollection The type to map to.
32 | * @param mixed $data Deserialized data to map to the type.
33 | * @return mixed
34 | */
35 | public function map($typeCollection, $data)
36 | {
37 | if (\is_string($typeCollection)) {
38 | return $this->map(
39 | $this->dataTypeFactory->fromString($typeCollection),
40 | $data,
41 | );
42 | }
43 |
44 | if ($data === 'null' || $data === null) {
45 | if ($this->config->strictNullMapping === false || $typeCollection->isNullable()) {
46 | return null;
47 | }
48 |
49 | throw new UnexpectedNullValueException($this->dataTypeFactory->print($typeCollection));
50 | }
51 |
52 | // Loop over all possible types and parse to the first one that matches
53 | foreach ($typeCollection->types as $type) {
54 | try {
55 | if ($type->isNative()) {
56 | return $this->mapNativeType($type, $data);
57 | }
58 |
59 | if ($type->isArray()) {
60 | return $this->mapArray($type, $data);
61 | }
62 |
63 | return $this->mapObject($type, $data);
64 | } catch (CouldNotMapValueException) {
65 | continue;
66 | }
67 | }
68 |
69 | throw new CouldNotMapValueException($data, $typeCollection);
70 | }
71 |
72 | /**
73 | * Remove cached class mappers.
74 | */
75 | public function clearCache(): void
76 | {
77 | $this->objectMapper->clearCache();
78 | }
79 |
80 | private function mapNativeType(DataType $type, mixed $data): float|object|bool|int|string|null
81 | {
82 | return match ($type->type) {
83 | 'null' => null,
84 | 'bool' => \filter_var($data, \FILTER_VALIDATE_BOOL),
85 | 'int' => (int) $data,
86 | 'float' => (float) $data,
87 | 'string' => (string) $data,
88 | 'object' => (object) $data,
89 | default => throw new CouldNotMapValueException($data, $type),
90 | };
91 | }
92 |
93 | private function mapArray(DataType $type, mixed $data): array
94 | {
95 | if (! \is_iterable($data)) {
96 | throw new CouldNotMapValueException($data, $type);
97 | }
98 |
99 | if ($type->isGenericArray()) {
100 | return (array) $data;
101 | }
102 |
103 | $keyType = null;
104 | $valueType = $type->genericTypes[0];
105 | if (\count($type->genericTypes) > 1) {
106 | [$keyType, $valueType] = $type->genericTypes;
107 | }
108 |
109 | $mappedArray = [];
110 | foreach ($data as $key => $value) {
111 | if ($keyType !== null) {
112 | $key = $this->map($keyType, $key);
113 | }
114 |
115 | $mappedArray[$key] = $this->map($valueType, $value);
116 | }
117 |
118 | return $mappedArray;
119 | }
120 |
121 | private function mapObject(DataType $type, mixed $data): ?object
122 | {
123 | try {
124 | return $this->objectMapper->map($type, $data);
125 | } catch (CouldNotResolveClassException) {
126 | throw new CouldNotMapValueException($data, $type);
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/MapperCommands.php:
--------------------------------------------------------------------------------
1 | clearCache();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/MapperConfig.php:
--------------------------------------------------------------------------------
1 | */
11 | public array $constructorArguments = [];
12 |
13 | /** @var array */
14 | public array $properties = [];
15 |
16 | /** @var array */
17 | public array $classAttributes = [];
18 |
19 | public function __construct(
20 | public readonly string $namespacedClassName,
21 | public readonly string $fileName,
22 | ) {
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Objects/ClassBluePrinter.php:
--------------------------------------------------------------------------------
1 | dataTypeFactory = new DataTypeFactory();
19 | $this->docBlockParser = new DocBlockParser();
20 | }
21 |
22 | public function print(string $class): ClassBluePrint
23 | {
24 | $reflection = new ReflectionClass($class);
25 | $blueprint = new ClassBluePrint($class, $reflection->getFileName());
26 |
27 | $this->printConstructor($reflection, $blueprint);
28 | $this->printProperties($reflection, $blueprint);
29 | $this->printAttributes($reflection, $blueprint);
30 |
31 | return $blueprint;
32 | }
33 |
34 | private function printConstructor(ReflectionClass $reflection, ClassBluePrint $bluePrint): void
35 | {
36 | $constructor = $reflection->getConstructor();
37 | if ($constructor === null || $constructor->getNumberOfParameters() === 0) {
38 | return;
39 | }
40 |
41 | // Get information from potential doc comment
42 | $doc = null;
43 | if ($constructor->getDocComment()) {
44 | $doc = $this->docBlockParser->parse($constructor->getDocComment());
45 | }
46 |
47 | // Loop over each parameter and describe it
48 | foreach ($constructor->getParameters() as $param) {
49 | // Using the getName() function omits the question mark for nullable types.
50 | $type = (string) $param->getType();
51 |
52 | if ($doc !== null && \in_array($type, [null, 'array', 'iterable'])) {
53 | $type = $doc->getParamType($param->getName());
54 | }
55 |
56 | $arg = [
57 | 'type' => $this->resolveType(
58 | $this->dataTypeFactory->fromString($type),
59 | $reflection->getName(),
60 | ),
61 | ];
62 | if ($param->isDefaultValueAvailable()) {
63 | $arg['default'] = $param->getDefaultValue();
64 | }
65 |
66 | $bluePrint->constructorArguments[$param->getName()] = $arg;
67 | }
68 | }
69 |
70 | private function printProperties(ReflectionClass $reflection, ClassBluePrint $blueprint): void
71 | {
72 | $properties = $reflection->getProperties();
73 | foreach ($properties as $property) {
74 | if (! $property->isPublic() || $property->isReadOnly()) {
75 | continue;
76 | }
77 |
78 | // Already mapped through constructor?
79 | if (\array_key_exists($property->getName(), $blueprint->constructorArguments)) {
80 | continue;
81 | }
82 |
83 | $type = $property->getType()?->getName();
84 | if (\in_array($type, [null, 'array', 'iterable']) && $property->getDocComment()) {
85 | $doc = $this->docBlockParser->parse($property->getDocComment());
86 | if ($doc->varType !== null) {
87 | $type = $doc->varType;
88 | }
89 | }
90 |
91 | $mapped = [
92 | 'type' => $this->resolveType(
93 | $this->dataTypeFactory->fromString($type, $property->getType()->allowsNull()),
94 | $reflection->getName(),
95 | ),
96 | ];
97 | if ($property->hasDefaultValue()) {
98 | $mapped['default'] = $property->getDefaultValue();
99 | }
100 |
101 | $blueprint->properties[$property->getName()] = $mapped;
102 | }
103 | }
104 |
105 | private function printAttributes(ReflectionClass $reflection, ClassBluePrint $blueprint): void
106 | {
107 | foreach ($reflection->getAttributes(PostMapping::class) as $attribute) {
108 | $blueprint->classAttributes[] = $attribute->newInstance();
109 | }
110 |
111 | // Also check parent for relevant attributes
112 | if ($reflection->getParentClass()) {
113 | $this->printAttributes($reflection->getParentClass(), $blueprint);
114 | }
115 | }
116 |
117 | private function resolveType(DataTypeCollection $type, string $className): DataTypeCollection
118 | {
119 | $baseClassName = \explode('\\', $className);
120 | $baseClassName = \end($baseClassName);
121 |
122 | $collection = [];
123 | foreach ($type->types as $dataType) {
124 | $generics = [];
125 | foreach ($dataType->genericTypes as $genericType) {
126 | $generics[] = $this->resolveType($genericType, $className);
127 | }
128 |
129 | $typeName = $dataType->type;
130 | if (\in_array($typeName, ['self', 'static', $baseClassName])) {
131 | $typeName = $className;
132 | }
133 |
134 | $collection[] = new DataType(
135 | $typeName,
136 | $dataType->isNullable,
137 | $generics,
138 | );
139 | }
140 |
141 | return new DataTypeCollection($collection);
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/Objects/ClassResolver.php:
--------------------------------------------------------------------------------
1 | findSourceFile();
17 | if ($sourceFile === null) {
18 | throw new CouldNotResolveClassException($name);
19 | }
20 |
21 | $name = $this->findClassNameInFile($name, $sourceFile);
22 | if (\class_exists($name)) {
23 | return $name;
24 | }
25 |
26 | throw new CouldNotResolveClassException($name, $sourceFile);
27 | }
28 |
29 | private function findSourceFile(): ?string
30 | {
31 | $backtrace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 10);
32 | $mapperFileSuffix = \implode(\DIRECTORY_SEPARATOR, ['data-mapper', 'src', 'Mapper.php']);
33 |
34 | $foundMapper = false;
35 | foreach ($backtrace as $trace) {
36 | if (\str_ends_with($trace['file'], $mapperFileSuffix)) {
37 | $foundMapper = true;
38 | continue;
39 | }
40 |
41 | if ($foundMapper) {
42 | return $trace['file'];
43 | }
44 | }
45 |
46 | return null;
47 | }
48 |
49 | private function findClassNameInFile(string $name, string $sourceFile): string
50 | {
51 | $file = \file_get_contents($sourceFile);
52 | if ($file === false) {
53 | return $name;
54 | }
55 |
56 | $nameParts = \explode('\\', $name);
57 | $lastPart = \end($nameParts);
58 |
59 | $newline = false;
60 | $fileLength = \strlen($file);
61 | for ($i = 0; $i < $fileLength; $i++) {
62 | $char = $file[$i];
63 |
64 | // Don't care about spaces
65 | if ($char === ' ' || $char === "\t") {
66 | continue;
67 | }
68 |
69 | if ($char === \PHP_EOL) {
70 | $newline = true;
71 | continue;
72 | }
73 |
74 | // Find the class in the same namespace as the caller
75 | if ($newline && $char === 'n' && \substr($file, $i, 10) === 'namespace ') {
76 | $i += 10;
77 | $namespace = '';
78 | while (($char = $file[$i++]) !== ';') {
79 | $namespace .= $char;
80 | }
81 |
82 | $classInNamespace = $namespace . '\\' . $lastPart;
83 | if (\class_exists($classInNamespace)) {
84 | return $classInNamespace;
85 | }
86 | }
87 |
88 | // If we are after a newline and find a use statement, parse it!
89 | if ($newline && $char === 'u' && \substr($file, $i, 4) === 'use ') {
90 | $i += 4;
91 |
92 | $startAlias = false;
93 | $importName = '';
94 | $importAlias = '';
95 | while (($char = $file[$i++]) !== ';') {
96 | if ($char === ' ' && \substr($file, $i, 3) === 'as ') {
97 | $startAlias = true;
98 | $i += 3;
99 | continue;
100 | }
101 |
102 | if ($startAlias) {
103 | $importAlias .= $char;
104 | } else {
105 | $importName .= $char;
106 | }
107 | }
108 |
109 | $nameParts = \explode('\\', $importName);
110 | $lastImportPart = \end($nameParts);
111 |
112 | if ($importAlias === $lastPart || $lastImportPart === $lastPart) {
113 | return $importName;
114 | }
115 |
116 | $i--;
117 | }
118 |
119 | $newline = false;
120 | }
121 |
122 | return $name;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/Objects/DocBlock.php:
--------------------------------------------------------------------------------
1 | */
8 | public array $paramTypes = [];
9 | public ?string $returnType = null;
10 | public ?string $varType = null;
11 |
12 | public function getParamType(string $name): ?string
13 | {
14 | return $this->paramTypes[$name] ?? null;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Objects/DocBlockParser.php:
--------------------------------------------------------------------------------
1 | $this->parseParam($contents, $i, $docblock),
23 | '@return', '@var' => $this->parseReturn($contents, $i, $docblock, \substr($identifier, 1) . 'Type'),
24 | default => null,
25 | };
26 | }
27 | }
28 |
29 | return $docblock;
30 | }
31 |
32 | private function parseParam(string $contents, int &$i, DocBlock $docblock): void
33 | {
34 | $type = $this->parseType($contents, $i);
35 |
36 | $docblock->paramTypes[$this->parseVariable($contents, $i)] = $type;
37 | }
38 |
39 | private function parseReturn(string $contents, int &$i, DocBlock $docblock, string $docProperty = 'returnType'): void
40 | {
41 | $docblock->{$docProperty} = $this->parseType($contents, $i);
42 | }
43 |
44 | private function parseType(string $contents, int &$i): string
45 | {
46 | $type = '';
47 | $sawSpace = false;
48 | $lastChar = '';
49 | for (; $i < \strlen($contents); $i++) {
50 | $char = $contents[$i];
51 | if ($char === ' ') {
52 | $type .= $char;
53 | $sawSpace = true;
54 | continue;
55 | }
56 |
57 | if (\in_array($char, ['|', ',', ':'])) {
58 | $type .= $char;
59 | $lastChar = $char;
60 | $sawSpace = false;
61 | continue;
62 | }
63 |
64 | if ($sawSpace && ! \in_array($lastChar, ['|', ',', ':'])) {
65 | break;
66 | }
67 |
68 | $type .= $char;
69 | $lastChar = $char;
70 | $sawSpace = false;
71 | }
72 |
73 | return \trim($type);
74 | }
75 |
76 | private function parseVariable(string $contents, int &$i): string
77 | {
78 | $variable = '';
79 | for (; $i < \strlen($contents); $i++) {
80 | $char = $contents[$i];
81 | if ($char === ' ') {
82 | if (empty($variable)) {
83 | continue;
84 | }
85 |
86 | break;
87 | }
88 |
89 | if (empty($variable) && $char !== '$') {
90 | throw new \Exception('Expected variable to start with \'$\', found \'' . $char . '\'.');
91 | }
92 |
93 | $variable .= $char;
94 | }
95 |
96 | return \trim(\substr($variable, 1));
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/Objects/ObjectMapper.php:
--------------------------------------------------------------------------------
1 | classBluePrinter = new ClassBluePrinter();
25 | }
26 |
27 | /**
28 | * @param DataType|string $type
29 | * @param array|string $data
30 | * @return object|null
31 | * @throws CouldNotResolveClassException
32 | */
33 | public function map(DataType|string $type, array|string $data): ?object
34 | {
35 | $class = $this->dataTypeFactory->classResolver->resolve(\is_string($type) ? $type : $type->type);
36 | if (\is_subclass_of($class, MapsItself::class)) {
37 | return \call_user_func([$class, 'mapSelf'], $data, $this->mapper);
38 | }
39 |
40 | // If the data is a string and the class is an enum, create the enum.
41 | if (\is_string($data) && \is_subclass_of($class, \BackedEnum::class)) {
42 | if ($this->mapper->config->enumTryFrom) {
43 | return $class::tryFrom($data);
44 | }
45 |
46 | return $class::from($data);
47 | }
48 |
49 | $functionName = self::MAPPER_FUNCTION_PREFIX . \md5($class . ($type instanceof DataType && $type->isNullable ? '1' : '0'));
50 | if ($this->mapper->config->classCacheKeySource === 'md5' || $this->mapper->config->classCacheKeySource === 'modified') {
51 | $reflection = new ReflectionClass($class);
52 | $functionName = match ($this->mapper->config->classCacheKeySource) {
53 | 'md5' => self::MAPPER_FUNCTION_PREFIX . \md5(\md5_file($reflection->getFileName()) . $functionName),
54 | 'modified' => self::MAPPER_FUNCTION_PREFIX . \md5(\filemtime($reflection->getFileName()) . $functionName),
55 | };
56 | }
57 |
58 | $fileName = $this->mapperDirectory() . \DIRECTORY_SEPARATOR . $functionName . '.php';
59 | if (! \file_exists($fileName)) {
60 | \file_put_contents(
61 | $fileName,
62 | $this->createObjectMappingFunction(
63 | $this->classBluePrinter->print($class),
64 | $functionName,
65 | $type instanceof DataType && $type->isNullable,
66 | ),
67 | );
68 | }
69 |
70 | // Include the function containing file and call the function.
71 | require_once($fileName);
72 | return ($functionName)($this->mapper, $data);
73 | }
74 |
75 | public function clearCache(): void
76 | {
77 | foreach (\glob($this->mapperDirectory() . \DIRECTORY_SEPARATOR . self::MAPPER_FUNCTION_PREFIX . '*.php') as $file) {
78 | \unlink($file);
79 | }
80 | }
81 |
82 | public function mapperDirectory(): string
83 | {
84 | $dir = \str_replace('{$TMP}', \sys_get_temp_dir(), $this->mapper->config->classMapperDirectory);
85 | if (! \file_exists($dir) && ! \mkdir($dir, 0777, true) && ! \is_dir($dir)) {
86 | throw new \RuntimeException("Could not create caching directory '{$dir}'");
87 | }
88 |
89 | return \rtrim($dir, \DIRECTORY_SEPARATOR);
90 | }
91 |
92 | private function createObjectMappingFunction(ClassBluePrint $blueprint, string $mapFunctionName, bool $isNullable): string
93 | {
94 | $tab = ' ';
95 | $content = '';
96 |
97 | if ($isNullable) {
98 | $content .= $tab . $tab . 'if ($data === [] && $mapper->config->nullObjectFromEmptyArray) {' . \PHP_EOL;
99 | $content .= $tab . $tab . $tab . 'return null;' . \PHP_EOL;
100 | $content .= $tab . $tab . '}' . \PHP_EOL . \PHP_EOL;
101 | }
102 |
103 | // Instantiate a new object
104 | $args = [];
105 | foreach ($blueprint->constructorArguments as $name => $argument) {
106 | $arg = "\$data['{$name}']";
107 | if ($argument['type']->isNullable()) {
108 | $arg = "({$arg} ?? null)";
109 | }
110 |
111 | if ($argument['type'] !== null) {
112 | $arg = $this->castInMapperFunction($arg, $argument['type'], $blueprint);
113 | }
114 |
115 | if (\array_key_exists('default', $argument)) {
116 | $arg = $this->wrapDefault($arg, $name, $argument['default']);
117 | }
118 |
119 | $args[] = $arg;
120 | }
121 | $content .= $tab . $tab . '$x = new ' . $blueprint->namespacedClassName . '(' . \implode(', ', $args) . ');';
122 |
123 | // Map properties
124 | foreach ($blueprint->properties as $name => $property) {
125 | // Use a foreach to map key/value arrays
126 | if (\count($property['type']->types) === 1 && $property['type']->types[0]->isArray() && \count($property['type']->types[0]->genericTypes) === 2) {
127 | $content .= $this->buildPropertyForeachMapping($name, $property, $blueprint);
128 |
129 | continue;
130 | }
131 |
132 | $propertyName = "\$data['{$name}']";
133 | if ($property['type']->isNullable()) {
134 | $propertyName = "({$propertyName} ?? null)";
135 | }
136 |
137 | $propertyMap = $this->castInMapperFunction($propertyName, $property['type'], $blueprint);
138 | if (\array_key_exists('default', $property)) {
139 | $propertyMap = $this->wrapDefault($propertyMap, $name, $property['default']);
140 | }
141 |
142 | $propertySet = \PHP_EOL . $tab . $tab . '$x->' . $name . ' = ' . $propertyMap . ';';
143 |
144 | if ($this->mapper->config->allowUninitializedFields && ! \array_key_exists('default', $property)) {
145 | $propertySet = $this->wrapArrayKeyExists($propertySet, $name);
146 | }
147 |
148 | $content .= $propertySet;
149 | }
150 |
151 | // Post mapping functions?
152 | foreach ($blueprint->classAttributes as $attribute) {
153 | if ($attribute instanceof PostMapping) {
154 | if (\is_string($attribute->postMappingCallback)) {
155 | $content.= \PHP_EOL . \PHP_EOL . $tab . $tab . "\$x->{$attribute->postMappingCallback}(\$data, \$x);";
156 | } else {
157 | $content.= \PHP_EOL . \PHP_EOL . $tab . $tab . "\call_user_func({$attribute->postMappingCallback}, \$data, \$x);";
158 | }
159 | }
160 | }
161 |
162 | // Render the function
163 | $mapperClass = Mapper::class;
164 | return <<types) === 1) {
181 | $type = $type->types[0];
182 |
183 | if ($type->isNullable) {
184 | return "{$propertyName} === null ? null : " . $this->castInMapperFunction($propertyName, new DataTypeCollection([$type->removeNullable()]), $bluePrint);
185 | }
186 |
187 | if ($type->isNative()) {
188 | return match ($type->type) {
189 | 'null' => 'null',
190 | 'bool' => "\\filter_var({$propertyName}, \FILTER_VALIDATE_BOOL)",
191 | 'float' => '(float) ' . $propertyName,
192 | 'int' => '(int) ' . $propertyName,
193 | 'string' => '(string) ' . $propertyName,
194 | 'object' => '(object) ' . $propertyName,
195 | default => $propertyName,
196 | };
197 | }
198 |
199 | if ($type->isArray()) {
200 | if ($type->isGenericArray()) {
201 | return '(array) ' . $propertyName;
202 | }
203 | if (\count($type->genericTypes) === 1) {
204 | $uniqid = \uniqid();
205 | return "\\array_map(static fn (\$x{$uniqid}) => " . $this->castInMapperFunction('$x' . $uniqid, $type->genericTypes[0], $bluePrint) . ", {$propertyName})";
206 | }
207 | }
208 |
209 | if (\is_subclass_of($type->type, \BackedEnum::class)) {
210 | $enumFunction = $this->mapper->config->enumTryFrom ? 'tryFrom' : 'from';
211 |
212 | return "{$type->type}::{$enumFunction}({$propertyName})";
213 | }
214 |
215 | if (\is_subclass_of($type->type, MapsItself::class)) {
216 | return "{$type->type}::mapSelf({$propertyName}, \$mapper)";
217 | }
218 |
219 | $className = $this->dataTypeFactory->print($type, $bluePrint->fileName);
220 | if (\class_exists($className)) {
221 | return "\$mapper->objectMapper->map('{$className}', {$propertyName})";
222 | }
223 | }
224 |
225 | return '$mapper->map(\'' . $this->dataTypeFactory->print($type, $bluePrint->fileName) . '\', ' . $propertyName . ')';
226 | }
227 |
228 | private function wrapDefault(string $value, string $arrayKey, mixed $defaultValue): string
229 | {
230 | if (\str_contains($value, '?')) {
231 | $value = "({$value})";
232 | }
233 |
234 | if (\is_object($defaultValue)) {
235 | $defaultRaw = 'new ' . $defaultValue::class . '()';
236 | } else {
237 | $defaultRaw = \var_export($defaultValue, true);
238 | }
239 |
240 | return "(\\array_key_exists('{$arrayKey}', \$data) ? {$value} : {$defaultRaw})";
241 | }
242 |
243 | private function wrapArrayKeyExists(string $expression, string $arrayKey): string
244 | {
245 | $content = \PHP_EOL . \str_repeat(' ', 2) . "if (\\array_key_exists('{$arrayKey}', \$data)) {";
246 | $content .= \str_replace(\PHP_EOL, \PHP_EOL . ' ', $expression) . \PHP_EOL;
247 | $content .= \str_repeat(' ', 2) . '}';
248 |
249 | return $content;
250 | }
251 |
252 | public function __destruct()
253 | {
254 | if ($this->mapper->config->debug) {
255 | $this->clearCache();
256 | }
257 | }
258 |
259 | /** @param array{type: DataTypeCollection, default?: mixed} $property */
260 | private function buildPropertyForeachMapping(string $propertyName, array $property, ClassBluePrint $blueprint): string
261 | {
262 | $foreach = \PHP_EOL . \str_repeat(' ', 2) . '$x->' . $propertyName . ' = [];';
263 | $foreach .= \PHP_EOL . \str_repeat(' ', 2) . 'foreach ($data[\'' . $propertyName . '\'] as $key => $value) {';
264 | $foreach .= \PHP_EOL . \str_repeat(' ', 3) . '$x->' . $propertyName . '[' . $this->castInMapperFunction('$key', $property['type']->types[0]->genericTypes[0], $blueprint) . '] = ';
265 | $foreach .= $this->castInMapperFunction('$value', $property['type']->types[0]->genericTypes[1], $blueprint) . ';';
266 | $foreach .= \PHP_EOL . \str_repeat(' ', 2) . '}';
267 |
268 | if (\array_key_exists('default', $property) || $this->mapper->config->allowUninitializedFields) {
269 | $foreach = $this->wrapArrayKeyExists($foreach, $propertyName);
270 | }
271 |
272 | return $foreach;
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/src/Types/DataType.php:
--------------------------------------------------------------------------------
1 | $genericTypes
11 | * @param string|null $arrayFields
12 | */
13 | public function __construct(
14 | public readonly string $type,
15 | public readonly bool $isNullable,
16 | public readonly array $genericTypes = [],
17 | public readonly ?string $arrayFields = null,
18 | ) {
19 | }
20 |
21 | public function isArray(): bool
22 | {
23 | return \in_array(
24 | $this->type,
25 | [
26 | 'array',
27 | 'iterable',
28 | ],
29 | );
30 | }
31 |
32 | public function isGenericArray(): bool
33 | {
34 | return $this->isArray() && empty($this->genericTypes);
35 | }
36 |
37 | /** @see https://www.php.net/manual/en/language.types.intro.php */
38 | public function isNative(): bool
39 | {
40 | return \in_array(
41 | $this->type,
42 | [
43 | 'null',
44 | 'bool',
45 | 'float',
46 | 'int',
47 | 'string',
48 | 'object',
49 | ],
50 | );
51 | }
52 |
53 | /**
54 | * returns a new instance of itself that is not nullable
55 | */
56 | public function removeNullable(): self
57 | {
58 | return new self($this->type, false, $this->genericTypes);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Types/DataTypeCollection.php:
--------------------------------------------------------------------------------
1 | */
8 | public readonly array $types;
9 |
10 | /**
11 | * @param array $types
12 | */
13 | public function __construct(array $types)
14 | {
15 | // Ensure null is always the last type
16 | $typesArray = [];
17 | $null = null;
18 | foreach ($types as $type) {
19 | if ($type->type === 'null') {
20 | $null = $type;
21 | } else {
22 | $typesArray[] = $type;
23 | }
24 | }
25 |
26 | if ($null !== null) {
27 | $typesArray[] = $null;
28 | }
29 |
30 | $this->types = $typesArray;
31 | }
32 |
33 | public function isNullable(): bool
34 | {
35 | return ! empty(
36 | \array_filter(
37 | $this->types,
38 | static fn (DataType $t) => $t->isNullable || $t->type === 'null',
39 | )
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Types/DataTypeFactory.php:
--------------------------------------------------------------------------------
1 | */
12 | private array $typeCache = [];
13 |
14 | public function __construct(
15 | public readonly ClassResolver $classResolver = new ClassResolver(),
16 | ) {
17 | }
18 |
19 | /**
20 | * @throws InvalidTypeException
21 | * @throws UnexpectedTokenException
22 | */
23 | public function fromString(string $rawType, bool $forceNullable = false): DataTypeCollection
24 | {
25 | $rawType = \trim($rawType);
26 |
27 | if (\array_key_exists($rawType, $this->typeCache)) {
28 | return $this->typeCache[$rawType];
29 | }
30 |
31 | $tokens = $this->tokenize($rawType);
32 | if (\count($tokens) === 1) {
33 | return $this->typeCache[$rawType] = new DataTypeCollection([
34 | new DataType($rawType, $forceNullable),
35 | ]);
36 | }
37 |
38 | // Parse tokens and make sure nothing is left.
39 | $type = $this->parseTokens($tokens);
40 | if (! empty($tokens)) {
41 | throw new UnexpectedTokenException(\array_shift($tokens));
42 | }
43 |
44 | return $this->typeCache[$rawType] = $type;
45 | }
46 |
47 | /**
48 | * Prints a type with resolved class names, if possible.
49 | *
50 | * @param DataTypeCollection|DataType $type
51 | * @param string|null $sourceFile
52 | * @return string
53 | */
54 | public function print(DataTypeCollection|DataType $type, ?string $sourceFile = null): string
55 | {
56 | if ($type instanceof DataType) {
57 | $type = new DataTypeCollection([$type]);
58 | }
59 | \assert($type instanceof DataTypeCollection);
60 |
61 | $types = [];
62 | foreach ($type->types as $t) {
63 | $typeString = $t->type;
64 |
65 | // Attempt to resolve the class name
66 | try {
67 | $typeString = $t->isNative() || $t->isArray()
68 | ? $typeString
69 | : $this->classResolver->resolve($t->type, $sourceFile);
70 | } catch (\Throwable) {
71 | }
72 |
73 | // Add generic types between < >
74 | if (\count($t->genericTypes) > 0) {
75 | $typeString .= '<' . \implode(
76 | ', ',
77 | \array_map(
78 | fn (DataTypeCollection $c) => $this->print($c, $sourceFile),
79 | $t->genericTypes,
80 | ),
81 | ) . '>';
82 | }
83 |
84 | if ($t->isNullable) {
85 | $typeString = '?' . $typeString;
86 | }
87 |
88 | $types[] = $typeString;
89 | }
90 |
91 | return \implode('|', $types);
92 | }
93 |
94 | /**
95 | * @param string $type
96 | * @return array
97 | */
98 | private function tokenize(string $type): array
99 | {
100 | $tokens = [];
101 | $token = '';
102 |
103 | for ($i = 0; $i < \strlen($type); $i++) {
104 | $char = $type[$i];
105 | if ($char === ' ') {
106 | continue;
107 | }
108 |
109 | if (\in_array($char, ['<', '>', '?', ',', '|'])) {
110 | if (! empty(\trim($token))) {
111 | $tokens[] = $token;
112 | }
113 | $tokens[] = $char;
114 | $token = '';
115 | } else if ($char === '[') {
116 | if (! empty(\trim($token))) {
117 | $tokens[] = $token;
118 | }
119 | $token = '';
120 | if ($type[++$i] === ']') {
121 | $tokens[] = '[]';
122 | continue;
123 | }
124 |
125 | throw new UnexpectedTokenException('[');
126 | } else if ($char === '{') {
127 | if ($token !== 'array') {
128 | throw new UnexpectedTokenException('{');
129 | }
130 |
131 | $tokens[] = $token;
132 | $token = '';
133 | for (; $i < \strlen($type); $i++) {
134 | $token .= $type[$i];
135 | if ($type[$i] === '}') {
136 | break;
137 | }
138 | }
139 |
140 | if (! \str_ends_with($token, '}')) {
141 | throw new InvalidTypeException($type, 'Missing closing }');
142 | }
143 |
144 | $tokens[] = $token;
145 | $token = '';
146 | } else {
147 | $token .= $char;
148 | }
149 | }
150 |
151 | if ($token !== '') {
152 | $tokens[] = $token;
153 | }
154 |
155 | return $tokens;
156 | }
157 |
158 | /**
159 | * @param array $tokens
160 | * @return DataTypeCollection
161 | */
162 | private function parseTokens(array &$tokens): DataTypeCollection
163 | {
164 | $types = [];
165 |
166 | $stringStack = '';
167 | $nullable = false;
168 |
169 | while ($token = \array_shift($tokens)) {
170 | if ($token === '<') {
171 | $genericTypes = $this->fetchGenericTypes($tokens);
172 | while (($tokens[0] ?? '') === '[]') {
173 | $stringStack .= \array_shift($tokens);
174 | }
175 | $types[] = $this->makeDataType(
176 | $stringStack,
177 | $nullable,
178 | $genericTypes,
179 | );
180 | $stringStack = '';
181 | $nullable = false;
182 |
183 | continue;
184 | }
185 |
186 | if ($token === '|') {
187 | $types[] = $this->makeDataType($stringStack, $nullable);
188 | $stringStack = '';
189 | $nullable = false;
190 |
191 | continue;
192 | }
193 |
194 | if ($token === '?') {
195 | $nullable = true;
196 | continue;
197 | }
198 |
199 | if (\str_starts_with($token, '{')) {
200 | $types[] = new DataType('array', $nullable, arrayFields: $token);
201 | $stringStack = '';
202 | $nullable = false;
203 |
204 | continue;
205 | }
206 |
207 | if (\in_array($token, ['>', ','])) {
208 | throw new UnexpectedTokenException($token);
209 | }
210 |
211 | $stringStack .= $token;
212 | }
213 |
214 | if (! empty($stringStack)) {
215 | $types[] = $this->makeDataType($stringStack, $nullable);
216 | }
217 |
218 | return new DataTypeCollection($types);
219 | }
220 |
221 | /**
222 | * @param array $tokens
223 | * @return array
224 | */
225 | private function fetchGenericTypes(array &$tokens): array
226 | {
227 | $types = [];
228 | $collection = [];
229 | $stringStack = '';
230 | $nullable = false;
231 | while ($token = \array_shift($tokens)) {
232 | if ($token === '<') {
233 | $types[] = $this->makeDataType(
234 | $stringStack,
235 | $nullable,
236 | $this->fetchGenericTypes($tokens),
237 | );
238 | $stringStack = '';
239 | $nullable = false;
240 |
241 | continue;
242 | }
243 |
244 | if ($token === ',' || $token === '|') {
245 | if (! empty($stringStack)) {
246 | $types[] = $this->makeDataType($stringStack, $nullable);
247 | }
248 |
249 | if ($token === ',') {
250 | $collection[] = new DataTypeCollection($types);
251 | $types = [];
252 | }
253 |
254 | $stringStack = '';
255 | $nullable = false;
256 | continue;
257 | }
258 |
259 | if ($token === '>') {
260 | break;
261 | }
262 |
263 | if ($token === '?') {
264 | $nullable = true;
265 | continue;
266 | }
267 |
268 | if (\str_starts_with($token, '{')) {
269 | $types[] = new DataType('array', $nullable, arrayFields: $token);
270 | $stringStack = '';
271 | $nullable = false;
272 |
273 | continue;
274 | }
275 |
276 | $stringStack .= $token;
277 | }
278 |
279 | if (! empty($stringStack)) {
280 | $types[] = $this->makeDataType($stringStack, $nullable);
281 | }
282 | if (! empty($types)) {
283 | $collection[] = new DataTypeCollection($types);
284 | }
285 |
286 | return $collection;
287 | }
288 |
289 | /**
290 | * @param string $type
291 | * @param bool $nullable
292 | * @param array $genericTypes
293 | * @return DataType
294 | */
295 | private function makeDataType(string $type, bool $nullable = false, array $genericTypes = []): DataType
296 | {
297 | // Parse a stack of [] arrays
298 | $arrayStack = 0;
299 | while (\str_ends_with($type, '[]')) {
300 | $type = \substr($type, 0, -2);
301 | $arrayStack++;
302 | }
303 | if ($arrayStack > 0) {
304 | $type = new DataType($type, false, $genericTypes);
305 | for ($i = 0; $i < $arrayStack; $i++) {
306 | $type = new DataType(
307 | 'array',
308 | $arrayStack - $i === 1 ? $nullable : false,
309 | [
310 | new DataTypeCollection([$type]),
311 | ],
312 | );
313 | }
314 |
315 | return $type;
316 | }
317 |
318 | return new DataType($type, $nullable, $genericTypes);
319 | }
320 | }
321 |
--------------------------------------------------------------------------------