1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace Cycle\ORM\Entity\Behavior\Schema; |
||||
6 | |||||
7 | use Cycle\Database\Schema\AbstractColumn; |
||||
0 ignored issues
–
show
|
|||||
8 | use Cycle\Database\Schema\AbstractTable; |
||||
9 | use Cycle\ORM\Entity\Behavior\Exception\BehaviorCompilationException; |
||||
10 | use Cycle\ORM\Parser\Typecast; |
||||
11 | use Cycle\ORM\Parser\TypecastInterface; |
||||
12 | use Cycle\ORM\SchemaInterface; |
||||
13 | use Cycle\Schema\Defaults; |
||||
14 | use Cycle\Schema\Definition\Entity; |
||||
15 | use Cycle\Schema\Definition\Field; |
||||
16 | use Cycle\Schema\Definition\Map\FieldMap; |
||||
17 | use Cycle\Schema\Registry; |
||||
18 | |||||
19 | /** |
||||
20 | * @internal |
||||
21 | */ |
||||
22 | class RegistryModifier |
||||
23 | { |
||||
24 | protected const DEFINITION = '/(?P<type>[a-z]+)(?: *\((?P<options>[^\)]+)\))?/i'; |
||||
25 | protected const INTEGER_TYPES = [ |
||||
26 | 'int', |
||||
27 | 'smallint', |
||||
28 | 'tinyint', |
||||
29 | 'bigint', |
||||
30 | 'integer', |
||||
31 | 160 | 'tinyInteger', |
|||
32 | 'smallInteger', |
||||
33 | 160 | 'bigInteger', |
|||
34 | 160 | ]; |
|||
35 | 160 | protected const DATETIME_TYPES = ['datetime', 'datetime2']; |
|||
36 | protected const INT_COLUMN = AbstractColumn::INT; |
||||
37 | protected const STRING_COLUMN = AbstractColumn::STRING; |
||||
38 | 112 | protected const DATETIME_COLUMN = 'datetime'; |
|||
39 | protected const UUID_COLUMN = 'uuid'; |
||||
40 | 112 | ||||
41 | 64 | protected FieldMap $fields; |
|||
42 | 8 | protected AbstractTable $table; |
|||
43 | protected Entity $entity; |
||||
44 | 56 | protected Defaults $defaults; |
|||
45 | |||||
46 | 48 | public function __construct(Registry $registry, string $role) |
|||
47 | { |
||||
48 | $this->entity = $registry->getEntity($role); |
||||
49 | 96 | $this->fields = $this->entity->getFields(); |
|||
50 | $this->table = $registry->getTableSchema($this->entity); |
||||
51 | 96 | $this->defaults = $registry->getDefaults(); |
|||
52 | } |
||||
53 | |||||
54 | 96 | public static function isIntegerType(string $type): bool |
|||
55 | { |
||||
56 | \preg_match(self::DEFINITION, $type, $matches); |
||||
57 | 56 | ||||
58 | return \in_array($matches['type'], self::INTEGER_TYPES, true); |
||||
59 | 56 | } |
|||
60 | 40 | ||||
61 | public static function isDatetimeType(string $type): bool |
||||
62 | { |
||||
63 | 40 | \preg_match(self::DEFINITION, $type, $matches); |
|||
64 | |||||
65 | 40 | return \in_array($matches['type'], self::DATETIME_TYPES, true); |
|||
66 | } |
||||
67 | |||||
68 | 56 | public static function isStringType(string $type): bool |
|||
69 | { |
||||
70 | 56 | \preg_match(self::DEFINITION, $type, $matches); |
|||
71 | |||||
72 | return $matches['type'] === 'string'; |
||||
73 | 48 | } |
|||
74 | |||||
75 | 48 | public static function isUuidType(string $type): bool |
|||
76 | 40 | { |
|||
77 | \preg_match(self::DEFINITION, $type, $matches); |
||||
78 | |||||
79 | 40 | return $matches['type'] === 'uuid'; |
|||
80 | } |
||||
81 | 40 | ||||
82 | public function addDatetimeColumn(string $columnName, string $fieldName, int|null $generated = null): AbstractColumn |
||||
83 | { |
||||
84 | 48 | if ($this->fields->has($fieldName)) { |
|||
85 | if (!static::isDatetimeType($this->fields->get($fieldName)->getType())) { |
||||
86 | 48 | throw new BehaviorCompilationException(\sprintf('Field %s must be of type datetime.', $fieldName)); |
|||
87 | } |
||||
88 | $this->validateColumnName($fieldName, $columnName); |
||||
89 | $this->fields->get($fieldName)->setGenerated($generated); |
||||
90 | |||||
91 | return $this->table->column($columnName); |
||||
92 | 32 | } |
|||
93 | |||||
94 | 32 | $field = (new Field()) |
|||
95 | ->setColumn($columnName) |
||||
96 | ->setType('datetime') |
||||
97 | ->setTypecast('datetime') |
||||
98 | ->setGenerated($generated); |
||||
99 | $this->fields->set($fieldName, $field); |
||||
100 | |||||
101 | return $this->table->column($columnName)->type(self::DATETIME_COLUMN); |
||||
102 | } |
||||
103 | 32 | ||||
104 | public function addIntegerColumn(string $columnName, string $fieldName, int|null $generated = null): AbstractColumn |
||||
105 | 32 | { |
|||
106 | if ($this->fields->has($fieldName)) { |
||||
107 | if (!static::isIntegerType($this->fields->get($fieldName)->getType())) { |
||||
108 | 104 | throw new BehaviorCompilationException(\sprintf('Field %s must be of type integer.', $fieldName)); |
|||
109 | } |
||||
110 | 104 | $this->validateColumnName($fieldName, $columnName); |
|||
111 | 64 | $this->fields->get($fieldName)->setGenerated($generated); |
|||
112 | |||||
113 | return $this->table->column($columnName); |
||||
114 | 88 | } |
|||
115 | |||||
116 | $field = (new Field()) |
||||
117 | ->setColumn($columnName) |
||||
118 | ->setType('integer') |
||||
119 | ->setTypecast('int') |
||||
120 | 24 | ->setGenerated($generated); |
|||
121 | $this->fields->set($fieldName, $field); |
||||
122 | 24 | ||||
123 | 16 | return $this->table->column($columnName)->type(self::INT_COLUMN); |
|||
124 | } |
||||
125 | |||||
126 | 24 | public function addStringColumn(string $columnName, string $fieldName, int|null $generated = null): AbstractColumn |
|||
127 | 24 | { |
|||
128 | 16 | if ($this->fields->has($fieldName)) { |
|||
129 | 16 | if (!static::isStringType($this->fields->get($fieldName)->getType())) { |
|||
130 | throw new BehaviorCompilationException(\sprintf('Field %s must be of type string.', $fieldName)); |
||||
131 | } |
||||
132 | 16 | $this->validateColumnName($fieldName, $columnName); |
|||
133 | 16 | $this->fields->get($fieldName)->setGenerated($generated); |
|||
134 | 16 | ||||
135 | return $this->table->column($columnName); |
||||
136 | 16 | } |
|||
137 | |||||
138 | $field = (new Field())->setColumn($columnName)->setType('string')->setGenerated($generated); |
||||
139 | $this->fields->set($fieldName, $field); |
||||
140 | |||||
141 | return $this->table->column($columnName)->type(self::STRING_COLUMN); |
||||
142 | 96 | } |
|||
143 | |||||
144 | 96 | /** |
|||
145 | * @throws BehaviorCompilationException |
||||
146 | 96 | */ |
|||
147 | 8 | public function addUuidColumn(string $columnName, string $fieldName, int|null $generated = null): AbstractColumn |
|||
148 | 8 | { |
|||
149 | 8 | if ($this->fields->has($fieldName)) { |
|||
150 | 8 | if (!static::isUuidType($this->fields->get($fieldName)->getType())) { |
|||
151 | throw new BehaviorCompilationException(\sprintf('Field %s must be of type uuid.', $fieldName)); |
||||
152 | 8 | } |
|||
153 | $this->validateColumnName($fieldName, $columnName); |
||||
154 | $this->fields->get($fieldName)->setGenerated($generated); |
||||
155 | |||||
156 | return $this->table->column($columnName); |
||||
157 | } |
||||
158 | |||||
159 | 104 | $field = (new Field())->setColumn($columnName)->setType('uuid')->setGenerated($generated); |
|||
160 | $this->fields->set($fieldName, $field); |
||||
161 | 104 | ||||
162 | return $this->table->column($columnName)->type(self::UUID_COLUMN); |
||||
163 | 64 | } |
|||
164 | 64 | ||||
165 | public function findColumnName(string $fieldName, ?string $columnName): ?string |
||||
166 | { |
||||
167 | 40 | if ($columnName !== null) { |
|||
168 | 40 | return $columnName; |
|||
169 | } |
||||
170 | |||||
171 | 40 | return $this->fields->has($fieldName) ? $this->fields->get($fieldName)->getColumn() : null; |
|||
172 | } |
||||
173 | |||||
174 | /** |
||||
175 | * @param class-string<TypecastInterface> $handler |
||||
0 ignored issues
–
show
|
|||||
176 | */ |
||||
177 | 40 | public function setTypecast(Field $field, array|string|null $rule, string $handler = Typecast::class): Field |
|||
178 | { |
||||
179 | if ($field->getTypecast() === null) { |
||||
180 | $field->setTypecast($rule); |
||||
181 | } |
||||
182 | |||||
183 | $defaultHandlers = $this->defaults[SchemaInterface::TYPECAST_HANDLER] ?? []; |
||||
184 | if (!\is_array($defaultHandlers)) { |
||||
185 | $defaultHandlers = [$defaultHandlers]; |
||||
186 | } |
||||
187 | |||||
188 | $handlers = $this->entity->getTypecast() ?? []; |
||||
189 | if (!\is_array($handlers)) { |
||||
190 | $handlers = [$handlers]; |
||||
191 | } |
||||
192 | |||||
193 | if (!\in_array($handler, $handlers, true) && !\in_array($handler, $defaultHandlers, true)) { |
||||
0 ignored issues
–
show
It seems like
$handlers can also be of type string ; however, parameter $haystack of in_array() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
194 | $this->entity->setTypecast(\array_merge($handlers, [$handler])); |
||||
0 ignored issues
–
show
It seems like
$handlers can also be of type string ; however, parameter $arrays of array_merge() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
195 | } |
||||
196 | |||||
197 | return $field; |
||||
198 | } |
||||
199 | |||||
200 | /** |
||||
201 | * @throws BehaviorCompilationException |
||||
202 | */ |
||||
203 | protected function validateColumnName(string $fieldName, string $columnName): void |
||||
204 | { |
||||
205 | $field = $this->fields->get($fieldName); |
||||
206 | |||||
207 | if ($field->getColumn() !== $columnName) { |
||||
208 | throw new BehaviorCompilationException( |
||||
209 | \sprintf( |
||||
210 | 'Ambiguous column name definition. ' |
||||
211 | . 'The `%s` field already linked with the `%s` column but the behavior expects `%s`.', |
||||
212 | $fieldName, |
||||
213 | $field->getColumn(), |
||||
214 | $columnName, |
||||
215 | ), |
||||
216 | ); |
||||
217 | } |
||||
218 | } |
||||
219 | |||||
220 | /** |
||||
221 | * @deprecated since v1.2 |
||||
222 | */ |
||||
223 | protected function isType(string $type, string $fieldName, string $columnName): bool |
||||
224 | { |
||||
225 | if ($type === self::DATETIME_COLUMN) { |
||||
226 | return |
||||
227 | $this->table->column($columnName)->getInternalType() === self::DATETIME_COLUMN || |
||||
228 | $this->fields->get($fieldName)->getType() === self::DATETIME_COLUMN; |
||||
229 | } |
||||
230 | |||||
231 | if ($type === self::INT_COLUMN) { |
||||
232 | return $this->table->column($columnName)->getType() === self::INT_COLUMN; |
||||
233 | } |
||||
234 | |||||
235 | if ($type === self::UUID_COLUMN) { |
||||
236 | return |
||||
237 | $this->table->column($columnName)->getInternalType() === self::UUID_COLUMN || |
||||
238 | $this->fields->get($fieldName)->getType() === self::UUID_COLUMN; |
||||
239 | } |
||||
240 | |||||
241 | return $this->table->column($columnName)->getType() === $type; |
||||
242 | } |
||||
243 | } |
||||
244 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths