Completed
Push — master ( 3c1060...c0e803 )
by Toni Hermoso
18:28
created

SDImportData::importProperties()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 4
nop 3
dl 0
loc 36
rs 8.7217
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 9 and the first side effect is on line 4.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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 {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
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
Documentation introduced by
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
Unused Code introduced by
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...
Unused Code introduced by
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...
Unused Code introduced by
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...
Unused Code introduced by
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...
Unused Code introduced by
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...
Unused Code introduced by
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...
Unused Code introduced by
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...
Unused Code introduced by
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;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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
Documentation introduced by
$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
Documentation introduced by
$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
Bug introduced by
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
Unused Code introduced by
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
Unused Code introduced by
$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
Bug introduced by
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();
297
		$parserData->updateStore();
298
		// Below it works event with maintenance function
299
		// $store = \SMW\StoreFactory::getStore();
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
300
		// $store->updateData( $parserData->getSemanticData() );
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
301
		
302
		return true;
303
	}	
304
	
305
	/**
306
	 * @param $wikiPage wikiPage
307
     * @param $revision revision
308
     * @param $user user
309
	 * @param $object string
310
	 * @param struct object
311
	 * 
312
	 * @return boolean
313
     * 
314
     * Code from: https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2974
315
	*/
316
	public static function insertInternalObjectviaJSON( $wikiPage, $revision, $user, $object, $struct ) {
317
318
		$applicationFactory = \SMW\ApplicationFactory::getInstance();
319
		
320
		$mwCollaboratorFactory = $applicationFactory->newMwCollaboratorFactory();
321
		
322
		/** * Initialize the ParserOuput object */
323
		$editInfoProvider = $mwCollaboratorFactory->newEditInfoProvider( $wikiPage, $revision, $user );
324
		
325
		$parserOutput = $editInfoProvider->fetchEditInfo()->getOutput();
326
327
		if ( !$parserOutput instanceof \ParserOutput ) {
0 ignored issues
show
Bug introduced by
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...
328
			return true;
329
		}
330
		
331
		$parserData = $applicationFactory->newParserData( $wikiPage->getTitle(), $parserOutput );
332
333
		$subject = $parserData->getSubject();
334
		
335
		// Identify the content as unique
336
		$subobjectName = '_SDI' . md5( json_encode( $struct ) );
337
338
		$subject = new \SMW\DIWikiPage( $subject->getDBkey(), $subject->getNamespace(), $subject->getInterwiki(), $subobjectName );
339
340
		// Build the subobject by using a separate container object
341
		$containerSemanticData = new \SMWContainerSemanticData( $subject );
342
343
		// Iterate through here
344
		
345
		foreach ( $struct as $property => $value ) {
346
			// If you don't know the type, use the DataValueFactory
347
			$dataValue = \SMW\DataValueFactory::getInstance()->newDataValueByText( $property, $value );
348
			$containerSemanticData->addDataValue( $dataValue );
349
		}
350
		
351
		// Object assignation
352
		
353
		if ( $object ) {
354
			
355
			if ( ! empty( $object ) ) {
356
				
357
				if ( $wikiPage->getTitle() ) {
358
					
359
					$fullTitle = $wikiPage->getTitle()->getPrefixedText();
360
					
361
					$dataValue = \SMW\DataValueFactory::getInstance()->newDataValueByText( $object, $fullTitle );
362
					$containerSemanticData->addDataValue( $dataValue );
363
					
364
				}
365
				
366
			}
367
			
368
		}
369
		
370
		
371
		// This part is used to add the subobject the the main subject
372
		$parserData->getSemanticData()->addPropertyObjectValue( new \SMW\DIProperty( \SMW\DIProperty::TYPE_SUBOBJECT ), new \SMWDIContainer( $containerSemanticData ) );
373
		$parserData->pushSemanticDataToParserOutput();
374
		$parserData->updateStore();
375
376
		// Below it works event with maintenance function
377
		// $store = \SMW\StoreFactory::getStore();
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
378
		// $store->updateData( $parserData->getSemanticData() );
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
379
		
380
		return true;
381
	}
382
383
384
	/**
385
	* @first -> First hash
386
	* @second -> Second hash
387
	* @key -> Actual key
388
389
	* @return variable (depending on case)
390
	*/
391
	public static function getSelector( $first, $second, $key, $opt=null ) {
392
		
393
		if ( key_exists( $key, $first ) ) {
394
			// Here process
395
			
396
			$array = array();
397
398
			if ( is_array( $first[ $key ] ) ) {
399
				$keyvals = $first[ $key ];
400
			} else {
401
				$keyvals = explode( ",", $first[ $key ] );
402
			}
403
			
404
			if ( self::isAssocArray( $keyvals ) || ( $opt && $opt === "Array" ) ) {
405
				
406
				return $keyvals;
407
			
408
			} else {
409
			
410
				if ( count( $keyvals ) < 2 ) {
411
					// If => ergo hash
412
					
413
					$keyhvals = explode( "#", $keyvals[0], 2 );
414
	
415
					if ( count( $keyhvals ) > 1 ) {
416
						$array[ trim( $keyhvals[0] ) ] = trim( $keyhvals[1] );
417
						return $array;
418
					} else {
419
						return trim( $keyhvals[0] );
420
					}
421
				} else { 
422
	
423
					foreach ( $keyvals as $keyval ) {
424
						$keyval = trim( $keyval );
425
						
426
						// If => ergo hash
427
						$keyhvals = explode( "#", $keyval, 2 );
428
		
429
						if ( count( $keyhvals ) > 1 ) {
430
							$array[ trim( $keyhvals[0] ) ] = trim( $keyhvals[1] );
431
						}
432
						else {
433
							array_push( $array, trim( $keyhvals[0] ) );
434
						}
435
					}
436
	
437
					return $array;
438
				}
439
			
440
			}
441
442
		} else {
443
			if ( key_exists( $key, $second ) ) {
444
				// Direct
445
				return $second[$key];
446
			} else {
447
				return false;
448
			}
449
		}
450
		
451
	}
452
	
453
454
	/**
455
	 * Whether associative array or not
456
	 * $arr Array
457
	 * @return boolean
458
	*/
459
	
460
	public static function isAssocArray( array $arr ) {
461
	    if (array() === $arr) return false;
462
	    return array_keys($arr) !== range(0, count($arr) - 1);
463
	}
464
465
466
467
	/**
468
	 * Occurs after the save page request has been processed.
469
	 * @see https://www.mediawiki.org/wiki/Manual:Hooks/PageContentSaveComplete
470
	 *
471
	 * @param text string
472
	 *
473
	 * @return boolean
474
	*/
475
	public static function processWikiText( $text, $pageTitle ) {
476
477
		// TODO: Ideally a full wikitext processing here, etc.
478
479
		if ( $text == '{{PAGENAME}}' ) {
480
			$text = $pageTitle->getText();
481
		} elseif ( $text == '{{FULLPAGENAME}}' ) {
482
			$text = $pageTitle->getPrefixedText();
483
		} else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
484
			// Do nothing;
485
		}
486
487
		return $text;
488
	}
489
490
	
491
	private static function getJSONContent( $content ) {
492
493
		$outcome = array( );
494
		$args = null;
495
		$data = null;
496
497
		if ( $content ) {
498
			
499
			if ( is_object( $content ) ) {
500
				
501
				if ( $content->getModel() == CONTENT_MODEL_JSON ) {
502
503
					// Only act if JSON
504
					
505
					$json = $content->getNativeData();
506
507
					
508
					list( $args, $data ) = self::processJSON( $json );
509
				}
510
				
511
			}
512
		}
513
		
514
		array_push( $outcome, $args );
515
		array_push( $outcome, $data );
516
517
		return $outcome;
518
	}
519
520
	
