Issues (48)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/SDImportData.php (28 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
if ( !defined( 'MEDIAWIKI' ) ) {
4
	echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" );
5
}
6
7
/** In this class we store things related to data processing **/
8
9
class SDImportData {
10
11
12
	/**
13
	 * Occurs after the save page request has been processed.
14
	 *
15
	 * @param WikiPage $wikiPage
16
	 * @param User $user
17
	 * @param Content $content
18
	 * @param string $summary
19
	 * @param boolean $isMinor
20
	 * @param boolean $isWatch
21
	 * @param $section Deprecated
22
	 * @param integer $flags
23
	 * @param {Revision|null} $revision
0 ignored issues
show
The doc-type {Revision|null} could not be parsed: Unknown type name "{Revision" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
24
	 * @param Status $status
25
	 * @param integer $baseRevId
26
	 * @param integer $undidRevId
27
	 *
28
	 * @return boolean
29
	 * @see https://www.mediawiki.org/wiki/Manual:Hooks/PageContentSaveComplete
30
	 */
31
	public static function saveJSONData( $wikiPage, $user, $content, $summary, $isMinor, $isWatch, $section, $flags, $revision, $status, $baseRevId, $undidRevId=null ) {
0 ignored issues
show
The parameter $summary is not used and could be removed.

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

Loading history...
The parameter $isMinor is not used and could be removed.

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

Loading history...
The parameter $isWatch is not used and could be removed.

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

Loading history...
The parameter $section is not used and could be removed.

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

Loading history...
The parameter $flags is not used and could be removed.

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

Loading history...
The parameter $status is not used and could be removed.

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

Loading history...
The parameter $baseRevId is not used and could be removed.

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

Loading history...
The parameter $undidRevId is not used and could be removed.

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

Loading history...
32
		
33
		global $wgSDImportDataPage;
34
35
		if ( $wikiPage ) {
36
			
37
			// Get NS
38
			$pageTitle = $wikiPage->getTitle();
39
40
			if ( is_object( $pageTitle ) ) {
41
42
				$ns = $pageTitle->getNamespace();
43
44
				if ( key_exists( $ns, $wgSDImportDataPage ) ) {
45
46
					$nsRepo = $wgSDImportDataPage[$ns];
47
48
					if ( array_key_exists( "json", $nsRepo ) ) {		
49
50
						if ( $nsRepo["json"] ) {
51
52
							list( $args, $table ) = self::getJSONContent( $content );
53
						
54
							$object = self::getSelector( $args, $nsRepo, "rowobject" ); // String
55
							$fields = self::getSelector( $args, $nsRepo, "rowfields", "Array" );
56
							$types = self::getSelector( $args, $nsRepo, "typefields", "Array" ); // Array
57
							$refs = self::getSelector( $args, $nsRepo, "ref" ); // Hash
58
							$pre = self::getSelector( $args, $nsRepo, "prefields" ); // Array
59
							$post = self::getSelector( $args, $nsRepo, "postfields" ); // Array
60
							$single = self::getSelector( $args, $nsRepo, "single" ); // Boolean
61
62
							// Adding properties, unless they exist
63
							$propertyTypes = self::addPropertyTypes( $fields, $types );
64
							// TODO: Handling failing, etc.
65
							self::importProperties( $propertyTypes );
66
							
67
							// No more properties added than their types
68
							if ( sizeof( array_keys( $fields ) ) > sizeof( $propertyTypes ) ) {
69
								
70
								for ( $f = sizeof( array_keys( $fields ) ); $f >= sizeof( $propertyTypes ); $f-- ) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
71
									array_pop( $fields );
72
								}
73
								
74
							}
75
		
76
							$dprops = array();
77
78
							if ( $refs ) {
79
								foreach ( $refs as $key => $val ) {
80
									$dprops[ $key ] = self::processWikiText( $val, $pageTitle );
81
								}
82
							}
83
							
84
							if ( $table ) {
85
							
86
								foreach ( $table as $row ) {
87
									$fieldcount = 0;
88
									$struct = array();
89
									foreach ( $row as $field ) {
90
				
91
										$field = trim( $field );
92
										
93
										if ( ! empty( $field ) ) {
94
											$pretxt = "";
95
											if ( isset( $pre[ $fieldcount ] ) && !empty( $pre[ $fieldcount ] ) ) {
96
												$pretxt = $pre[ $fieldcount ].":"; // : for pre
97
											}
98
											$postxt = "";
99
											if ( isset( $post[ $fieldcount ] ) && !empty( $post[ $fieldcount ] ) ) {
100
												$postxt = "@".$post[ $fieldcount ]; // @ for post
101
											}
102
											
103
											if ( array_key_exists( $fieldcount, $fields ) ) {
104
												$struct[ $fields[ $fieldcount ] ] =  $pretxt.$field.$postxt;
105
											}
106
										}
107
										$fieldcount++;
108
									}
109
									foreach ( $dprops as $dpropk => $dpropv ) {
110
										$struct[ $dpropk ] = $dpropv;
111
									}
112
113
									if ( count( array_keys( $struct ) ) > 0 ) {
114
										
115
										if ( $single ) {
116
											self::insertObjectviaJSON( $wikiPage, $revision, $user, $struct );
0 ignored issues
show
$struct is of type array, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
117
										} else {
118
											self::insertInternalObjectviaJSON( $wikiPage, $revision, $user, $object, $struct );
0 ignored issues
show
$struct is of type array, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
119
										}
120
									}
121
									
122
								}
123
							
124
							}
125
						
126
						}
127
					
128
					}
129
		
130
				}
131
			
132
			}
133
		
134
		}
135
		
136
		return true;
137
	}
138
	
139
	
140
	/**
141
	 * Function for combining properties and types
142
	 * @param $props array
143
	 * @param $types array
144
	 * 
145
	 * @return array
146
	*/
147
	public static function addPropertyTypes( $props, $types ) {
148
		
149
		$count = 0;
150
		
151
		foreach ( $types as $type ) {
152
			
153
			if ( array_key_exists( $count, $props ) ) {
154
				
155
				$prop = $props[ $count ];
156
				$propertyTypes[ $prop ] = $type;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$propertyTypes was never initialized. Although not strictly required by PHP, it is generally a good practice to add $propertyTypes = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
157
			}
158
			
159
			$count++;
160
			
161
		}
162
		
163
		return $propertyTypes;
0 ignored issues
show
The variable $propertyTypes does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
164
	}
165
	
166
	/**
167
	 * Function for importing Properties straight into the wiki
168
	 * @param $propertyTypes array
169
	 * @param $overwrite boolean
170
	 * @param $user User
171
	 * 
172
	 * @return array
173
	*/
174
	public static function importProperties( $propertyTypes, $overwrite=false, $user=null) {
0 ignored issues
show
The parameter $user is not used and could be removed.

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

Loading history...
175
		
176
		$edit_summary = "Adding property via SDImport";
177
		$listProps = array();
178
		
179
		foreach ( $propertyTypes as $prop => $type ) {
180
			
181
			// Consider going ahead it type is not null
182
			if ( $type ) {
183
184
				// TODO: to consider not hardcoding NS
185
				$propPageName = "Property:".$prop;
186
				
187
				$propTitle = Title::newFromText( $propPageName );
188
				
189
				$wikiPage = new WikiPage( $propTitle );
190
				
191
				if ( ! $wikiPage->exists() || ( $wikiPage->exists() && $overwrite ) ) {
192
					
193
					$text = "[[Has type::".$type."]]";
194
					
195
					$new_content = new WikitextContent( $text );
196
					$status = $wikiPage->doEditContent( $new_content, $edit_summary );
0 ignored issues
show
$status is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
197
					
198
					// Adding list
199
					// TODO: ideally handling Status 
200
					array_push( $listProps, $prop );
201
				}
202
			
203
			}
204
			
205
		}
206
		
207
		
208
		return $listProps;
209
	}
210
	
211
	/**
212
	 * @param $pageTitle Title
213
	 * @param $object string
214
	 * @param struct object
215
	 * 
216
	 * @return boolean
217
	*/
218
	public static function insertInternalObject( $parser, $pageTitle, $object, $struct ) {
219
220
		# TODO: Check if this will work
221
		if ( ! $parser ) {
222
			$parser = new Parser();
223
			$parser->setTitle( $pageTitle ); // Put context
224
		}
225
		
226
		$subobjectArgs = array( &$parser );
227
		// Blank first argument, so that subobject ID will be
228
		// an automatically-generated random number.
229
		$subobjectArgs[1] = '';
230
		// "main" property, pointing back to the page.
231
		$mainPageName = $pageTitle->getText();
232
		$mainPageNamespace = $pageTitle->getNsText();
233
		if ( $mainPageNamespace != '' ) {
234
			$mainPageName = $mainPageNamespace . ':' . $mainPageName;
235
		}
236
		$subobjectArgs[2] = $object . '=' . $mainPageName;
237
238
		foreach ( $struct as $prop => $value ) {
239
			$subobjectArgs[] = $prop . '=' . $value;
240
		}
241
242
		if ( class_exists( 'SMW\SubobjectParserFunction' ) ) {
243
			// SMW 1.9+
244
			$subobjectFunction = \SMW\ParserFunctionFactory::newFromParser( $parser )->getSubobjectParser();
0 ignored issues
show
Deprecated Code introduced by
The method SMW\ParserFunctionFactory::getSubobjectParser() has been deprecated with message: since 2.1, use newSubobjectParserFunction

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
245
			return $subobjectFunction->parse( new SMW\ParserParameterFormatter( $subobjectArgs ) );
246
		} else {
247
			// SMW 1.8
248
			call_user_func_array( array( 'SMWSubobject', 'render' ), $subobjectArgs );
249
		}
250
		return;
251
	}
252
	
253
	
254
	/**
255
	 * @param $wikiPage wikiPage
256
     * @param $revision revision
257
     * @param $user user
258
	 * @param struct object
259
	 * 
260
	 * @return boolean
261
     * 
262
     * Code adapted from: https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2974
263
	*/
264
	public static function insertObjectviaJSON( $wikiPage, $revision, $user, $struct ) {
265
	
266
		$applicationFactory = \SMW\ApplicationFactory::getInstance();
267
		
268
		$mwCollaboratorFactory = $applicationFactory->newMwCollaboratorFactory();
269
		
270
		/** * Initialize the ParserOuput object */
271
		$editInfoProvider = $mwCollaboratorFactory->newEditInfoProvider( $wikiPage, $revision, $user );
272
		
273
		$parserOutput = $editInfoProvider->fetchEditInfo()->getOutput();
274
275
		if ( !$parserOutput instanceof \ParserOutput ) {
0 ignored issues
show
The class ParserOutput does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
276
			return true;
277
		}
278
		
279
		$parserData = $applicationFactory->newParserData( $wikiPage->getTitle(), $parserOutput );
280
	
281
		$subject = $parserData->getSubject();
282
283
		$subject = new \SMW\DIWikiPage( $subject->getDBkey(), $subject->getNamespace(), $subject->getInterwiki() );
284
		
285
		// TODO: To finish
286
		foreach ( $struct as $property => $value ) {
287
			// Struct to iterate
288
			
289
			$dataValue = \SMW\DataValueFactory::getInstance()->newDataValueByText( $property, $value, false, $subject );
290
291
			$parserData->getSemanticData()->addDataValue( $dataValue );
292
293
		}
294
	
295
		// This part is used to add the subobject the the main subject
296
		$parserData->pushSemanticDataToParserOutput();
0 ignored issues
show
Deprecated Code introduced by
The method SMW\ParserData::pushSemanticDataToParserOutput() has been deprecated with message: since 3.0, use copyToParserOutput

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
297
		$parserData->updateStore();
298
		
299
		// Below it works event with maintenance function
300
		$store = \SMW\StoreFactory::getStore();
301
		$store->updateData( $parserData->getSemanticData() );
302
		
303
		return true;
304
	}	
305
	
306
	/**
307
	 * @param $wikiPage wikiPage
308
     * @param $revision revision
309
     * @param $user user
310
	 * @param $object string
311
	 * @param struct object
312
	 * 
313
	 * @return boolean
314
     * 
315
     * Code from: https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2974
316
	*/
317
	public static function insertInternalObjectviaJSON( $wikiPage, $revision, $user, $object, $struct ) {
318
319
		$applicationFactory = \SMW\ApplicationFactory::getInstance();
320
		
321
		$mwCollaboratorFactory = $applicationFactory->newMwCollaboratorFactory();
322
		
323
		/** * Initialize the ParserOuput object */
324
		$editInfoProvider = $mwCollaboratorFactory->newEditInfoProvider( $wikiPage, $revision, $user );
325
		
326
		$parserOutput = $editInfoProvider->fetchEditInfo()->getOutput();
327
328
		if ( !$parserOutput instanceof \ParserOutput ) {
0 ignored issues
show
The class ParserOutput does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
329
			return true;
330
		}
331
		
332
		$parserData = $applicationFactory->newParserData( $wikiPage->getTitle(), $parserOutput );
333
334
		$subject = $parserData->getSubject();
335
		
336
		// Identify the content as unique
337
		$subobjectName = '_SDI' . md5( json_encode( $struct ) );
338
339
		$subject = new \SMW\DIWikiPage( $subject->getDBkey(), $subject->getNamespace(), $subject->getInterwiki(), $subobjectName );
340
341
		// Build the subobject by using a separate container object
342
		$containerSemanticData = new \SMWContainerSemanticData( $subject );
343
344
		// Iterate through here
345
		
346
		foreach ( $struct as $property => $value ) {
347
			// If you don't know the type, use the DataValueFactory
348
			$dataValue = \SMW\DataValueFactory::getInstance()->newDataValueByText( $property, $value );
349
			$containerSemanticData->addDataValue( $dataValue );
350
		}
351
		
352
		// Object assignation
353
		
354
		if ( $object ) {
355
			
356
			if ( ! empty( $object ) ) {
357
				
358
				if ( $wikiPage->getTitle() ) {
359
					
360
					$fullTitle = $wikiPage->getTitle()->getPrefixedText();
361
					
362
					$dataValue = \SMW\DataValueFactory::getInstance()->newDataValueByText( $object, $fullTitle );
363
					$containerSemanticData->addDataValue( $dataValue );
364
					
365
				}
366
				
367
			}
368
			
369
		}
370
		
371
		
372
		// This part is used to add the subobject the the main subject
373
		$parserData->getSemanticData()->addPropertyObjectValue( new \SMW\DIProperty( \SMW\DIProperty::TYPE_SUBOBJECT ), new \SMWDIContainer( $containerSemanticData ) );
374
		$parserData->pushSemanticDataToParserOutput();
0 ignored issues
show
Deprecated Code introduced by
The method SMW\ParserData::pushSemanticDataToParserOutput() has been deprecated with message: since 3.0, use copyToParserOutput

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
375
		$parserData->updateStore();
376
377
		// Below it works event with maintenance function
378
		$store = \SMW\StoreFactory::getStore();
379
		$store->updateData( $parserData->getSemanticData() );
380
		
381
		return true;
382
	}
383
384
385
	/**
386
	* @first -> First hash
387
	* @second -> Second hash
388
	* @key -> Actual key
389
390
	* @return variable (depending on case)
391
	*/
392
	public static function getSelector( $first, $second, $key, $opt=null ) {
393
		
394
		if ( key_exists( $key, $first ) ) {
395
			// Here process
396
			
397
			$array = array();
398
399
			if ( is_array( $first[ $key ] ) ) {
400
				$keyvals = $first[ $key ];
401
			} else {
402
				$keyvals = explode( ",", $first[ $key ] );
403
			}
404
			
405
			if ( self::isAssocArray( $keyvals ) || ( $opt && $opt === "Array" ) ) {
406
				
407
				return $keyvals;
408
			
409
			} else {
410
			
411
				if ( count( $keyvals ) < 2 ) {
412
					// If => ergo hash
413
					
414
					$keyhvals = explode( "#", $keyvals[0], 2 );
415
	
416
					if ( count( $keyhvals ) > 1 ) {
417
						$array[ trim( $keyhvals[0] ) ] = trim( $keyhvals[1] );
418
						return $array;
419
					} else {
420
						return trim( $keyhvals[0] );
421
					}
422
				} else { 
423
	
424
					foreach ( $keyvals as $keyval ) {
425
						$keyval = trim( $keyval );
426
						
427
						// If => ergo hash
428
						$keyhvals = explode( "#", $keyval, 2 );
429
		
430
						if ( count( $keyhvals ) > 1 ) {
431
							$array[ trim( $keyhvals[0] ) ] = trim( $keyhvals[1] );
432
						}
433
						else {
434
							array_push( $array, trim( $keyhvals[0] ) );
435
						}
436
					}
437
	
438
					return $array;
439
				}
440
			
441
			}
442
443
		} else {
444
			if ( key_exists( $key, $second ) ) {
445
				// Direct
446
				return $second[$key];
447
			} else {
448
				return false;
449
			}
450
		}
451
		
452
	}
453
	
454
455
	/**
456
	 * Whether associative array or not
457
	 * $arr Array
458
	 * @return boolean
459
	*/
460
	
461
	public static function isAssocArray( array $arr ) {
462
	    if (array() === $arr) return false;
463
	    return array_keys($arr) !== range(0, count($arr) - 1);
464
	}
465
466
467
468
	/**
469
	 * Occurs after the save page request has been processed.
470
	 * @see https://www.mediawiki.org/wiki/Manual:Hooks/PageContentSaveComplete
471
	 *
472
	 * @param text string
473
	 *
474
	 * @return boolean
475
	*/
476
	public static function processWikiText( $text, $pageTitle ) {
477
478
		// TODO: Ideally a full wikitext processing here, etc.
479
480
		if ( $text == '{{PAGENAME}}' ) {
481
			$text = $pageTitle->getText();
482
		} elseif ( $text == '{{FULLPAGENAME}}' ) {
483
			$text = $pageTitle->getPrefixedText();
484
		} else {
485
			// Do nothing;
486
		}
487
488
		return $text;
489
	}
490
491
	
492
	private static function getJSONContent( $content ) {
493
494
		$outcome = array( );
495
		$args = null;
496
		$data = null;
497
498
		if ( $content ) {
499
			
500
			if ( is_object( $content ) ) {
501
				
502
				if ( $content->getModel() == CONTENT_MODEL_JSON ) {
503
504
					// Only act if JSON
505
					
506
					$json = $content->getNativeData();
507
508
					
509
					list( $args, $data ) = self::processJSON( $json );
510
				}
511
				
512
			}
513
		}
514
		
515
		array_push( $outcome, $args );
516
		array_push( $outcome, $data );
517
518
		return $outcome;
519
	}
520
521
	
522
	private static function processJSON( $json ) {
523
524
		$outcome = array( );
525
		$args = null;
526
		$data = null;
527
		
528
		$SDIJSON = false;
529
		
530
		// Check JSON is valid
531
		$jsonObj = json_decode( $json, true );
532
533
		if ( $jsonObj ) {
534
		
535
			if ( array_key_exists( "meta", $jsonObj ) ) {
536
				
537
				$meta = $jsonObj["meta"];
538
539
				if ( array_key_exists( "app", $meta ) ) {
540
					
541
					if ( $meta["app"] === "SDI" ) {
542
						$SDIJSON = true;
543
					}
544
					
545
				}
546
				
547
				$args = $meta;
548
				
549
				# TODO: Addding more custom fields to args
550
				
551
				
552
				if ( array_key_exists( "data", $jsonObj ) && $SDIJSON ) {
553
				
554
					$dataObj = $jsonObj["data"];
555
				
556
					$data = self::checkJSONData( $dataObj );
557
				
558
				}
559
			}
560
		
561
		}
562
563
		
564
		array_push( $outcome, $args );
565
		array_push( $outcome, $data );
566
567
		return $outcome;	
568
		
569
	}
570
	
571
	private static function checkJSONData( $dataObj ) {
572
		
573
		$data = null;
574
		
575
		if ( is_array( $dataObj ) ) {
576
			
577
			if ( count( $dataObj ) > 0 ) {
578
				
579
				$bad = false;
580
				
581
				// We should have an array of arrays
582
				foreach ( $dataObj as $row ) {
583
					
584
					if ( ! is_array( $row ) ) {
585
						$bad = true;
586
					}
587
				}
588
				
589
				if ( ! $bad ) {
590
					$data = $dataObj;
591
				}
592
				
593
			}
594
		}
595
596
			
597
		return $data;
598
	}
599
600
601
	/** Import of Wikitext, let's say, at commit */
602
	/**
603
	* @text Bulk data text
604
	* @pagetitle Title of the page
605
	* @delimiter Delimiter of CSV
606
	* @enclosure Enclosure of CSV
607
	* @num Occurrence in page. If only one, then 0
608
	* @return status of update
609
	*/
610
	public static function importWikiText( $text, $pagetitle, $separator=NULL, $delimiter=NULL, $num=0 ) {
611
612
		$title = Title::newFromText( $pagetitle );
613
		$wikipage = WikiPage::factory( $title );
614
		
615
		# Default cases
616
		$newpage = false;
617
		$contentModel = "wikitext";
618
		
619
		$extraInfo = "";
620
621
		$ns = $pageTitle->getNamespace();
0 ignored issues
show
The variable $pageTitle does not exist. Did you mean $pagetitle?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
622
623
		if ( $GLOBALS["wgSDImportDataPage"] && array_key_exists( $ns, $GLOBALS["wgSDImportDataPage"] ) ) {
624
625
			if ( $separator !== NULL ) {
626
				if ( array_key_exists( "separator", $GLOBALS["wgSDImportDataPage"][$ns] ) ) {
627
					if ( $GLOBALS["wgSDImportDataPage"][$ns]["separator"] != $separator ) {
628
						$extraInfo = $extraInfo . " separator=\"".$separator."\"";
629
					}
630
				}
631
			}
632
			if ( $delimiter !== NULL ) {
633
				if ( array_key_exists( "delimiter", $GLOBALS["wgSDImportDataPage"][$ns] ) ) {
634
					if ( $GLOBALS["wgSDImportDataPage"][$ns]["delimiter"] != $delimiter ) {
635
						$extraInfo = $extraInfo . " delimiter='".$delimiter."' ";
0 ignored issues
show
$extraInfo is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
636
					}
637
				}
638
			}
639
		}
640
641
		// Retrievet text of page
642
		// Back-compatibility, just in case
643
		
644
		// TODO: Handle if creation of page
645
		if ( ! $wikipage->exists() ) {
646
			$newpage = true;
647
		}
648
		
649
		if ( ! $newpage ) {
650
		
651
			if ( method_exists ( $wikipage, "getContent" ) ) {
652
				$mainContent = $wikipage->getContent();
653
				$mainText = $mainContent->getNativeData();
654
				$contentModel = $wikipage->getContentModel();
655
				
656
				if ( ! $contentModel ) {
657
					$contentModel = "wikitext";
658
				}
659
				
660
			} else {
661
				$mainText = $wikipage->getText();
662
				$contentModel = "wikitext";
663
			}
664
		
665
		}
666
667
		$status = 0;
668
		$tableText = "";
669
		
670
		# Allow only in wikitext context
671
		if ( $contentModel === "wikitext" ) {
672
		
673
		
674
			if ( ! $newpage ) {
675
				
676
				// Get matches
677
				$page_parts = preg_split( "/(<smwdata.*?>)/", $mainText, -1, PREG_SPLIT_DELIM_CAPTURE );
0 ignored issues
show
The variable $mainText does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
678
		
679
				$count = 0;
680
				$outcome = array();
681
		
682
				foreach ( $page_parts as $page_part ) {
683
		
684
					if ( preg_match( "/<smwdata/", $page_part ) ) {
685
						$count = $count + 1;
686
					} else {
687
						if ( $num == $count - 1 ) {
688
							if ( preg_match( "/<\/smwdata/", $page_part ) ) {
689
		
690
								$in_parts = preg_split( "/(<\/smwdata.*?>)/", $page_part, -1, PREG_SPLIT_DELIM_CAPTURE );
691
								$in_parts[0] = "\n".$text."\n";
692
								$page_part = implode( "", $in_parts );
693
							}
694
						}
695
					}
696
		
697
					array_push( $outcome, $page_part );
698
				}
699
				
700
				// If stuff
701
				if ( count( $outcome ) > 0 ) {
702
	
703
					$tableText = implode( "", $outcome );			
704
				}
705
			
706
			} else {
707
				
708
				$tableText = "<smwdata>".$text."</smwdata>";
709
				
710
			}
711
	
712
	
713
			if ( ! empty( $tableText ) ) {
714
				
715
				// Submit content
716
				// Back-compatibility, just in case
717
				if ( method_exists ( $wikipage, "doEditContent" ) ) {
718
					$content = new WikiTextContent( $tableText );
719
					$status = $wikipage->doEditContent( $content, "Updating content" );
720
				} else {
721
					$status = $wikipage->doEdit( $tableText, "Updating content" );
722
				}
723
	
724
			}
725
			
726
			// TODO: Handle status value if not normal one
727
		
728
		}
729
730
		return $status;
731
	}
732
	
733
	
734
735
	/**
736
	* @text Bulk data text
737
	* @pagetitle Title of the page
738
	* @return status of update
739
	*/
740
741
	public static function importJSONBatch( $text, $namespace="", $overwrite=false) {
742
743
		$jsonObj = json_decode( $text, true );
744
		$dataObj = $jsonObj["data"];
745
		$dataHash = array();
746
		
747
		// If namespace is numeric, check if in config
748
		if ( is_numeric( $namespace ) ) {
749
			
750
			global $wgContLang;
751
			
752
			if ( $namespace == 0 ) {
753
				
754
				$namespace = "";
755
				
756
			} else {
757
			
758
				if ( $wgContLang->getNsText( intval( $namespace, 10 ) ) ) {
759
					
760
					$namespace = $wgContLang->getNsText( intval( $namespace, 10 ) );
761
				}
762
			
763
			}
764
		}
765
		
766
		for ( $x=0; $x <count($dataObj); $x++ ) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
767
768
			if ( count( $dataObj[$x] ) > 1 ) {
769
				
770
				$pageCell = array_shift(  $dataObj[$x] );
771
				
772
				if ( $pageCell !== "" ) {
773
					
774
					if ( $namespace === "" ) {
775
						$pagetitle =  $pageCell;
776
					} else {
777
						$pagetitle = $namespace . ':' . $pageCell;
778
					}	
779
					
780
				}
781
				
782
				if ( ! $dataHash[ $pagetitle ] ) {
783
					$dataHash[ $pagetitle ] = array();
0 ignored issues
show
The variable $pagetitle does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
784
				}
785
				
786
				array_push( $dataHash[ $pagetitle ], $dataObj[$x] );
787
				
788
			}
789
			
790
		}
791
		
792
		foreach ( $dataHash as $pagetitle => $dataArray ) {
793
			
794
			$jsonSubObj = array();
795
			$jsonSubObj["data"] = $dataArray;
796
			$jsonSubObj["meta"] = $jsonObj["meta"];
797
798
			self::importJSON( json_encode( $jsonSubObj ), $pagetitle, $overwrite );
799
			
800
		}
801
		
802
		return true;
803
804
	}
805
806
	/** Import of JSON into a page, let's say, at commit **/
807
	/**
808
	* @text Bulk data text
809
	* @pagetitle Title of the page
810
	* @return status of update
811
	*/
812
	public static function importJSON( $text, $pagetitle, $overwrite=false) {
813
814
		$title = Title::newFromText( $pagetitle );
815
		$wikipage = WikiPage::factory( $title );
816
		$goahead = true;
817
		$status = false;
818
		// Check if exists
819
		if ( $wikipage->exists() &&  ! $overwrite ) {
820
			$goahead = false;
821
		}
822
			if ( $goahead ) {
823
				// Check compatibility. Only if newer versions of MW
824
				if ( method_exists ( $wikipage, "getContent" ) ) {
825
					$contentModel = $wikipage->getContentModel();
826
					if ( $contentModel === "json" || ! $wikipage->exists() ) {
827
						$content = new JSONContent($text);
828
						$status = $wikipage->doEditContent( $content, "Updating content" );
829
					}
830
				}
831
			}
832
		return $status;
833
	}
834
	
835
	/** TODO: TO BE UPDATED **/
836
	public static function prepareStructForJSON( $meta, $data ) {
837
		
838
		$strJSON = "";
839
		
840
		if ( $data ) {
841
			
842
			$obj = array( );
843
			
844
			// TODO: this may change in future versions
845
			$obj["meta"] = array();
846
			$obj["meta"]["app"] = "SDI";
847
			$obj["meta"]["version"] = 0.1;
848
			
849
			if ( $meta ) {
850
				
851
				if ( array_key_exists( "rowfields", $meta ) ) {
852
					$obj["meta"]["rowfields"] = $meta["rowfields"];
853
				}
854
			}
855
			
856
			$obj["data"] = $data;
857
			
858
			$strJSON = json_encode( $obj );
859
		}
860
861
		return $strJSON;
862
	}
