vendor/spatie/data-transfer-object/src/DocblockFieldValidator.php line 49

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Spatie\DataTransferObject;
  4. use RecursiveArrayIterator;
  5. use RecursiveIteratorIterator;
  6. class DocblockFieldValidator extends FieldValidator
  7. {
  8. public const DOCBLOCK_REGEX = <<<REGEXP
  9. /
  10. @var ( # Starting with `@var `, we'll capture the definition the follows
  11. (?: # Not explicitly capturing this group,
  12. # which contains repeated sets of type definitions
  13. (?: # Not explicitly capturing this group
  14. [\w?|\\\\<>,\s] # Matches type definitions like `int|string|\My\Object|array<int, string>`
  15. )+ # These definitions can be repeated
  16. (?: # Not explicitly capturing this group
  17. \[] # Matches array definitions like `int[]`
  18. )? # Array definitions are optional though
  19. )+ # Repeated sets of type definitions
  20. ) # The whole definition after `@var ` is captured in one group
  21. /x
  22. REGEXP;
  23. public function __construct(string $definition, bool $hasDefaultValue = false)
  24. {
  25. preg_match(
  26. DocblockFieldValidator::DOCBLOCK_REGEX,
  27. $definition,
  28. $matches
  29. );
  30. $definition = trim($matches[1] ?? '');
  31. $this->hasTypeDeclaration = $definition !== '';
  32. $this->hasDefaultValue = $hasDefaultValue;
  33. $this->isNullable = $this->resolveNullable($definition);
  34. $this->isMixed = $this->resolveIsMixed($definition);
  35. $this->isMixedArray = $this->resolveIsMixedArray($definition);
  36. $this->allowedTypes = $this->resolveAllowedTypes($definition);
  37. $this->allowedArrayTypes = $this->resolveAllowedArrayTypes($definition);
  38. $this->allowedArrayKeyTypes = $this->resolveAllowedArrayKeyTypes($definition);
  39. }
  40. private function resolveNullable(string $definition): bool
  41. {
  42. if (! $definition) {
  43. return true;
  44. }
  45. if (Str::contains($definition, ['mixed', 'null', '?'])) {
  46. return true;
  47. }
  48. return false;
  49. }
  50. private function resolveIsMixed(string $definition): bool
  51. {
  52. return Str::contains($definition, ['mixed']);
  53. }
  54. private function resolveIsMixedArray(string $definition): bool
  55. {
  56. $types = $this->normaliseTypes(...explode('|', $definition));
  57. foreach ($types as $type) {
  58. if (in_array($type, ['iterable', 'array'])) {
  59. return true;
  60. }
  61. }
  62. return false;
  63. }
  64. private function resolveAllowedTypes(string $definition): array
  65. {
  66. return $this->normaliseTypes(...explode('|', $definition));
  67. }
  68. private function resolveAllowedArrayTypes(string $definition): array
  69. {
  70. // Iterators flatten the array for multiple return types from DataTransferObjectCollection::current
  71. return $this->normaliseTypes(...(new RecursiveIteratorIterator(new RecursiveArrayIterator(array_map(
  72. function (string $type) {
  73. if (! $type) {
  74. return;
  75. }
  76. if (strpos($type, '[]') !== false) {
  77. return str_replace('[]', '', $type);
  78. }
  79. if (strpos($type, '>') !== false) {
  80. preg_match('/([\w\\\\]+)(>)/', $type, $matches);
  81. return $matches[1];
  82. }
  83. if (is_subclass_of($type, DataTransferObjectCollection::class)) {
  84. return $this->resolveAllowedArrayTypesFromCollection($type);
  85. }
  86. return null;
  87. },
  88. explode('|', $definition)
  89. )))));
  90. }
  91. private function resolveAllowedArrayKeyTypes(string $definition): array
  92. {
  93. return $this->normaliseTypes(...array_map(
  94. function (string $type) {
  95. if (strpos($type, '<') === false) {
  96. return;
  97. }
  98. preg_match('/(<)([\w\\\\]+)(,)/', $type, $matches);
  99. return $matches[2] ?? null;
  100. },
  101. explode('|', $definition)
  102. ));
  103. }
  104. }