521
	private static function processJSON( $json ) {
522
523
		$outcome = array( );
524
		$args = null;
525
		$data = null;
526
		
527
		$SDIJSON = false;
528
		
529
		// Check JSON is valid
530
		$jsonObj = json_decode( $json, true );
531
532
		if ( $jsonObj ) {
533
		
534
			if ( array_key_exists( "meta", $jsonObj ) ) {
535
				
536
				$meta = $jsonObj["meta"];
537
538
				if ( array_key_exists( "app", $meta ) ) {
539
					
540
					if ( $meta["app"] === "SDI" ) {
541
						$SDIJSON = true;
542
					}
543
					
544
				}
545
				
546
				$args = $meta;
547
				
548
				# TODO: Addding more custom fields to args
549
				
550
				
551
				if ( array_key_exists( "data", $jsonObj ) && $SDIJSON ) {
552
				
553
					$dataObj = $jsonObj["data"];
554
				
555
					$data = self::checkJSONData( $dataObj );
556
				
557
				}
558
			}
559
		
560
		}
561
562
		
563
		array_push( $outcome, $args );
564
		array_push( $outcome, $data );
565
566
		return $outcome;	
567
		
568
	}
569
	
570
	private static function checkJSONData( $dataObj ) {
571
		
572
		$data = null;
573
		
574
		if ( is_array( $dataObj ) ) {
575
			
576
			if ( count( $dataObj ) > 0 ) {
577
				
578
				$bad = false;
579
				
580
				// We should have an array of arrays
581
				foreach ( $dataObj as $row ) {
582
					
583
					if ( ! is_array( $row ) ) {
584
						$bad = true;
585
					}
586
				}
587
				
588
				if ( ! $bad ) {
589
					$data = $dataObj;
590
				}
591
				
592
			}
593
		}
594
595
			
596
		return $data;
597
	}
598
599
600
	/** Import of Wikitext, let's say, at commit */
601
	/**
602
	* @text Bulk data text
603
	* @pagetitle Title of the page
604
	* @delimiter Delimiter of CSV
605
	* @enclosure Enclosure of CSV
606
	* @num Occurrence in page. If only one, then 0
607
	* @return status of update
608
	*/
