1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Deleted file in the 'filearchive' table. |
4
|
|
|
* |
5
|
|
|
* This program is free software; you can redistribute it and/or modify |
6
|
|
|
* it under the terms of the GNU General Public License as published by |
7
|
|
|
* the Free Software Foundation; either version 2 of the License, or |
8
|
|
|
* (at your option) any later version. |
9
|
|
|
* |
10
|
|
|
* This program is distributed in the hope that it will be useful, |
11
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
12
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13
|
|
|
* GNU General Public License for more details. |
14
|
|
|
* |
15
|
|
|
* You should have received a copy of the GNU General Public License along |
16
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc., |
17
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18
|
|
|
* http://www.gnu.org/copyleft/gpl.html |
19
|
|
|
* |
20
|
|
|
* @file |
21
|
|
|
* @ingroup FileAbstraction |
22
|
|
|
*/ |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Class representing a row of the 'filearchive' table |
26
|
|
|
* |
27
|
|
|
* @ingroup FileAbstraction |
28
|
|
|
*/ |
29
|
|
|
class ArchivedFile { |
30
|
|
|
/** @var int Filearchive row ID */ |
31
|
|
|
private $id; |
32
|
|
|
|
33
|
|
|
/** @var string File name */ |
34
|
|
|
private $name; |
35
|
|
|
|
36
|
|
|
/** @var string FileStore storage group */ |
37
|
|
|
private $group; |
38
|
|
|
|
39
|
|
|
/** @var string FileStore SHA-1 key */ |
40
|
|
|
private $key; |
41
|
|
|
|
42
|
|
|
/** @var int File size in bytes */ |
43
|
|
|
private $size; |
44
|
|
|
|
45
|
|
|
/** @var int Size in bytes */ |
46
|
|
|
private $bits; |
47
|
|
|
|
48
|
|
|
/** @var int Width */ |
49
|
|
|
private $width; |
50
|
|
|
|
51
|
|
|
/** @var int Height */ |
52
|
|
|
private $height; |
53
|
|
|
|
54
|
|
|
/** @var string Metadata string */ |
55
|
|
|
private $metadata; |
56
|
|
|
|
57
|
|
|
/** @var string MIME type */ |
58
|
|
|
private $mime; |
59
|
|
|
|
60
|
|
|
/** @var string Media type */ |
61
|
|
|
private $media_type; |
62
|
|
|
|
63
|
|
|
/** @var string Upload description */ |
64
|
|
|
private $description; |
65
|
|
|
|
66
|
|
|
/** @var int User ID of uploader */ |
67
|
|
|
private $user; |
68
|
|
|
|
69
|
|
|
/** @var string User name of uploader */ |
70
|
|
|
private $user_text; |
71
|
|
|
|
72
|
|
|
/** @var string Time of upload */ |
73
|
|
|
private $timestamp; |
74
|
|
|
|
75
|
|
|
/** @var bool Whether or not all this has been loaded from the database (loadFromXxx) */ |
76
|
|
|
private $dataLoaded; |
77
|
|
|
|
78
|
|
|
/** @var int Bitfield akin to rev_deleted */ |
79
|
|
|
private $deleted; |
80
|
|
|
|
81
|
|
|
/** @var string SHA-1 hash of file content */ |
82
|
|
|
private $sha1; |
83
|
|
|
|
84
|
|
|
/** @var string Number of pages of a multipage document, or false for |
85
|
|
|
* documents which aren't multipage documents |
86
|
|
|
*/ |
87
|
|
|
private $pageCount; |
88
|
|
|
|
89
|
|
|
/** @var string Original base filename */ |
90
|
|
|
private $archive_name; |
91
|
|
|
|
92
|
|
|
/** @var MediaHandler */ |
93
|
|
|
protected $handler; |
94
|
|
|
|
95
|
|
|
/** @var Title */ |
96
|
|
|
protected $title; # image title |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @throws MWException |
100
|
|
|
* @param Title $title |
101
|
|
|
* @param int $id |
102
|
|
|
* @param string $key |
103
|
|
|
* @param string $sha1 |
104
|
|
|
*/ |
105
|
|
|
function __construct( $title, $id = 0, $key = '', $sha1 = '' ) { |
106
|
|
|
$this->id = -1; |
107
|
|
|
$this->title = false; |
|
|
|
|
108
|
|
|
$this->name = false; |
|
|
|
|
109
|
|
|
$this->group = 'deleted'; // needed for direct use of constructor |
110
|
|
|
$this->key = ''; |
111
|
|
|
$this->size = 0; |
112
|
|
|
$this->bits = 0; |
113
|
|
|
$this->width = 0; |
114
|
|
|
$this->height = 0; |
115
|
|
|
$this->metadata = ''; |
116
|
|
|
$this->mime = "unknown/unknown"; |
117
|
|
|
$this->media_type = ''; |
118
|
|
|
$this->description = ''; |
119
|
|
|
$this->user = 0; |
120
|
|
|
$this->user_text = ''; |
121
|
|
|
$this->timestamp = null; |
122
|
|
|
$this->deleted = 0; |
123
|
|
|
$this->dataLoaded = false; |
124
|
|
|
$this->exists = false; |
|
|
|
|
125
|
|
|
$this->sha1 = ''; |
126
|
|
|
|
127
|
|
|
if ( $title instanceof Title ) { |
128
|
|
|
$this->title = File::normalizeTitle( $title, 'exception' ); |
129
|
|
|
$this->name = $title->getDBkey(); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
if ( $id ) { |
133
|
|
|
$this->id = $id; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
if ( $key ) { |
137
|
|
|
$this->key = $key; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
if ( $sha1 ) { |
141
|
|
|
$this->sha1 = $sha1; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
if ( !$id && !$key && !( $title instanceof Title ) && !$sha1 ) { |
145
|
|
|
throw new MWException( "No specifications provided to ArchivedFile constructor." ); |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* Loads a file object from the filearchive table |
151
|
|
|
* @throws MWException |
152
|
|
|
* @return bool|null True on success or null |
153
|
|
|
*/ |
154
|
|
|
public function load() { |
155
|
|
|
if ( $this->dataLoaded ) { |
156
|
|
|
return true; |
157
|
|
|
} |
158
|
|
|
$conds = []; |
159
|
|
|
|
160
|
|
|
if ( $this->id > 0 ) { |
161
|
|
|
$conds['fa_id'] = $this->id; |
162
|
|
|
} |
163
|
|
|
if ( $this->key ) { |
164
|
|
|
$conds['fa_storage_group'] = $this->group; |
165
|
|
|
$conds['fa_storage_key'] = $this->key; |
166
|
|
|
} |
167
|
|
|
if ( $this->title ) { |
168
|
|
|
$conds['fa_name'] = $this->title->getDBkey(); |
169
|
|
|
} |
170
|
|
|
if ( $this->sha1 ) { |
171
|
|
|
$conds['fa_sha1'] = $this->sha1; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
if ( !count( $conds ) ) { |
175
|
|
|
throw new MWException( "No specific information for retrieving archived file" ); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
if ( !$this->title || $this->title->getNamespace() == NS_FILE ) { |
179
|
|
|
$this->dataLoaded = true; // set it here, to have also true on miss |
180
|
|
|
$dbr = wfGetDB( DB_REPLICA ); |
181
|
|
|
$row = $dbr->selectRow( |
182
|
|
|
'filearchive', |
183
|
|
|
self::selectFields(), |
184
|
|
|
$conds, |
185
|
|
|
__METHOD__, |
186
|
|
|
[ 'ORDER BY' => 'fa_timestamp DESC' ] |
187
|
|
|
); |
188
|
|
|
if ( !$row ) { |
189
|
|
|
// this revision does not exist? |
190
|
|
|
return null; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
// initialize fields for filestore image object |
194
|
|
|
$this->loadFromRow( $row ); |
195
|
|
|
} else { |
196
|
|
|
throw new MWException( 'This title does not correspond to an image page.' ); |
197
|
|
|
} |
198
|
|
|
$this->exists = true; |
199
|
|
|
|
200
|
|
|
return true; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* Loads a file object from the filearchive table |
205
|
|
|
* |
206
|
|
|
* @param stdClass $row |
207
|
|
|
* @return ArchivedFile |
208
|
|
|
*/ |
209
|
|
|
public static function newFromRow( $row ) { |
210
|
|
|
$file = new ArchivedFile( Title::makeTitle( NS_FILE, $row->fa_name ) ); |
211
|
|
|
$file->loadFromRow( $row ); |
212
|
|
|
|
213
|
|
|
return $file; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* Fields in the filearchive table |
218
|
|
|
* @return array |
219
|
|
|
*/ |
220
|
|
|
static function selectFields() { |
221
|
|
|
return [ |
222
|
|
|
'fa_id', |
223
|
|
|
'fa_name', |
224
|
|
|
'fa_archive_name', |
225
|
|
|
'fa_storage_key', |
226
|
|
|
'fa_storage_group', |
227
|
|
|
'fa_size', |
228
|
|
|
'fa_bits', |
229
|
|
|
'fa_width', |
230
|
|
|
'fa_height', |
231
|
|
|
'fa_metadata', |
232
|
|
|
'fa_media_type', |
233
|
|
|
'fa_major_mime', |
234
|
|
|
'fa_minor_mime', |
235
|
|
|
'fa_description', |
236
|
|
|
'fa_user', |
237
|
|
|
'fa_user_text', |
238
|
|
|
'fa_timestamp', |
239
|
|
|
'fa_deleted', |
240
|
|
|
'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */ |
241
|
|
|
'fa_sha1', |
242
|
|
|
]; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Load ArchivedFile object fields from a DB row. |
247
|
|
|
* |
248
|
|
|
* @param stdClass $row Object database row |
249
|
|
|
* @since 1.21 |
250
|
|
|
*/ |
251
|
|
|
public function loadFromRow( $row ) { |
252
|
|
|
$this->id = intval( $row->fa_id ); |
253
|
|
|
$this->name = $row->fa_name; |
254
|
|
|
$this->archive_name = $row->fa_archive_name; |
255
|
|
|
$this->group = $row->fa_storage_group; |
256
|
|
|
$this->key = $row->fa_storage_key; |
257
|
|
|
$this->size = $row->fa_size; |
258
|
|
|
$this->bits = $row->fa_bits; |
259
|
|
|
$this->width = $row->fa_width; |
260
|
|
|
$this->height = $row->fa_height; |
261
|
|
|
$this->metadata = $row->fa_metadata; |
262
|
|
|
$this->mime = "$row->fa_major_mime/$row->fa_minor_mime"; |
263
|
|
|
$this->media_type = $row->fa_media_type; |
264
|
|
|
$this->description = $row->fa_description; |
265
|
|
|
$this->user = $row->fa_user; |
266
|
|
|
$this->user_text = $row->fa_user_text; |
267
|
|
|
$this->timestamp = $row->fa_timestamp; |
268
|
|
|
$this->deleted = $row->fa_deleted; |
269
|
|
|
if ( isset( $row->fa_sha1 ) ) { |
270
|
|
|
$this->sha1 = $row->fa_sha1; |
271
|
|
|
} else { |
272
|
|
|
// old row, populate from key |
273
|
|
|
$this->sha1 = LocalRepo::getHashFromKey( $this->key ); |
274
|
|
|
} |
275
|
|
|
if ( !$this->title ) { |
276
|
|
|
$this->title = Title::makeTitleSafe( NS_FILE, $row->fa_name ); |
277
|
|
|
} |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Return the associated title object |
282
|
|
|
* |
283
|
|
|
* @return Title |
284
|
|
|
*/ |
285
|
|
|
public function getTitle() { |
286
|
|
|
if ( !$this->title ) { |
287
|
|
|
$this->load(); |
288
|
|
|
} |
289
|
|
|
return $this->title; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* Return the file name |
294
|
|
|
* |
295
|
|
|
* @return string |
296
|
|
|
*/ |
297
|
|
|
public function getName() { |
298
|
|
|
if ( $this->name === false ) { |
299
|
|
|
$this->load(); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
return $this->name; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* @return int |
307
|
|
|
*/ |
308
|
|
|
public function getID() { |
309
|
|
|
$this->load(); |
310
|
|
|
|
311
|
|
|
return $this->id; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* @return bool |
316
|
|
|
*/ |
317
|
|
|
public function exists() { |
318
|
|
|
$this->load(); |
319
|
|
|
|
320
|
|
|
return $this->exists; |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
/** |
324
|
|
|
* Return the FileStore key |
325
|
|
|
* @return string |
326
|
|
|
*/ |
327
|
|
|
public function getKey() { |
328
|
|
|
$this->load(); |
329
|
|
|
|
330
|
|
|
return $this->key; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
/** |
334
|
|
|
* Return the FileStore key (overriding base File class) |
335
|
|
|
* @return string |
336
|
|
|
*/ |
337
|
|
|
public function getStorageKey() { |
338
|
|
|
return $this->getKey(); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* Return the FileStore storage group |
343
|
|
|
* @return string |
344
|
|
|
*/ |
345
|
|
|
public function getGroup() { |
346
|
|
|
return $this->group; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* Return the width of the image |
351
|
|
|
* @return int |
352
|
|
|
*/ |
353
|
|
|
public function getWidth() { |
354
|
|
|
$this->load(); |
355
|
|
|
|
356
|
|
|
return $this->width; |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
/** |
360
|
|
|
* Return the height of the image |
361
|
|
|
* @return int |
362
|
|
|
*/ |
363
|
|
|
public function getHeight() { |
364
|
|
|
$this->load(); |
365
|
|
|
|
366
|
|
|
return $this->height; |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* Get handler-specific metadata |
371
|
|
|
* @return string |
372
|
|
|
*/ |
373
|
|
|
public function getMetadata() { |
374
|
|
|
$this->load(); |
375
|
|
|
|
376
|
|
|
return $this->metadata; |
377
|
|
|
} |
378
|
|
|
|
379
|
|
|
/** |
380
|
|
|
* Return the size of the image file, in bytes |
381
|
|
|
* @return int |
382
|
|
|
*/ |
383
|
|
|
public function getSize() { |
384
|
|
|
$this->load(); |
385
|
|
|
|
386
|
|
|
return $this->size; |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* Return the bits of the image file, in bytes |
391
|
|
|
* @return int |
392
|
|
|
*/ |
393
|
|
|
public function getBits() { |
394
|
|
|
$this->load(); |
395
|
|
|
|
396
|
|
|
return $this->bits; |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
/** |
400
|
|
|
* Returns the MIME type of the file. |
401
|
|
|
* @return string |
402
|
|
|
*/ |
403
|
|
|
public function getMimeType() { |
404
|
|
|
$this->load(); |
405
|
|
|
|
406
|
|
|
return $this->mime; |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* Get a MediaHandler instance for this file |
411
|
|
|
* @return MediaHandler |
412
|
|
|
*/ |
413
|
|
|
function getHandler() { |
414
|
|
|
if ( !isset( $this->handler ) ) { |
415
|
|
|
$this->handler = MediaHandler::getHandler( $this->getMimeType() ); |
|
|
|
|
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
return $this->handler; |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Returns the number of pages of a multipage document, or false for |
423
|
|
|
* documents which aren't multipage documents |
424
|
|
|
* @return bool|int |
425
|
|
|
*/ |
426
|
|
View Code Duplication |
function pageCount() { |
427
|
|
|
if ( !isset( $this->pageCount ) ) { |
428
|
|
|
// @FIXME: callers expect File objects |
429
|
|
|
if ( $this->getHandler() && $this->handler->isMultiPage( $this ) ) { |
430
|
|
|
$this->pageCount = $this->handler->pageCount( $this ); |
|
|
|
|
431
|
|
|
} else { |
432
|
|
|
$this->pageCount = false; |
|
|
|
|
433
|
|
|
} |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
return $this->pageCount; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
/** |
440
|
|
|
* Return the type of the media in the file. |
441
|
|
|
* Use the value returned by this function with the MEDIATYPE_xxx constants. |
442
|
|
|
* @return string |
443
|
|
|
*/ |
444
|
|
|
public function getMediaType() { |
445
|
|
|
$this->load(); |
446
|
|
|
|
447
|
|
|
return $this->media_type; |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* Return upload timestamp. |
452
|
|
|
* |
453
|
|
|
* @return string |
454
|
|
|
*/ |
455
|
|
|
public function getTimestamp() { |
456
|
|
|
$this->load(); |
457
|
|
|
|
458
|
|
|
return wfTimestamp( TS_MW, $this->timestamp ); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
/** |
462
|
|
|
* Get the SHA-1 base 36 hash of the file |
463
|
|
|
* |
464
|
|
|
* @return string |
465
|
|
|
* @since 1.21 |
466
|
|
|
*/ |
467
|
|
|
function getSha1() { |
468
|
|
|
$this->load(); |
469
|
|
|
|
470
|
|
|
return $this->sha1; |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
/** |
474
|
|
|
* Returns ID or name of user who uploaded the file |
475
|
|
|
* |
476
|
|
|
* @note Prior to MediaWiki 1.23, this method always |
477
|
|
|
* returned the user id, and was inconsistent with |
478
|
|
|
* the rest of the file classes. |
479
|
|
|
* @param string $type 'text' or 'id' |
480
|
|
|
* @return int|string |
481
|
|
|
* @throws MWException |
482
|
|
|
*/ |
483
|
|
|
public function getUser( $type = 'text' ) { |
484
|
|
|
$this->load(); |
485
|
|
|
|
486
|
|
|
if ( $type == 'text' ) { |
487
|
|
|
return $this->user_text; |
488
|
|
|
} elseif ( $type == 'id' ) { |
489
|
|
|
return (int)$this->user; |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
throw new MWException( "Unknown type '$type'." ); |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* Return the user name of the uploader. |
497
|
|
|
* |
498
|
|
|
* @deprecated since 1.23 Use getUser( 'text' ) instead. |
499
|
|
|
* @return string |
500
|
|
|
*/ |
501
|
|
|
public function getUserText() { |
502
|
|
|
wfDeprecated( __METHOD__, '1.23' ); |
503
|
|
|
$this->load(); |
504
|
|
|
if ( $this->isDeleted( File::DELETED_USER ) ) { |
505
|
|
|
return 0; |
506
|
|
|
} else { |
507
|
|
|
return $this->user_text; |
508
|
|
|
} |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
/** |
512
|
|
|
* Return upload description. |
513
|
|
|
* |
514
|
|
|
* @return string |
515
|
|
|
*/ |
516
|
|
|
public function getDescription() { |
517
|
|
|
$this->load(); |
518
|
|
|
if ( $this->isDeleted( File::DELETED_COMMENT ) ) { |
519
|
|
|
return 0; |
520
|
|
|
} else { |
521
|
|
|
return $this->description; |
522
|
|
|
} |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
/** |
526
|
|
|
* Return the user ID of the uploader. |
527
|
|
|
* |
528
|
|
|
* @return int |
529
|
|
|
*/ |
530
|
|
|
public function getRawUser() { |
531
|
|
|
$this->load(); |
532
|
|
|
|
533
|
|
|
return $this->user; |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
/** |
537
|
|
|
* Return the user name of the uploader. |
538
|
|
|
* |
539
|
|
|
* @return string |
540
|
|
|
*/ |
541
|
|
|
public function getRawUserText() { |
542
|
|
|
$this->load(); |
543
|
|
|
|
544
|
|
|
return $this->user_text; |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
/** |
548
|
|
|
* Return upload description. |
549
|
|
|
* |
550
|
|
|
* @return string |
551
|
|
|
*/ |
552
|
|
|
public function getRawDescription() { |
553
|
|
|
$this->load(); |
554
|
|
|
|
555
|
|
|
return $this->description; |
556
|
|
|
} |
557
|
|
|
|
558
|
|
|
/** |
559
|
|
|
* Returns the deletion bitfield |
560
|
|
|
* @return int |
561
|
|
|
*/ |
562
|
|
|
public function getVisibility() { |
563
|
|
|
$this->load(); |
564
|
|
|
|
565
|
|
|
return $this->deleted; |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
/** |
569
|
|
|
* for file or revision rows |
570
|
|
|
* |
571
|
|
|
* @param int $field One of DELETED_* bitfield constants |
572
|
|
|
* @return bool |
573
|
|
|
*/ |
574
|
|
|
public function isDeleted( $field ) { |
575
|
|
|
$this->load(); |
576
|
|
|
|
577
|
|
|
return ( $this->deleted & $field ) == $field; |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Determine if the current user is allowed to view a particular |
582
|
|
|
* field of this FileStore image file, if it's marked as deleted. |
583
|
|
|
* @param int $field |
584
|
|
|
* @param null|User $user User object to check, or null to use $wgUser |
585
|
|
|
* @return bool |
586
|
|
|
*/ |
587
|
|
|
public function userCan( $field, User $user = null ) { |
588
|
|
|
$this->load(); |
589
|
|
|
|
590
|
|
|
$title = $this->getTitle(); |
591
|
|
|
return Revision::userCanBitfield( $this->deleted, $field, $user, $title ?: null ); |
592
|
|
|
} |
593
|
|
|
} |
594
|
|
|
|
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..