863
	
864
865
	/**
866
	* @param $out OutputPage
867
	* @param $text string
868
	* @return $out OutputPage
869
	*/
0 ignored issues
show
The doc-type $out could not be parsed: Unknown type name "$out" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
870
	
871
	public static function onOutputPageBeforeHTML( &$out, &$text ) {
0 ignored issues
show
The parameter $text is not used and could be removed.

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

Loading history...
872
873
		// We add Modules
874
		$out->addModules( 'ext.sdimport' );
875
		
876
		global $wgSDImportDataPage;
877
		
878
		// Get Namespace
879
		
880
		$context = RequestContext::getMain();
881
		if ( $context ) {
882
			$pageTitle = $context->getTitle();
883
			if ( $pageTitle ) {
884
				
885
				$ns = $pageTitle->getNamespace();
886
				
887
				if ( array_key_exists( $ns, $wgSDImportDataPage ) ) {
888
					
889
					if ( array_key_exists( "form", $wgSDImportDataPage[$ns] )  ) {
890
						
891
						if ( $wgSDImportDataPage[$ns]["form"] === true ) {
892
							
893
							// Adding form libraries only if needed
894
							$out->addModules( 'ext.sdimport.form' );
895
							
896
						}
897
					}
898
				}
899
				
900
			}
901
		}
902
		
903
		return $out;
904
	}
905
	
906
	/** This allow PHP vars to be exposed to JavaScript **/
907
	
908
	public static function onResourceLoaderGetConfigVars( &$vars ) {
909
		
910
		global $wgSDImportDataPage;
911
912
913
		$vars['wgSDImportDataPage'] = $wgSDImportDataPage;
914
915
		return true;
916
	}
917
}
918