Completed
Push — master ( 9b8b24...6e1754 )
by Julito
58:58
created

MoodleImport   F

Complexity

Total Complexity 157

Size/Duplication

Total Lines 1069
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1069
rs 0.8273
c 0
b 0
f 0
wmc 157

17 Methods

Rating   Name   Duplication   Size   Complexity  
B readForumModule() 0 19 5
B matchMoodleChamiloQuestionTypes() 0 17 7
C getAllQuestionFiles() 0 46 15
A getQuestionTypeOptionsTag() 0 10 3
A fixPathInText() 0 13 3
C readMainFilesXml() 0 49 20
C processFillBlanks() 0 88 12
C readQuizModule() 0 33 8
F import() 0 309 32
D processAnswers() 0 151 13
C readMainQuestionsXml() 0 53 14
A getQuestionTypeAnswersTag() 0 10 3
B processTrueFalse() 0 28 4
A traverseArray() 0 8 4
B readResourceModule() 0 23 5
B processUniqueAnswer() 0 28 4
B readUrlModule() 0 19 5

How to fix   Complexity   

Complex Class

Complex classes like MoodleImport often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MoodleImport, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
 * Class MoodleImport
6
 *
7
 * @author José Loguercio <[email protected]>,
8
 * @author Julio Montoya <[email protected]>
9
 *
10
 * @package chamilo.library
11
 */
12
13
class MoodleImport
14
{
15
    /**
16
     * Import moodle file
17
     *
18
     * @param resource $uploadedFile *.* mbz file moodle course backup
19
     * @return bool
20
     */
21
    public function import($uploadedFile)
22
    {
23
        $file = $uploadedFile['tmp_name'];
24
25
        if (is_file($file) && is_readable($file)) {
26
            $package = new PclZip($file);
27
            $mainFileKey = 0;
28
            $packageContent = $package->listContent();
29
            if (!empty($packageContent)) {
30
                foreach ($packageContent as $index => $value) {
31
                    if ($value['filename'] == 'moodle_backup.xml') {
32
                        $mainFileKey = $index;
33
                        break;
34
                    }
35
                }
36
            }
37
38
            if (!$mainFileKey) {
39
                Display::addFlash(
40
                    Display::return_message(
41
                        get_lang('FailedToImportThisIsNotAMoodleFile'),
42
                        'error'
43
                    )
44
                );
45
            }
46
47
            $folder = api_get_unique_id();
48
            $destinationDir = api_get_path(SYS_ARCHIVE_PATH).$folder;
49
            $coursePath = api_get_course_path();
50
            $sessionId = api_get_session_id();
51
            $groupId = api_get_group_id();
52
            $documentPath = api_get_path(SYS_COURSE_PATH).$coursePath.'/document';
53
            $courseInfo = api_get_course_info();
54
55
            mkdir($destinationDir, api_get_permissions_for_new_directories(), true);
56
57
            create_unexisting_directory(
58
                $courseInfo,
59
                api_get_user_id(),
60
                $sessionId,
61
                $groupId,
62
                null,
63
                $documentPath,
64
                '/moodle',
65
                'Moodle Docs',
66
                0
67
            );
68
69
            $package->extract(
70
                PCLZIP_OPT_PATH,
71
                $destinationDir
72
            );
73
74
            // This process will upload all question resource files
75
            $filesXml = @file_get_contents($destinationDir.'/files.xml');
76
            $mainFileModuleValues = $this->getAllQuestionFiles($filesXml);
77
            $currentResourceFilePath = $destinationDir.'/files/';
78
79
            $importedFiles = [];
80
81
            foreach ($mainFileModuleValues as $fileInfo) {
82
                $dirs = new RecursiveDirectoryIterator($currentResourceFilePath);
0 ignored issues
show
Bug introduced by
The call to RecursiveDirectoryIterator::__construct() has too few arguments starting with flags. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

82
                $dirs = /** @scrutinizer ignore-call */ new RecursiveDirectoryIterator($currentResourceFilePath);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
83
                foreach (new RecursiveIteratorIterator($dirs) as $file) {
84
                    if (is_file($file) && strpos($file, $fileInfo['contenthash']) !== false) {
85
                        $files = [];
86
                        $files['file']['name'] = $fileInfo['filename'];
87
                        $files['file']['tmp_name'] = $file->getPathname();
88
                        $files['file']['type'] = $fileInfo['mimetype'];
89
                        $files['file']['error'] = 0;
90
                        $files['file']['size'] = $fileInfo['filesize'];
91
                        $files['file']['from_file'] = true;
92
                        $files['file']['move_file'] = true;
93
                        $_POST['language'] = $courseInfo['language'];
94
                        $_POST['moodle_import'] = true;
95
96
                        $data = DocumentManager::upload_document(
97
                            $files,
98
                            '/moodle',
99
                            isset($fileInfo['title']) ? $fileInfo['title'] : pathinfo($fileInfo['filename'], PATHINFO_FILENAME),
100
                            '',
101
                            null,
102
                            null,
103
                            true,
104
                            true,
105
                            'file',
106
                            // This is to validate spaces as hyphens
107
                            false
108
                        );
109
110
                        if ($data) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
111
                            $importedFiles[$fileInfo['filename']] = basename($data['path']);
112
                        }
113
                    }
114
                }
115
            }
116
117
            $xml = @file_get_contents($destinationDir.'/moodle_backup.xml');
118
119
            $doc = new DOMDocument();
0 ignored issues
show
Bug introduced by
The call to DOMDocument::__construct() has too few arguments starting with version. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
            $doc = /** @scrutinizer ignore-call */ new DOMDocument();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
120
            $res = @$doc->loadXML($xml);
121
            if ($res) {
122
                $activities = $doc->getElementsByTagName('activity');
123
                foreach ($activities as $activity) {
124
                    if ($activity->childNodes->length) {
125
                        $currentItem = [];
126
127
                        foreach ($activity->childNodes as $item) {
128
                            $currentItem[$item->nodeName] = $item->nodeValue;
129
                        }
130
131
                        $moduleName = isset($currentItem['modulename']) ? $currentItem['modulename'] : false;
132
                        switch ($moduleName) {
133
                            case 'forum':
134
                                require_once '../forum/forumfunction.inc.php';
135
                                $catForumValues = [];
136
137
                                // Read the current forum module xml.
138
                                $moduleDir = $currentItem['directory'];
139
                                $moduleXml = @file_get_contents($destinationDir.'/'.$moduleDir.'/'.$moduleName.'.xml');
0 ignored issues
show
Bug introduced by
Are you sure $moduleName of type mixed|false can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

139
                                $moduleXml = @file_get_contents($destinationDir.'/'.$moduleDir.'/'./** @scrutinizer ignore-type */ $moduleName.'.xml');
Loading history...
140
                                $moduleValues = $this->readForumModule($moduleXml);
141
142
                                // Create a Forum category based on Moodle forum type.
143
                                $catForumValues['forum_category_title'] = $moduleValues['type'];
144
                                $catForumValues['forum_category_comment'] = '';
145
                                $catId = store_forumcategory(
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $catId is correct as store_forumcategory($cat...es, $courseInfo, false) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
146
                                    $catForumValues,
147
                                    $courseInfo,
148
                                    false
149
                                );
150
                                $forumValues = [];
151
                                $forumValues['forum_title'] = $moduleValues['name'];
152
                                $forumValues['forum_image'] = '';
153
                                $forumValues['forum_comment'] = $moduleValues['intro'];
154
                                $forumValues['forum_category'] = $catId;
155
                                $forumValues['moderated'] = 0;
156
157
                                store_forum($forumValues, $courseInfo);
158
                                break;
159
                            case 'quiz':
160
                                // Read the current quiz module xml.
161
                                // The quiz case is the very complicate process of all the import.
162
                                // Please if you want to review the script, try to see the readingXML functions.
163
                                // The readingXML functions in this clases do all the mayor work here.
164
165
                                $moduleDir = $currentItem['directory'];
166
                                $moduleXml = @file_get_contents($destinationDir.'/'.$moduleDir.'/'.$moduleName.'.xml');
167
                                $questionsXml = @file_get_contents($destinationDir.'/questions.xml');
168
                                $moduleValues = $this->readQuizModule($moduleXml);
169
170
                                // At this point we got all the prepared resources from Moodle file
171
                                // $moduleValues variable contains all the necesary info to the quiz import
172
                                // var_dump($moduleValues); // <-- uncomment this to see the final array
173
174
                                $exercise = new Exercise($courseInfo['real_id']);
175
                                $title = Exercise::format_title_variable($moduleValues['name']);
176
                                $exercise->updateTitle($title);
177
                                $exercise->updateDescription($moduleValues['intro']);
178
                                $exercise->updateAttempts($moduleValues['attempts_number']);
179
                                $exercise->updateFeedbackType(0);
180
181
                                // Match shuffle question with chamilo
182
                                switch ($moduleValues['shufflequestions']) {
183
                                    case '0':
184
                                        $exercise->setRandom(0);
185
                                        break;
186
                                    case '1':
187
                                        $exercise->setRandom(-1);
188
                                        break;
189
                                    default:
190
                                        $exercise->setRandom(0);
191
                                }
192
                                $exercise->updateRandomAnswers($moduleValues['shuffleanswers']);
193
                                // @todo divide to minutes
194
                                $exercise->updateExpiredTime($moduleValues['timelimit']);
195
196
                                if ($moduleValues['questionsperpage'] == 1) {
197
                                    $exercise->updateType(2);
198
                                } else {
199
                                    $exercise->updateType(1);
200
                                }
201
202
                                // Create the new Quiz
203
                                $exercise->save();
204
205
                                // Ok, we got the Quiz and create it, now its time to add the Questions
206
                                foreach ($moduleValues['question_instances'] as $index => $question) {
207
                                    $questionsValues = $this->readMainQuestionsXml(
208
                                        $questionsXml,
209
                                        $question['questionid']
210
                                    );
211
                                    $moduleValues['question_instances'][$index] = $questionsValues;
212
                                    // Set Question Type from Moodle XML element <qtype>
213
                                    $qType = $moduleValues['question_instances'][$index]['qtype'];
214
                                    // Add the matched chamilo question type to the array
215
                                    $moduleValues['question_instances'][$index]['chamilo_qtype'] = $this->matchMoodleChamiloQuestionTypes($qType);
216
                                    $questionInstance = Question::getInstance(
217
                                        $moduleValues['question_instances'][$index]['chamilo_qtype']
218
                                    );
219
                                    if ($questionInstance) {
220
                                        $questionInstance->updateTitle(
221
                                            $moduleValues['question_instances'][$index]['name']
222
                                        );
223
                                        $questionText = $moduleValues['question_instances'][$index]['questiontext'];
224
225
                                        // Replace the path from @@PLUGINFILE@@ to a correct chamilo path
226
                                        $questionText = str_replace(
227
                                            '@@PLUGINFILE@@',
228
                                            '/courses/'.$coursePath.'/document/moodle',
229
                                            $questionText
230
                                        );
231
232
                                        if ($importedFiles) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $importedFiles of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
233
                                            $this->fixPathInText($importedFiles, $questionText);
234
                                        }
235
236
                                        $questionInstance->updateDescription($questionText);
237
                                        $questionInstance->updateLevel(1);
238
                                        $questionInstance->updateCategory(0);
239
240
                                        //Save normal question if NOT media
241
                                        if ($questionInstance->type != MEDIA_QUESTION) {
242
                                            $questionInstance->save($exercise);
243
244
                                            // modify the exercise
245
                                            $exercise->addToList($questionInstance->id);
246
                                            $exercise->update_question_positions();
247
                                        }
248
249
                                        $questionList = $moduleValues['question_instances'][$index]['plugin_qtype_'.$qType.'_question'];
250
                                        $currentQuestion = $moduleValues['question_instances'][$index];
251
252
                                        $this->processAnswers(
253
                                            $exercise,
254
                                            $questionList,
255
                                            $qType,
256
                                            $questionInstance,
257
                                            $currentQuestion,
258
                                            $importedFiles
259
                                        );
260
                                    }
261
                                }
262
                                break;
263
                            case 'resource':
264
                                // Read the current resource module xml.
265
                                $moduleDir = $currentItem['directory'];
266
                                $moduleXml = @file_get_contents($destinationDir.'/'.$moduleDir.'/'.$moduleName.'.xml');
267
                                $filesXml = @file_get_contents($destinationDir.'/files.xml');
268
                                $moduleValues = $this->readResourceModule($moduleXml);
269
                                $mainFileModuleValues = $this->readMainFilesXml(
270
                                    $filesXml,
271
                                    $moduleValues['contextid']
272
                                );
273
                                $fileInfo = array_merge($moduleValues, $mainFileModuleValues, $currentItem);
0 ignored issues
show
Bug introduced by
It seems like $moduleValues can also be of type false; however, parameter $array1 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 ignore-type  annotation

273
                                $fileInfo = array_merge(/** @scrutinizer ignore-type */ $moduleValues, $mainFileModuleValues, $currentItem);
Loading history...
Bug introduced by
It seems like $mainFileModuleValues can also be of type false; however, parameter $array2 of array_merge() does only seem to accept null|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 ignore-type  annotation

273
                                $fileInfo = array_merge($moduleValues, /** @scrutinizer ignore-type */ $mainFileModuleValues, $currentItem);
Loading history...
274
                                $currentResourceFilePath = $destinationDir.'/files/';
275
                                $dirs = new RecursiveDirectoryIterator($currentResourceFilePath);
276
                                foreach (new RecursiveIteratorIterator($dirs) as $file) {
277
                                    if (is_file($file) && strpos($file, $fileInfo['contenthash']) !== false) {
278
                                        $files = [];
279
                                        $files['file']['name'] = $fileInfo['filename'];
280
                                        $files['file']['tmp_name'] = $file->getPathname();
281
                                        $files['file']['type'] = $fileInfo['mimetype'];
282
                                        $files['file']['error'] = 0;
283
                                        $files['file']['size'] = $fileInfo['filesize'];
284
                                        $files['file']['from_file'] = true;
285
                                        $files['file']['move_file'] = true;
286
                                        $_POST['language'] = $courseInfo['language'];
287
                                        $_POST['moodle_import'] = true;
288
289
                                        DocumentManager::upload_document(
290
                                            $files,
291
                                            '/moodle',
292
                                            $fileInfo['title'],
293
                                            '',
294
                                            null,
295
                                            null,
296
                                            true,
297
                                            true
298
                                        );
299
                                    }
300
                                }
301
302
                                break;
303
                            case 'url':
304
                                // Read the current url module xml.
305
                                $moduleDir = $currentItem['directory'];
306
                                $moduleXml = @file_get_contents($destinationDir.'/'.$moduleDir.'/'.$moduleName.'.xml');
307
                                $moduleValues = $this->readUrlModule($moduleXml);
308
                                $_POST['title'] = $moduleValues['name'];
309
                                $_POST['url'] = $moduleValues['externalurl'];
310
                                $_POST['description'] = $moduleValues['intro'];
311
                                $_POST['category_id'] = 0;
312
                                $_POST['target'] = '_blank';
313
314
                                Link::addlinkcategory("link");
315
                                break;
316
                        }
317
                    }
318
                }
319
            } else {
320
                removeDir($destinationDir);
321
                return false;
322
            }
323
        } else {
324
            return false;
325
        }
326
327
        removeDir($destinationDir);
328
329
        return $packageContent[$mainFileKey];
330
    }
331
332
    /**
333
     * Read and validate the forum module XML
334
     *
335
     * @param resource $moduleXml XML file
336
     * @return mixed | array if is a valid xml file, false otherwise
337
     */
338
    public function readForumModule($moduleXml)
339
    {
340
        $moduleDoc = new DOMDocument();
0 ignored issues
show
Bug introduced by
The call to DOMDocument::__construct() has too few arguments starting with version. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

340
        $moduleDoc = /** @scrutinizer ignore-call */ new DOMDocument();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
341
        $moduleRes = @$moduleDoc->loadXML($moduleXml);
342
        if ($moduleRes) {
343
            $activities = $moduleDoc->getElementsByTagName('forum');
344
            $currentItem = [];
345
            foreach ($activities as $activity) {
346
                if ($activity->childNodes->length) {
347
                    foreach ($activity->childNodes as $item) {
348
                        $currentItem[$item->nodeName] = $item->nodeValue;
349
                    }
350
                }
351
            }
352
353
            return $currentItem;
354
        }
355
356
        return false;
357
    }
358
359
    /**
360
     * Read and validate the resource module XML
361
     *
362
     * @param resource $moduleXml XML file
363
     * @return mixed | array if is a valid xml file, false otherwise
364
     */
365
    public function readResourceModule($moduleXml)
366
    {
367
        $moduleDoc = new DOMDocument();
0 ignored issues
show
Bug introduced by
The call to DOMDocument::__construct() has too few arguments starting with version. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

367
        $moduleDoc = /** @scrutinizer ignore-call */ new DOMDocument();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
368
        $moduleRes = @$moduleDoc->loadXML($moduleXml);
369
        if ($moduleRes) {
370
            $activities = $moduleDoc->getElementsByTagName('resource');
371
            $mainActivity = $moduleDoc->getElementsByTagName('activity');
372
            $contextId = $mainActivity->item(0)->getAttribute('contextid');
373
            $currentItem = [];
374
            foreach ($activities as $activity) {
375
                if ($activity->childNodes->length) {
376
                    foreach ($activity->childNodes as $item) {
377
                        $currentItem[$item->nodeName] = $item->nodeValue;
378
                    }
379
                }
380
            }
381
382
            $currentItem['contextid'] = $contextId;
383
384
            return $currentItem;
385
        }
386
387
        return false;
388
    }
389
390
    /**
391
     * Read and validate the url module XML
392
     *
393
     * @param resource $moduleXml XML file
394
     * @return mixed | array if is a valid xml file, false otherwise
395
     */
396
    public function readUrlModule($moduleXml)
397
    {
398
        $moduleDoc = new DOMDocument();
0 ignored issues
show
Bug introduced by
The call to DOMDocument::__construct() has too few arguments starting with version. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

398
        $moduleDoc = /** @scrutinizer ignore-call */ new DOMDocument();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
399
        $moduleRes = @$moduleDoc->loadXML($moduleXml);
400
        if ($moduleRes) {
401
            $activities = $moduleDoc->getElementsByTagName('url');
402
            $currentItem = [];
403
            foreach ($activities as $activity) {
404
                if ($activity->childNodes->length) {
405
                    foreach ($activity->childNodes as $item) {
406
                        $currentItem[$item->nodeName] = $item->nodeValue;
407
                    }
408
                }
409
            }
410
411
            return $currentItem;
412
        }
413
414
        return false;
415
    }
416
417
    /**
418
     * Read and validate the quiz module XML
419
     *
420
     * @param resource $moduleXml XML file
421
     * @return mixed | array if is a valid xml file, false otherwise
422
     */
423
    public function readQuizModule($moduleXml)
424
    {
425
        $moduleDoc = new DOMDocument();
0 ignored issues
show
Bug introduced by
The call to DOMDocument::__construct() has too few arguments starting with version. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

425
        $moduleDoc = /** @scrutinizer ignore-call */ new DOMDocument();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
426
        $moduleRes = @$moduleDoc->loadXML($moduleXml);
427
        if ($moduleRes) {
428
            $activities = $moduleDoc->getElementsByTagName('quiz');
429
            $currentItem = [];
430
            foreach ($activities as $activity) {
431
                if ($activity->childNodes->length) {
432
                    foreach ($activity->childNodes as $item) {
433
                        $currentItem[$item->nodeName] = $item->nodeValue;
434
                    }
435
                }
436
            }
437
438
            $questions = $moduleDoc->getElementsByTagName('question_instance');
439
440
            $questionList = [];
441
            $counter = 0;
442
            foreach ($questions as $question) {
443
                if ($question->childNodes->length) {
444
                    foreach ($question->childNodes as $item) {
445
                        $questionList[$counter][$item->nodeName] = $item->nodeValue;
446
                    }
447
                    $counter++;
448
                }
449
450
            }
451
            $currentItem['question_instances'] = $questionList;
452
            return $currentItem;
453
        }
454
455
        return false;
456
    }
457
458
    /**
459
     * Search the current file resource in main Files XML
460
     *
461
     * @param resource $filesXml XML file
462
     * @param int $contextId
463
     * @return mixed | array if is a valid xml file, false otherwise
464
     */
465
    public function readMainFilesXml($filesXml, $contextId)
466
    {
467
        $moduleDoc = new DOMDocument();
0 ignored issues
show
Bug introduced by
The call to DOMDocument::__construct() has too few arguments starting with version. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

467
        $moduleDoc = /** @scrutinizer ignore-call */ new DOMDocument();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
468
        $moduleRes = @$moduleDoc->loadXML($filesXml);
469
        if ($moduleRes) {
470
            $activities = $moduleDoc->getElementsByTagName('file');
471
            $currentItem = [];
472
            foreach ($activities as $activity) {
473
                if ($activity->childNodes->length) {
474
                    $isThisItemThatIWant = false;
475
                    foreach ($activity->childNodes as $item) {
476
                        if (!$isThisItemThatIWant && $item->nodeName == 'contenthash') {
477
                            $currentItem['contenthash'] = $item->nodeValue;
478
                        }
479
                        if ($item->nodeName == 'contextid' &&
480
                            intval($item->nodeValue) == intval($contextId) && !$isThisItemThatIWant
481
                        ) {
482
                            $isThisItemThatIWant = true;
483
                            continue;
484
                        }
485
486
                        if ($isThisItemThatIWant && $item->nodeName == 'filename') {
487
                            $currentItem['filename'] = $item->nodeValue;
488
                        }
489
490
                        if ($isThisItemThatIWant && $item->nodeName == 'filesize') {
491
                            $currentItem['filesize'] = $item->nodeValue;
492
                        }
493
494
                        if ($isThisItemThatIWant && $item->nodeName == 'mimetype' &&
495
                            $item->nodeValue == 'document/unknown'
496
                        ) {
497
                            break;
498
                        }
499
500
                        if ($isThisItemThatIWant && $item->nodeName == 'mimetype' &&
501
                            $item->nodeValue !== 'document/unknown'
502
                        ) {
503
                            $currentItem['mimetype'] = $item->nodeValue;
504
                            break 2;
505
                        }
506
                    }
507
                }
508
            }
509
510
            return $currentItem;
511
        }
512
513
        return false;
514
    }