609
	public static function importWikiText( $text, $pagetitle, $separator=NULL, $delimiter=NULL, $num=0 ) {
0 ignored issues
show
Coding Style introduced by
importWikiText uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
610
611
		$title = Title::newFromText( $pagetitle );
612
		$wikipage = WikiPage::factory( $title );
613
		
614
		# Default cases
615
		$newpage = false;
616
		$contentModel = "wikitext";
617
		
618
		$extraInfo = "";
619
620
		$ns = $pageTitle->getNamespace();
0 ignored issues
show
Bug introduced by
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...
621
622
		if ( $GLOBALS["wgSDImportDataPage"] && array_key_exists( $ns, $GLOBALS["wgSDImportDataPage"] ) ) {
623
624
			if ( $separator !== NULL ) {
625
				if ( array_key_exists( "separator", $GLOBALS["wgSDImportDataPage"][$ns] ) ) {
626
					if ( $GLOBALS["wgSDImportDataPage"][$ns]["separator"] != $separator ) {
627
						$extraInfo = $extraInfo . " separator=\"".$separator."\"";
628
					}
629
				}
630
			}
631
			if ( $delimiter !== NULL ) {
632
				if ( array_key_exists( "delimiter", $GLOBALS["wgSDImportDataPage"][$ns] ) ) {
633
					if ( $GLOBALS["wgSDImportDataPage"][$ns]["delimiter"] != $delimiter ) {
634
						$extraInfo = $extraInfo . " delimiter='".$delimiter."' ";
0 ignored issues
show
Unused Code introduced by
$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...
635
					}
636
				}
637
			}
638
		}
639
640
		// Retrievet text of page
641
		// Back-compatibility, just in case
642
		
643
		// TODO: Handle if creation of page
644
		if ( ! $wikipage->exists() ) {
645
			$newpage = true;
646
		}
647
		
648
		if ( ! $newpage ) {
649
		
650
			if ( method_exists ( $wikipage, "getContent" ) ) {
651
				$mainContent = $wikipage->getContent();
652
				$mainText = $mainContent->getNativeData();
653
				$contentModel = $wikipage->getContentModel();
654
				
655
				if ( ! $contentModel ) {
656
					$contentModel = "wikitext";
657
				}
658
				
659
			} else {
660
				$mainText = $wikipage->getText();
661
				$contentModel = "wikitext";
662
			}
663
		
664
		}
665
666
		$status = 0;
667
		$tableText = "";
668
		
669
		# Allow only in wikitext context
670
		if ( $contentModel === "wikitext" ) {
671
		
672
		
673
			if ( ! $newpage ) {
674
				
675
				// Get matches
676
				$page_parts = preg_split( "/(<smwdata.*?>)/", $mainText, -1, PREG_SPLIT_DELIM_CAPTURE );
0 ignored issues
show
Bug introduced by
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...
677
		
678
				$count = 0;
679
				$outcome = array();
680
		
681
				foreach ( $page_parts as $page_part ) {
682
		
683
					if ( preg_match( "/<smwdata/", $page_part ) ) {
684
						$count = $count + 1;
685
					} else {
686
						if ( $num == $count - 1 ) {
687
							if ( preg_match( "/<\/smwdata/", $page_part ) ) {
688
		
689
								$in_parts = preg_split( "/(<\/smwdata.*?>)/", $page_part, -1, PREG_SPLIT_DELIM_CAPTURE );
690
								$in_parts[0] = "\n".$text."\n";
691
								$page_part = implode( "", $in_parts );
692
							}
693
						}
694
					}
695
		
696
					array_push( $outcome, $page_part );
697
				}
698
				
699
				// If stuff
700
				if ( count( $outcome ) > 0 ) {
701
	
702
					$tableText = implode( "", $outcome );			
703
				}
704
			
705
			} else {
706
				
707
				$tableText = "<smwdata>".$text."</smwdata>";
708
				
709
			}
710
	
711
	
712
			if ( ! empty( $tableText ) ) {
713
				
714
				// Submit content
715
				// Back-compatibility, just in case
716
				if ( method_exists ( $wikipage, "doEditContent" ) ) {
717
					$content = new WikiTextContent( $tableText );
718
					$status = $wikipage->doEditContent( $content, "Updating content" );
719
				} else {
720
					$status = $wikipage->doEdit( $tableText, "Updating content" );
721
				}
722
	
723
			}
724
			
725
			// TODO: Handle status value if not normal one
726
		
727
		}
728
729
		return $status;
730
	}
731
	
732
	
733
734
	/**
735
	* @text Bulk data text
736
	* @pagetitle Title of the page
737
	* @return status of update
738
	*/
739
740
	public static function importJSONBatch( $text, $namespace="", $overwrite=false) {
741
742
		$jsonObj = json_decode( $text, true );
743
		$dataObj = $jsonObj["data"];
744
		$dataHash = array();
745
		
746
		// If namespace is numeric, check if in config
747
		if ( is_numeric( $namespace ) ) {
748
			
749
			global $wgContLang;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
750
			
751
			if ( $namespace == 0 ) {
752
				
753
				$namespace = "";
754
				
755
			} else {
756
			
757
				if ( $wgContLang->getNsText( intval( $namespace, 10 ) ) ) {
758
					
759
					$namespace = $wgContLang->getNsText( intval( $namespace, 10 ) );
760
				}
761
			
762
			}
763
		}
764
		
765
		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...
766
767
			if ( count( $dataObj[$x] ) > 1 ) {
768
				
769
				$pageCell = array_shift(  $dataObj[$x] );
770
				
771
				if ( $pageCell !== "" ) {
772
					
773
					if ( $namespace === "" ) {
774
						$pagetitle =  $pageCell;
775
					} else {
776
						$pagetitle = $namespace . ':' . $pageCell;
777
					}	
778
					
779
				}
780
				
781
				if ( ! $dataHash[ $pagetitle ] ) {
782
					$dataHash[ $pagetitle ] = array();
0 ignored issues
show
Bug introduced by
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...
783
				}
784
				
785
				array_push( $dataHash[ $pagetitle ], $dataObj[$x] );
786
				
787
			}
788
			
789
		}
