This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace frictionlessdata\tableschema; |
||
4 | |||
5 | use frictionlessdata\tableschema\Fields\FieldsFactory; |
||
6 | |||
7 | /** |
||
8 | * Table Schema representation. |
||
9 | * Loads and validates a Table Schema descriptor from a descriptor / path to file / url containing the descriptor. |
||
10 | */ |
||
11 | class Schema |
||
12 | { |
||
13 | protected $DEFAULT_FIELD_CLASS = '\\frictionlessdata\\tableschema\\Fields\\StringField'; |
||
14 | |||
15 | /** |
||
16 | * Schema constructor. |
||
17 | * |
||
18 | * @param mixed $descriptor |
||
19 | * |
||
20 | * @throws Exceptions\SchemaLoadException |
||
21 | * @throws Exceptions\SchemaValidationFailedException |
||
22 | */ |
||
23 | public function __construct($descriptor = null) |
||
24 | { |
||
25 | if (is_null($descriptor)) { |
||
26 | $this->descriptor = (object) ['fields' => []]; |
||
27 | } else { |
||
28 | if (Utils::isJsonString($descriptor)) { |
||
29 | // it's a json encoded string |
||
30 | try { |
||
31 | $this->descriptor = json_decode($descriptor); |
||
32 | } catch (\Exception $e) { |
||
33 | throw new Exceptions\SchemaLoadException($descriptor, null, $e->getMessage()); |
||
34 | } |
||
35 | if (!$this->descriptor) { |
||
36 | throw new Exceptions\SchemaLoadException($descriptor, null, 'invalid json'); |
||
37 | } |
||
38 | } elseif (is_string($descriptor)) { |
||
39 | // it's a url or file path |
||
40 | $descriptorSource = $descriptor; |
||
41 | try { |
||
42 | $descriptor = file_get_contents($descriptorSource); |
||
43 | } catch (\Exception $e) { |
||
44 | throw new Exceptions\SchemaLoadException(null, $descriptorSource, $e->getMessage()); |
||
45 | } |
||
46 | try { |
||
47 | $this->descriptor = json_decode($descriptor); |
||
48 | } catch (\Exception $e) { |
||
49 | throw new Exceptions\SchemaLoadException($descriptor, $descriptorSource, $e->getMessage()); |
||
50 | } |
||
51 | } else { |
||
52 | $this->descriptor = $descriptor; |
||
53 | } |
||
54 | if (!is_object($this->descriptor) && !is_array($this->descriptor)) { |
||
55 | throw new Exceptions\SchemaLoadException($descriptor, null, 'descriptor must be an object or array'); |
||
56 | } |
||
57 | $this->descriptor = json_decode(json_encode($this->descriptor)); |
||
58 | $validationErrors = SchemaValidator::validate($this->descriptor()); |
||
59 | if (count($validationErrors) > 0) { |
||
60 | throw new Exceptions\SchemaValidationFailedException($validationErrors); |
||
61 | } |
||
62 | } |
||
63 | } |
||
64 | |||
65 | /** |
||
66 | * loads and validates the given descriptor source (php object / string / path to file / url) |
||
67 | * returns an array of validation error objects. |
||
68 | * |
||
69 | * @param mixed $descriptor |
||
70 | * |
||
71 | * @return array |
||
72 | */ |
||
73 | public static function validate($descriptor) |
||
74 | { |
||
75 | try { |
||
76 | new static($descriptor); |
||
77 | |||
78 | return []; |
||
79 | } catch (Exceptions\SchemaLoadException $e) { |
||
80 | return [ |
||
81 | new SchemaValidationError(SchemaValidationError::LOAD_FAILED, $e->getMessage()), |
||
82 | ]; |
||
83 | } catch (Exceptions\SchemaValidationFailedException $e) { |
||
84 | return $e->validationErrors; |
||
85 | } |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * @return object |
||
90 | */ |
||
91 | public function descriptor() |
||
92 | { |
||
93 | return $this->descriptor; |
||
94 | } |
||
95 | |||
96 | public function fullDescriptor() |
||
97 | { |
||
98 | $fullDescriptor = $this->descriptor(); |
||
99 | $fullFieldDescriptors = []; |
||
100 | foreach ($this->fields() as $field) { |
||
0 ignored issues
–
show
|
|||
101 | $fullFieldDescriptors[] = $field->fullDescriptor(); |
||
102 | } |
||
103 | $fullDescriptor->fields = $fullFieldDescriptors; |
||
104 | $fullDescriptor->missingValues = $this->missingValues(); |
||
105 | |||
106 | return $fullDescriptor; |
||
107 | } |
||
108 | |||
109 | public function field($name, $field = null) |
||
110 | { |
||
111 | $fields = $this->fields(); |
||
112 | if (!is_null($field)) { |
||
113 | $fields[$name] = $field; |
||
114 | |||
115 | return $this->fields($fields); |
||
116 | } elseif (array_key_exists($name, $fields)) { |
||
117 | return $fields[$name]; |
||
118 | } else { |
||
119 | throw new \Exception("unknown field name: {$name}"); |
||
120 | } |
||
121 | } |
||
122 | |||
123 | public function removeField($name) |
||
124 | { |
||
125 | $fields = $this->fields(); |
||
126 | unset($fields[$name]); |
||
127 | |||
128 | return $this->fields($fields); |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * @return Fields\BaseField[]|Schema array of field name => field object or the schema in case of editing |
||
133 | */ |
||
134 | public function fields($newFields = null) |
||
135 | { |
||
136 | if (is_null($newFields)) { |
||
137 | if (empty($this->fieldsCache)) { |
||
138 | foreach ($this->descriptor()->fields as $fieldDescriptor) { |
||
139 | if (!array_key_exists('type', $fieldDescriptor)) { |
||
140 | $field = new $this->DEFAULT_FIELD_CLASS($fieldDescriptor); |
||
141 | } else { |
||
142 | $field = Fields\FieldsFactory::field($fieldDescriptor); |
||
143 | } |
||
144 | $this->fieldsCache[$field->name()] = $field; |
||
145 | } |
||
146 | } |
||
147 | |||
148 | return $this->fieldsCache; |
||
149 | } else { |
||
150 | $this->descriptor()->fields = []; |
||
151 | $this->fieldsCache = []; |
||
152 | foreach ($newFields as $name => $field) { |
||
153 | $field = FieldsFactory::field($field, $name); |
||
154 | $this->fieldsCache[$name] = $field; |
||
155 | $this->descriptor()->fields[] = $field->descriptor(); |
||
156 | } |
||
157 | |||
158 | return $this->revalidate(); |
||
159 | } |
||
160 | } |
||
161 | |||
162 | public function missingValues($missingValues = null) |
||
163 | { |
||
164 | if (is_null($missingValues)) { |
||
165 | return isset($this->descriptor()->missingValues) ? $this->descriptor()->missingValues : ['']; |
||
166 | } else { |
||
167 | $this->descriptor()->missingValues = $missingValues; |
||
168 | |||
169 | return $this->revalidate(); |
||
170 | } |
||
171 | } |
||
172 | |||
173 | public function primaryKey($primaryKey = null) |
||
174 | { |
||
175 | if (is_null($primaryKey)) { |
||
176 | $primaryKey = isset($this->descriptor()->primaryKey) ? $this->descriptor()->primaryKey : []; |
||
177 | |||
178 | return is_array($primaryKey) ? $primaryKey : [$primaryKey]; |
||
179 | } else { |
||
180 | $this->descriptor()->primaryKey = $primaryKey; |
||
181 | |||
182 | return $this->revalidate(); |
||
183 | } |
||
184 | } |
||
185 | |||
186 | public function foreignKeys($foreignKeys = null) |
||
187 | { |
||
188 | if (is_null($foreignKeys)) { |
||
189 | $foreignKeys = isset($this->descriptor()->foreignKeys) ? $this->descriptor()->foreignKeys : []; |
||
190 | foreach ($foreignKeys as &$foreignKey) { |
||
191 | if (!is_array($foreignKey->fields)) { |
||
192 | $foreignKey->fields = [$foreignKey->fields]; |
||
193 | } |
||
194 | if (!is_array($foreignKey->reference->fields)) { |
||
195 | $foreignKey->reference->fields = [$foreignKey->reference->fields]; |
||
196 | } |
||
197 | } |
||
198 | |||
199 | return $foreignKeys; |
||
200 | } else { |
||
201 | $this->descriptor()->foreignKeys = $foreignKeys; |
||
202 | |||
203 | return $this->revalidate(); |
||
204 | } |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | * @param mixed[] $row |
||
209 | * |
||
210 | * @return mixed[] |
||
211 | * |
||
212 | * @throws Exceptions\FieldValidationException |
||
213 | */ |
||
214 | public function castRow($row) |
||
215 | { |
||
216 | $outRow = []; |
||
217 | $validationErrors = []; |
||
218 | foreach ($this->fields() as $fieldName => $field) { |
||
0 ignored issues
–
show
The expression
$this->fields() of type array|this<frictionlessdata\tableschema\Schema> is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
219 | $value = array_key_exists($fieldName, $row) ? $row[$fieldName] : null; |
||
220 | if (in_array($value, $this->missingValues())) { |
||
221 | $value = null; |
||
222 | } |
||
223 | try { |
||
224 | $outRow[$fieldName] = $field->castValue($value); |
||
225 | } catch (Exceptions\FieldValidationException $e) { |
||
226 | $validationErrors = array_merge($validationErrors, $e->validationErrors); |
||
227 | } |
||
228 | } |
||
229 | if (count($validationErrors) > 0) { |
||
230 | throw new Exceptions\FieldValidationException($validationErrors); |
||
231 | } |
||
232 | |||
233 | return $outRow; |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * @param array $row |
||
238 | * |
||
239 | * @return SchemaValidationError[] |
||
240 | */ |
||
241 | public function validateRow($row) |
||
242 | { |
||
243 | try { |
||
244 | $this->castRow($row); |
||
245 | |||
246 | return []; |
||
247 | } catch (Exceptions\FieldValidationException $e) { |
||
248 | return $e->validationErrors; |
||
249 | } |
||
250 | } |
||
251 | |||
252 | public function save($filename) |
||
253 | { |
||
254 | file_put_contents($filename, json_encode($this->fullDescriptor())); |
||
255 | } |
||
256 | |||
257 | public function revalidate() |
||
258 | { |
||
259 | $validationErrors = SchemaValidator::validate($this->descriptor()); |
||
260 | if (count($validationErrors) > 0) { |
||
261 | throw new Exceptions\SchemaValidationFailedException($validationErrors); |
||
262 | } else { |
||
263 | return $this; |
||
264 | } |
||
265 | } |
||
266 | |||
267 | protected $descriptor; |
||
268 | protected $fieldsCache = []; |
||
269 | } |
||
270 |
There are different options of fixing this problem.
If you want to be on the safe side, you can add an additional type-check:
If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:
Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.