515
516
    /**
517
     * Search the current question resource in main Questions XML
518
     *
519
     * @param resource $questionsXml XML file
520
     * @param int $questionId
521
     * @return mixed | array if is a valid xml file, false otherwise
522
     */
523
    public function readMainQuestionsXml($questionsXml, $questionId)
524
    {
525
        $moduleDoc = new DOMDocument();
0 ignored issues
show
Bug introduced by
The call to DOMDocument::__construct() has too few arguments starting with version. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

525
        $moduleDoc = /** @scrutinizer ignore-call */ new DOMDocument();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
526
        $moduleRes = @$moduleDoc->loadXML($questionsXml);
527
        if ($moduleRes) {
528
            $questions = $moduleDoc->getElementsByTagName('question');
529
            $currentItem = [];
530
            foreach ($questions as $question) {
531
                if (intval($question->getAttribute('id')) == $questionId) {
532
                    if ($question->childNodes->length) {
533
                        $currentItem['questionid'] = $questionId;
534
                        $questionType = '';
535
                        foreach ($question->childNodes as $item) {
536
                            $currentItem[$item->nodeName] = $item->nodeValue;
537
                            if ($item->nodeName == 'qtype') {
538
                                $questionType = $item->nodeValue;
539
                            }
540
541
                            if ($item->nodeName == 'plugin_qtype_'.$questionType.'_question') {
542
                                $answer = $item->getElementsByTagName($this->getQuestionTypeAnswersTag($questionType));
543
                                $currentItem['plugin_qtype_'.$questionType.'_question'] = [];
544
                                for ($i = 0; $i <= $answer->length - 1; $i++) {
545
                                    $currentItem['plugin_qtype_'.$questionType.'_question'][$i]['answerid'] = $answer->item($i)->getAttribute('id');
546
                                    foreach ($answer->item($i)->childNodes as $properties) {
547
                                        $currentItem['plugin_qtype_'.$questionType.'_question'][$i][$properties->nodeName] = $properties->nodeValue;
548
                                    }
549
                                }
550
551
                                $typeValues = $item->getElementsByTagName($this->getQuestionTypeOptionsTag($questionType));
552
                                for ($i = 0; $i <= $typeValues->length - 1; $i++) {
553
                                    foreach ($typeValues->item($i)->childNodes as $properties) {
554
                                        $currentItem[$questionType.'_values'][$properties->nodeName] = $properties->nodeValue;
555
                                        if ($properties->nodeName == 'sequence') {
556
                                            $sequence = $properties->nodeValue;
557
                                            $sequenceIds = explode(',', $sequence);
558
                                            foreach ($sequenceIds as $qId) {
559
                                                $questionMatch = $this->readMainQuestionsXml($questionsXml, $qId);
560
                                                $currentItem['plugin_qtype_'.$questionType.'_question'][] = $questionMatch;
561
                                            }
562
                                        }
563
                                    }
564
                                }
565
                            }
566
                        }
567
                    }
568
                }
569
            }
570
571
            $this->traverseArray($currentItem, ['#text', 'question_hints', 'tags']);
572
            return $currentItem;
573
        }
574
575
        return false;
576
    }
577
578
    /**
579
     * return the correct question type options tag
580
     *
581
     * @param string $questionType name
582
     * @return string question type tag
583
     */
584
    public function getQuestionTypeOptionsTag($questionType)
585
    {
586
        switch ($questionType) {
587
            case 'match':
588
            case 'ddmatch':
589
                return 'matchoptions';
590
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
591
            default:
592
                return $questionType;
593
                break;
594
        }
595
    }
596
597
    /**
598
     * return the correct question type answers tag
599
     *
600
     * @param string $questionType name
601
     * @return string question type tag
602
     */
603
    public function getQuestionTypeAnswersTag($questionType)
604
    {
605
        switch ($questionType) {
606
            case 'match':
607
            case 'ddmatch':
608
                return 'match';
609
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
610
            default:
611
                return 'answer';
612
                break;
613
        }
614
    }
615
616
    /**
617
     *
618
     * @param string $moodleQuestionType
619
     * @return integer Chamilo question type
620
     */
621
    public function matchMoodleChamiloQuestionTypes($moodleQuestionType)
622
    {
623
        switch ($moodleQuestionType) {
624
            case 'multichoice':
625
                return UNIQUE_ANSWER;
626
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
627
            case 'multianswer':
628
            case 'shortanswer':
629
            case 'match':
630
                return FILL_IN_BLANKS;
631
                break;
632
            case 'essay':
633
                return FREE_ANSWER;
634
                break;
635
            case 'truefalse':
636
                return UNIQUE_ANSWER_NO_OPTION;
637
                break;
638
        }
639
    }