790
		
791
		foreach ( $dataHash as $pagetitle => $dataArray ) {
792
			
793
			$jsonSubObj = array();
794
			$jsonSubObj["data"] = $dataArray;
795
			$jsonSubObj["meta"] = $jsonObj["meta"];
796
797
			self::importJSON( json_encode( $jsonSubObj ), $pagetitle, $overwrite );
798
			
799
		}
800
		
801
		return true;
802
803
	}
804
805
	/** Import of JSON into a page, let's say, at commit **/
806
	/**
807
	* @text Bulk data text
808
	* @pagetitle Title of the page
809
	* @return status of update
810
	*/
811
	public static function importJSON( $text, $pagetitle, $overwrite=false) {
812
813
		$title = Title::newFromText( $pagetitle );
814
		$wikipage = WikiPage::factory( $title );
815
		$goahead = true;
816
		$status = false;
817
		// Check if exists
818
		if ( $wikipage->exists() &&  ! $overwrite ) {
819
			$goahead = false;
820
		}
821
			if ( $goahead ) {
822
				// Check compatibility. Only if newer versions of MW
823
				if ( method_exists ( $wikipage, "getContent" ) ) {
824
					$contentModel = $wikipage->getContentModel();
825
					if ( $contentModel === "json" || ! $wikipage->exists() ) {
826
						$content = new JSONContent($text);
827
						$status = $wikipage->doEditContent( $content, "Updating content" );
828
					}
829
				}
830
			}
831
		return $status;
832
	}
833
	
834
	/** TODO: TO BE UPDATED **/
835
	public static function prepareStructForJSON( $meta, $data ) {
836
		
837
		$strJSON = "";
838
		
839
		if ( $data ) {
840
			
841
			$obj = array( );
842
			
843
			// TODO: this may change in future versions
844
			$obj["meta"] = array();
845
			$obj["meta"]["app"] = "SDI";
846
			$obj["meta"]["version"] = 0.1;
847
			
848
			if ( $meta ) {
849
				
850
				if ( array_key_exists( "rowfields", $meta ) ) {
851
					$obj["meta"]["rowfields"] = $meta["rowfields"];
852
				}
853
			}
854
			
855
			$obj["data"] = $data;
856
			
857
			$strJSON = json_encode( $obj );
858
		}
859
860
		return $strJSON;
861
	}
862
	
863
864
	/**
865
	* @param $out OutputPage
866
	* @param $text string
867
	* @return $out OutputPage
868
	*/
0 ignored issues
show
Documentation introduced by
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...
869
	
870
	public static function onOutputPageBeforeHTML( &$out, &$text ) {
0 ignored issues
show
Unused Code introduced by
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...
871
872
		// We add Modules
873
		$out->addModules( 'ext.sdimport' );
874
		
875
		global $wgSDImportDataPage;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
876
		
877
		// Get Namespace
878
		
879
		$context = RequestContext::getMain();
880
		if ( $context ) {
881
			$pageTitle = $context->getTitle();
882
			if ( $pageTitle ) {
883
				
884
				$ns = $pageTitle->getNamespace();
885
				
886
				if ( array_key_exists( $ns, $wgSDImportDataPage ) ) {
887
					
888
					if ( array_key_exists( "form", $wgSDImportDataPage[$ns] )  ) {
889
						
890
						if ( $wgSDImportDataPage[$ns]["form"] === true ) {
891
							
892
							// Adding form libraries only if needed
893
							$out->addModules( 'ext.sdimport.form' );
894
							
895
						}
896
					}
897
				}
898
				
899
			}
900
		}
901
		
902
		return $out;
903
	}
904
	
905
	/** This allow PHP vars to be exposed to JavaScript **/
906
	
907
	public static function onResourceLoaderGetConfigVars( &$vars ) {
908
		
909
		global $wgSDImportDataPage;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
910
911
912
		$vars['wgSDImportDataPage'] = $wgSDImportDataPage;
913
914
		return true;
915
	}
916
}
917