640
641
    /**
642
     * Fix moodle files that contains spaces
643
     * @param array $importedFiles
644
     * @param string $text
645
     * @return mixed
646
     */
647
    public function fixPathInText($importedFiles, &$text)
648
    {
649
        if ($importedFiles) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $importedFiles of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
650
            foreach ($importedFiles as $old => $new) {
651
                // Ofaj fix moodle file names
652
                // In some questions moodle text contains file with name like:
653
                // Bild%20Check-In-Formular%20Ausfu%CC%88llen.jpg"
654
                // rawurlencode function transforms '' (whitespace) to %20 and so on
655
                $text = str_replace(rawurlencode($old), $new, $text);
656
            }
657
        }
658
659
        return $text;
660
    }
661
662
    /**
663
     * Process Moodle Answers to Chamilo
664
     *
665
     * @param Exercise $exercise
666
     * @param array $questionList
667
     * @param string $questionType
668
     * @param Question $questionInstance Question/Answer instance
669
     * @param array $currentQuestion
670
     * @param array $importedFiles
671
     * @return integer db response
672
     */
673
    public function processAnswers(
674
        $exercise,
675
        $questionList,
676
        $questionType,
677
        $questionInstance,
678
        $currentQuestion,
679
        $importedFiles
680
    ) {
681
        switch ($questionType) {
682
            case 'multichoice':
683
                $objAnswer = new Answer($questionInstance->id);
684
                $questionWeighting = 0;
685
                foreach ($questionList as $slot => $answer) {
686
                    $this->processUniqueAnswer(
687
                        $objAnswer,
688
                        $answer,
689
                        $slot + 1,
690
                        $questionWeighting,
691
                        $importedFiles
692
                    );
693
                }
694
695
                // saves the answers into the data base
696
                $objAnswer->save();
697
                // sets the total weighting of the question
698
                $questionInstance->updateWeighting($questionWeighting);
699
                $questionInstance->save($exercise);
700
701
                return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type integer.
Loading history...
702
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
703
            case 'multianswer':
704
                $objAnswer = new Answer($questionInstance->id);
705
                $coursePath = api_get_course_path();
706
                $placeholder = str_replace(
707
                    '@@PLUGINFILE@@',
708
                    '/courses/'.$coursePath.'/document/moodle',
709
                    $currentQuestion['questiontext']
710
                );
711
                $optionsValues = [];
712
                foreach ($questionList as $slot => $subQuestion) {
713
                    $qtype = $subQuestion['qtype'];
714
                    $optionsValues[] = $this->processFillBlanks(
715
                        $objAnswer,
716
                        $qtype,
717
                        $subQuestion['plugin_qtype_'.$qtype.'_question'],
718
                        $placeholder,
719
                        $slot + 1,
720
                        $importedFiles
721
                    );
722
                }
723
724
                $answerOptionsWeight = '::';
725
                $answerOptionsSize = '';
726
                $questionWeighting = 0;
727
                foreach ($optionsValues as $index => $value) {
728
                    $questionWeighting += $value['weight'];
729
                    $answerOptionsWeight .= $value['weight'].',';
730
                    $answerOptionsSize .= $value['size'].',';
731
                }
732
733
                $answerOptionsWeight = substr($answerOptionsWeight, 0, -1);
734
                $answerOptionsSize = substr($answerOptionsSize, 0, -1);
735
                $answerOptions = $answerOptionsWeight.':'.$answerOptionsSize.':0@';
736
                $placeholder = $placeholder.PHP_EOL.$answerOptions;
737
738
                // This is a minor trick to clean the question description that in a multianswer is the main placeholder
739
                $questionInstance->updateDescription('');
740
                // sets the total weighting of the question
741
                $questionInstance->updateWeighting($questionWeighting);
742
                $questionInstance->save($exercise);
743
                $this->fixPathInText($importedFiles, $placeholder);
744
745
                // saves the answers into the data base
746
                $objAnswer->createAnswer($placeholder, 0, '', 0, 1);
747
                $objAnswer->save();
748
749
                return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type integer.
Loading history...
750
            case 'match':
751
                $objAnswer = new Answer($questionInstance->id);
752
                $placeholder = '';
753
754
                $optionsValues = $this->processFillBlanks(
755
                    $objAnswer,
756
                    'match',
757
                    $questionList,
758
                    $placeholder,
759
                    0,
760
                    $importedFiles
761
                );
762
763
                $answerOptionsWeight = '::';
764
                $answerOptionsSize = '';
765
                $questionWeighting = 0;
766
                foreach ($optionsValues as $index => $value) {
0 ignored issues
show
Bug introduced by
The expression $optionsValues of type integer is not traversable.
Loading history...
767
                    $questionWeighting += $value['weight'];
768
                    $answerOptionsWeight .= $value['weight'].',';
769
                    $answerOptionsSize .= $value['size'].',';
770
                }
771
772
                $answerOptionsWeight = substr($answerOptionsWeight, 0, -1);
773
                $answerOptionsSize = substr($answerOptionsSize, 0, -1);
774
                $answerOptions = $answerOptionsWeight.':'.$answerOptionsSize.':0@';
775
                $placeholder = $placeholder.PHP_EOL.$answerOptions;
776
777
                // sets the total weighting of the question
778
                $questionInstance->updateWeighting($questionWeighting);
779
                $questionInstance->save($exercise);
780
                // saves the answers into the database
781
                $this->fixPathInText($importedFiles, $placeholder);
782
                $objAnswer->createAnswer($placeholder, 0, '', 0, 1);
783
                $objAnswer->save();
784
785
                return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type integer.
Loading history...
786
                break;
787
            case 'shortanswer':
788
            case 'ddmatch':
789
                $questionWeighting = $currentQuestion['defaultmark'];
790
                $questionInstance->updateWeighting($questionWeighting);
791
                $questionInstance->updateDescription(get_lang('ThisQuestionIsNotSupportedYet'));
792
                $questionInstance->save($exercise);
793
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
794
                break;
795
            case 'essay':
796
                $questionWeighting = $currentQuestion['defaultmark'];
797
                $questionInstance->updateWeighting($questionWeighting);
798
                $questionInstance->save($exercise);
799
                return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type integer.
Loading history...
800
                break;
801
            case 'truefalse':
802
                $objAnswer = new Answer($questionInstance->id);
803
                $questionWeighting = 0;
804
                foreach ($questionList as $slot => $answer) {
805
                    $this->processTrueFalse(
806
                        $objAnswer,
807
                        $answer,
808
                        $slot + 1,
809
                        $questionWeighting,
810
                        $importedFiles
811
                    );
812
                }
813
814
                // saves the answers into the data base
815
                $objAnswer->save();
816
                // sets the total weighting of the question
817
                $questionInstance->updateWeighting($questionWeighting);
818
                $questionInstance->save($exercise);
819
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
820
                break;
821
            default:
822
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
823
                break;
824
        }
825
    }
826
827
    /**
828
     * Process Chamilo Unique Answer
829
     *
830
     * @param object $objAnswer
831
     * @param array $answerValues
832
     * @param integer $position
833
     * @param integer $questionWeighting
834
     * @param array $importedFiles
835
     * @return integer db response
836
     */
837
    public function processUniqueAnswer(
838
        $objAnswer,
839
        $answerValues,
840
        $position,
841
        &$questionWeighting,
842
        $importedFiles
843
    ) {
844
        $correct = intval($answerValues['fraction']) ? intval($answerValues['fraction']) : 0;
845
        $answer = $answerValues['answertext'];
846
        $comment = $answerValues['feedback'];
847
        $weighting = $answerValues['fraction'];
848
        $weighting = abs($weighting);
849
        if ($weighting > 0) {
850
            $questionWeighting += $weighting;
851
        }
852
        $goodAnswer = $correct ? true : false;
853
854
        $this->fixPathInText($importedFiles, $answer);
855
856
        $objAnswer->createAnswer(
857
            $answer,
858
            $goodAnswer,
859
            $comment,
860
            $weighting,
861
            $position,
862
            null,
863
            null,
864
            ''
865
        );
866
    }
867
868
    /**
869
     * Process Chamilo True False
870
     *
871
     * @param object $objAnswer
872
     * @param array $answerValues
873
     * @param integer $position
874
     * @param integer $questionWeighting
875
     * @param array $importedFiles
876
     *
877
     * @return integer db response
878
     */
879
    public function processTrueFalse(
880
        $objAnswer,
881
        $answerValues,
882
        $position,
883
        &$questionWeighting,
884
        $importedFiles
885
    ) {
886
        $correct = intval($answerValues['fraction']) ? intval($answerValues['fraction']) : 0;
887
        $answer = $answerValues['answertext'];
888
        $comment = $answerValues['feedback'];
889
        $weighting = $answerValues['fraction'];
890
        $weighting = abs($weighting);
891
        if ($weighting > 0) {
892
            $questionWeighting += $weighting;
893
        }
894
        $goodAnswer = $correct ? true : false;
895
896
        $this->fixPathInText($importedFiles, $answer);
897
898
        $objAnswer->createAnswer(
899
            $answer,
900
            $goodAnswer,
901
            $comment,
902
            $weighting,
903
            $position,
904
            null,
905
            null,
906
            ''
907
        );
908
    }
909
910
    /**
911
     * Process Chamilo FillBlanks
912
     *
913
     * @param object $objAnswer
914
     * @param array $questionType
915
     * @param array $answerValues
916
     * @param string $placeholder
917
     * @param integer $position
918
     * @param array $importedFiles
919
     * @return integer db response
920
     *
921
     */
922
    public function processFillBlanks(
923
        $objAnswer,
0 ignored issues
show
Unused Code introduced by
The parameter $objAnswer is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

923
        /** @scrutinizer ignore-unused */ $objAnswer,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
924
        $questionType,
925
        $answerValues,
926
        &$placeholder,
927
        $position,
928
        $importedFiles
0 ignored issues
show
Unused Code introduced by
The parameter $importedFiles is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

928
        /** @scrutinizer ignore-unused */ $importedFiles

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
929
    ) {
930
        $coursePath = api_get_course_path();
931
932
        switch ($questionType) {
933
            case 'multichoice':
934
                $optionsValues = [];
935
                $correctAnswer = '';
936
                $othersAnswer = '';
937
                foreach ($answerValues as $answer) {
938
                    $correct = intval($answer['fraction']);
939
                    if ($correct) {
940
                        $correctAnswer .= $answer['answertext'].'|';
941
                        $optionsValues['weight'] = $answer['fraction'];
942
                        $optionsValues['size'] = '200';
943
                    } else {
944
                        $othersAnswer .= $answer['answertext'].'|';
945
                    }
946
                }
947
                $currentAnswers = $correctAnswer.$othersAnswer;
948
                $currentAnswers = '['.substr($currentAnswers, 0, -1).']';
949
                $placeholder = str_replace("{#$position}", $currentAnswers, $placeholder);
950
951
                return $optionsValues;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $optionsValues returns the type array|array<mixed,mixed|string> which is incompatible with the documented return type integer.
Loading history...
952
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
953
            case 'shortanswer':
954
                $optionsValues = [];
955
                $correctAnswer = '';
956
                foreach ($answerValues as $answer) {
957
                    $correct = intval($answer['fraction']);
958
                    if ($correct) {
959
                        $correctAnswer .= $answer['answertext'];
960
                        $optionsValues['weight'] = $answer['fraction'];
961
                        $optionsValues['size'] = '200';
962
                    }
963
                }
964
965
                $currentAnswers = '['.$correctAnswer.']';
966
                $placeholder = str_replace("{#$position}", $currentAnswers, $placeholder);
967
968
                return $optionsValues;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $optionsValues returns the type array|array<mixed,mixed|string> which is incompatible with the documented return type integer.
Loading history...
969
                break;
970
            case 'match':
971
                $answers = [];
972
                // Here first we need to extract all the possible answers
973
                foreach ($answerValues as $slot => $answer) {
974
                    $answers[$slot] = $answer['answertext'];
975
                }
976
977
                // Now we set the order of the values matching the correct answer and set it to the first element
978
                $optionsValues = [];
979
                foreach ($answerValues as $slot => $answer) {
980
                    $correctAnswer = '';
981
                    $othersAnswers = '';
982
                    $correctAnswer .= $answer['answertext'].'|';
983
984
                    foreach ($answers as $other) {
985
                        if ($other !== $answer['answertext']) {
986
                            $othersAnswers .= $other.'|';
987
                        }
988
                    }
989
990
                    $optionsValues[$slot]['weight'] = 1;
991
                    $optionsValues[$slot]['size'] = '200';
992
993
                    $currentAnswers = htmlentities($correctAnswer.$othersAnswers);
994
                    $currentAnswers = '['.substr($currentAnswers, 0, -1).'] ';
995
                    $answer['questiontext'] = str_replace(
996
                        '@@PLUGINFILE@@',
997
                        '/courses/'.$coursePath.'/document/moodle',
998
                        $answer['questiontext']
999
                    );
1000
1001
                    $placeholder .= '<p> '.strip_tags($answer['questiontext']).' '.$currentAnswers.' </p>';
1002
                }
1003
1004
                return $optionsValues;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $optionsValues returns the type array which is incompatible with the documented return type integer.
Loading history...
1005
1006
                break;
1007
            default:
1008
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
1009
                break;
1010
        }
1011
    }
1012
1013
    /**
1014
     * get All files associated with a question
1015
     *
1016
     * @param $filesXml
1017
     * @return array
1018
     */
1019
    public function getAllQuestionFiles($filesXml)
1020
    {
1021
        $moduleDoc = new DOMDocument();
0 ignored issues
show
Bug introduced by
The call to DOMDocument::__construct() has too few arguments starting with version. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1021
        $moduleDoc = /** @scrutinizer ignore-call */ new DOMDocument();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
1022
        $moduleRes = @$moduleDoc->loadXML($filesXml);
1023
        $allFiles = [];
1024
        if ($moduleRes) {
1025
            $activities = $moduleDoc->getElementsByTagName('file');
1026
            foreach ($activities as $activity) {
1027
                $currentItem = [];
1028
                $thisIsAnInvalidItem = false;
1029
1030
                if ($activity->childNodes->length) {
1031
                    foreach ($activity->childNodes as $item) {
1032
                        if ($item->nodeName == 'component' && $item->nodeValue == 'mod_resource') {
1033
                            $thisIsAnInvalidItem = true;
1034
                        }
1035
1036
                        if ($item->nodeName == 'contenthash') {
1037
                            $currentItem['contenthash'] = $item->nodeValue;
1038
                        }
1039
1040
                        if ($item->nodeName == 'filename') {
1041
                            $currentItem['filename'] = $item->nodeValue;
1042
                        }
1043
1044
                        if ($item->nodeName == 'filesize') {
1045
                            $currentItem['filesize'] = $item->nodeValue;
1046
                        }
1047
1048
                        if ($item->nodeName == 'mimetype' && $item->nodeValue == 'document/unknown') {
1049
                            $thisIsAnInvalidItem = true;
1050
                        }
1051
1052
                        if ($item->nodeName == 'mimetype' && $item->nodeValue !== 'document/unknown') {
1053
                            $currentItem['mimetype'] = $item->nodeValue;
1054
                        }
1055
                    }
1056
                }
1057
1058
                if (!$thisIsAnInvalidItem) {
1059
                    $allFiles[] = $currentItem;
1060
                }
1061
            }
1062
        }
1063
1064
        return $allFiles;
1065
    }
1066
1067
1068
    /**
1069
     * Litle utility to delete the unuseful tags
1070
     *
1071
     * @param $array
1072
     * @param $keys
1073
     */
1074
    public function traverseArray(&$array, $keys)
1075
    {
1076
        foreach ($array as $key => &$value) {
1077
            if (is_array($value)) {
1078
                $this->traverseArray($value, $keys);
1079
            } else {
1080
                if (in_array($key, $keys)) {
1081
                    unset($array[$key]);
1082
                }
1083
            }
1084
        }
1085
    }
1086
}
1087