r65670 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r65669‎ | r65670 | r65671 >
Date:21:32, 29 April 2010
Author:jeroendedauw
Status:deferred
Tags:
Comment:
CHanges for 0.3 - Partially added dependency handling and support for by meta data validation and formatting
Modified paths:
  • /trunk/extensions/Validator/TopologicalSort.php (modified) (history)
  • /trunk/extensions/Validator/Validator.class.php (modified) (history)
  • /trunk/extensions/Validator/Validator.php (modified) (history)
  • /trunk/extensions/Validator/Validator_Manager.php (modified) (history)

Diff [purge]

Index: trunk/extensions/Validator/Validator.class.php
@@ -20,8 +20,13 @@
2121 *
2222 * @author Jeroen De Dauw
2323 *
24 - * TODO: add a way to use parameter values in the validation/formatting of other parameters.
25 - * This will also require new syntax to order the handling of the defined parameters.
 24+ * TODO: add dependency system
 25+ * * find out how to decompress dependency tree in an efficient way
 26+ * * handle params in the determined order and make meta data available for sucessive ones
 27+ * TODO: provide all original and inferred info about a parameter pair to the validation and formatting functions.
 28+ * this will allow for the special behaviour of the default parameter of display_points in Maps
 29+ * where the actual alias influences the handling
 30+ * TODO: break on fatal errors, such as missing required parameters that are dependencies
2631 */
2732 final class Validator {
2833
@@ -50,6 +55,18 @@
5156 public static $perItemValidation = true;
5257
5358 /**
 59+ * @var mixed The default value for parameters the user did not set and do not have their own
 60+ * default specified.
 61+ */
 62+ public static $defaultDefaultValue = '';
 63+
 64+ /**
 65+ * @var string The default delimiter for lists, used when the parameter definition does not
 66+ * specify one.
 67+ */
 68+ public static $defaultListDelimeter = ',';
 69+
 70+ /**
5471 * @var array Holder for the validation functions.
5572 */
5673 private static $mValidationFunctions = array(
@@ -106,53 +123,59 @@
107124 }
108125
109126 /**
110 - * Sets the raw parameters that will be validated when validateParameters is called.
111 - *
112 - * @param array $parameters
113 - */
114 - public function setParameters( array $parameters ) {
115 - $this->mRawParameters = $parameters;
116 - }
117 -
118 - /**
119127 * Determine all parameter names and value, and take care of default (nameless)
120128 * parameters, by turning them into named ones.
121129 *
122130 * @param array $rawParams
123131 * @param array $defaultParams
 132+ *
 133+ * TODO: retain info about the changing of defaults
124134 */
125135 public function parseAndSetParams( array $rawParams, array $defaultParams = array() ) {
126136 $parameters = array();
127137
128 - foreach( $rawParams as $arg ) {
 138+ $nr = 0;
 139+ $defaultNr = 0;
 140+
 141+ foreach ( $rawParams as $arg ) {
129142 // Only take into account strings. If the value is not a string,
130143 // it is not a raw parameter, and can not be parsed correctly in all cases.
131144 if ( is_string( $arg ) ) {
132145 $parts = explode( '=', $arg );
133146 if ( count( $parts ) == 1 ) {
134147 if ( count( $defaultParams ) > 0 ) {
135 - $defaultParam = array_shift( $defaultParams );
136 - $parameters[$defaultParam] = trim( $parts[0] );
 148+ $defaultParam = array_shift( $defaultParams );
 149+ $parameters[$defaultParam] = array(
 150+ 'value' => trim( $parts[0] ),
 151+ 'default' => $defaultNr,
 152+ 'position' => $nr
 153+ );
 154+ $defaultNr++;
137155 }
138156 } else {
139157 $name = strtolower( trim( array_shift( $parts ) ) );
140 - $parameters[$name] = trim( implode( '=', $parts ) );
141 - }
 158+ $parameters[$name] = array(
 159+ 'value' => trim( implode( '=', $parts ) ),
 160+ 'default' => false,
 161+ 'position' => $nr
 162+ );
 163+ }
142164 }
 165+ $nr++;
143166 }
144167
145 - $this->setParameters( $parameters );
 168+ $this->mRawParameters = $parameters;
146169 }
147170
148171 /**
149 - * Valides the raw parameters, and allocates them as valid, invalid or unknown.
150 - * Errors are collected, and can be retrieved via getErrors.
151 - *
152 - * @return boolean Indicates whether there where NO errors.
 172+ * @new
 173+ *
 174+ * TODO: merge meta data and value arrays
153175 */
154 - public function validateParameters() {
 176+ public function validateAndFormatParameters() {
155177 // Loop through all the user provided parameters, and destinguise between those that are allowed and those that are not.
156 - foreach ( $this->mRawParameters as $paramName => $paramValue ) {
 178+ foreach ( $this->mRawParameters as $paramName => $paramData ) {
 179+ $paramValue = $paramData['value'];
157180 // Attempt to get the main parameter name (takes care of aliases).
158181 $mainName = self::getMainParamName( $paramName );
159182 // If the parameter is found in the list of allowed ones, add it to the $mParameters array.
@@ -160,7 +183,12 @@
161184 // Check for parameter overriding. In most cases, this has already largely been taken care off,
162185 // in the form of later parameters overriding earlier ones. This is not true for different aliases though.
163186 if ( !array_key_exists( $mainName, $this->mParameters ) || self::$acceptOverriding ) {
164 - $this->mParameters[$mainName] = $paramValue;
 187+ $this->mParameters[$mainName] = array(
 188+ 'value' => $paramValue,
 189+ 'original-name' => $paramName,
 190+ 'default' => $paramData['default'],
 191+ 'position' => $paramData['position']
 192+ );
165193 }
166194 else {
167195 $this->errors[] = array( 'type' => 'override', 'name' => $mainName );
@@ -171,16 +199,27 @@
172200 $this->mErrors[] = array( 'type' => 'unknown', 'name' => $paramName );
173201 }
174202 }
 203+
 204+ $dependencyList = array();
 205+
 206+ foreach ( $this->mParameterInfo as $paramName => $paramInfo ) {
 207+ $dependencyList[$paramName] = array_key_exists( 'dependencies', $paramInfo ) ?
 208+ (array)$paramInfo['dependencies'] : array();
 209+ }
 210+
 211+ $sorter = new TopologicalSort( $dependencyList, true );
 212+ $orderedParameters = $sorter->doSort();
175213
176 - // Loop through the list of allowed parameters.
177 - foreach ( $this->mParameterInfo as $paramName => $paramInfo ) {
 214+ foreach ( $orderedParameters as $paramName ) {
 215+ $paramInfo = $this->mParameterInfo[$paramName];
 216+
178217 // If the user provided a value for this parameter, validate and handle it.
179218 if ( array_key_exists( $paramName, $this->mParameters ) ) {
180219
181 - $paramValue = $this->mParameters[$paramName];
 220+ $paramValue = $this->mParameters[$paramName]['value'];
182221 $this->cleanParameter( $paramName, $paramValue );
183222
184 - if ( $this->validateParameter( $paramName, $paramValue ) ) {
 223+ if ( $this->validateParameter( $paramName ) ) {
185224 // If the validation succeeded, add the parameter to the list of valid ones.
186225 $this->mValidParams[$paramName] = $paramValue;
187226 $this->setOutputTypes( $this->mValidParams[$paramName], $paramInfo );
@@ -197,13 +236,11 @@
198237 }
199238 else {
200239 // Set the default value (or default 'default value' if none is provided), and ensure the type is correct.
201 - $this->mValidParams[$paramName] = array_key_exists( 'default', $paramInfo ) ? $paramInfo['default'] : '';
 240+ $this->mValidParams[$paramName] = array_key_exists( 'default', $paramInfo ) ? $paramInfo['default'] : self::$defaultDefaultValue;
202241 $this->setOutputTypes( $this->mValidParams[$paramName], $paramInfo );
203242 }
204243 }
205244 }
206 -
207 - return count( $this->mErrors ) == 0;
208245 }
209246
210247 /**
@@ -233,7 +270,7 @@
234271 }
235272
236273 /**
237 - * Ensures the parameter info is valid, trims the value, and splits lists.
 274+ * Ensures the parameter info is valid, and splits lists.
238275 *
239276 * @param string $name
240277 * @param $value
@@ -260,10 +297,10 @@
261298 break;
262299 case 'float':
263300 $this->addTypeCriteria( $name, 'is_float' );
264 - break;
 301+ break;
265302 case 'number': // Note: This accepts non-decimal notations!
266303 $this->addTypeCriteria( $name, 'is_numeric' );
267 - break;
 304+ break;
268305 case 'boolean':
269306 // TODO: work with list of true and false values.
270307 // TODO: i18n
@@ -277,7 +314,7 @@
278315
279316 if ( count( $this->mParameterInfo[$name]['type'] ) > 1 && $this->mParameterInfo[$name]['type'][1] == 'list' ) {
280317 // Trimming and splitting of list values.
281 - $delimiter = count( $this->mParameterInfo[$name]['type'] ) > 2 ? $this->mParameterInfo[$name]['type'][2] : ',';
 318+ $delimiter = count( $this->mParameterInfo[$name]['type'] ) > 2 ? $this->mParameterInfo[$name]['type'][2] : self::$defaultListDelimeter;
282319 $value = preg_replace( '/((\s)*' . $delimiter . '(\s)*)/', $delimiter, $value );
283320 $value = explode( $delimiter, $value );
284321 }
@@ -285,10 +322,6 @@
286323 // Trimming of array values.
287324 for ( $i = count( $value ); $i > 0; $i-- ) $value[$i] = trim ( $value[$i] );
288325 }
289 - else {
290 - // Trimming of non-list values.
291 - $value = trim ( $value );
292 - }
293326 }
294327
295328 private function addTypeCriteria( $paramName, $criteriaName, $criteriaArgs = array() ) {
@@ -299,20 +332,23 @@
300333 * Valides the provided parameter by matching the value against the list and item criteria for the name.
301334 *
302335 * @param string $name
303 - * @param string $value
304336 *
305337 * @return boolean Indicates whether there the parameter value(s) is/are valid.
 338+ *
 339+ * TODO: value was byref arg for some reason - this could break stuff
306340 */
307 - private function validateParameter( $name, &$value ) {
 341+ private function validateParameter( $name ) {
308342 $hasNoErrors = true;
309343 $checkItemCriteria = true;
310344
 345+ $value = $this->mParameters[$name]['value'];
 346+
311347 if ( array_key_exists( 'list-criteria', $this->mParameterInfo[$name] ) ) {
312348 foreach ( $this->mParameterInfo[$name]['list-criteria'] as $criteriaName => $criteriaArgs ) {
313349 // Get the validation function. If there is no matching function, throw an exception.
314350 if ( array_key_exists( $criteriaName, self::$mListValidationFunctions ) ) {
315351 $validationFunction = self::$mListValidationFunctions[$criteriaName];
316 - $isValid = $this->doCriteriaValidation( $validationFunction, $value, $criteriaArgs );
 352+ $isValid = $this->doCriteriaValidation( $validationFunction, $value, array(), $criteriaArgs );
317353
318354 // Add a new error when the validation failed, and break the loop if errors for one parameter should not be accumulated.
319355 if ( ! $isValid ) {
@@ -362,7 +398,7 @@
363399
364400 // Loop through all the items in the parameter value, and validate them.
365401 foreach ( $value as $item ) {
366 - $isValid = $this->doCriteriaValidation( $validationFunction, $item, $criteriaArgs );
 402+ $isValid = $this->doCriteriaValidation( $validationFunction, $item, array(), $criteriaArgs );
367403 if ( $isValid ) {
368404 // If per item validation is on, store the valid items, so only these can be returned by Validator.
369405 if ( self::$perItemValidation ) $validItems[] = $item;
@@ -393,16 +429,16 @@
394430 }
395431 else {
396432 // Determine if the value is valid for single valued parameters.
397 - $isValid = $this->doCriteriaValidation( $validationFunction, $value, $criteriaArgs );
 433+ $isValid = $this->doCriteriaValidation( $validationFunction, $value, array(), $criteriaArgs );
398434 }
399435
400436 // Add a new error when the validation failed, and break the loop if errors for one parameter should not be accumulated.
401 - if ( ! $isValid ) {
 437+ if ( !$isValid ) {
402438 $isList = is_array( $value );
403439 if ( $isList ) $value = $this->mRawParameters[$name];
404440 $this->mErrors[] = array( 'type' => $criteriaName, 'args' => $criteriaArgs, 'name' => $name, 'list' => $isList, 'value' => $value );
405441 $hasNoErrors = false;
406 - if ( ! self::$accumulateParameterErrors ) break;
 442+ if ( !self::$accumulateParameterErrors ) break;
407443 }
408444 }
409445 else {
@@ -419,13 +455,14 @@
420456 *
421457 * @param $validationFunction
422458 * @param $value
423 - * @param $criteriaArgs
 459+ * @param array $metaData
 460+ * @param array $criteriaArgs
424461 *
425462 * @return boolean
426463 */
427 - private function doCriteriaValidation( $validationFunction, $value, $criteriaArgs ) {
 464+ private function doCriteriaValidation( $validationFunction, $value, array $metaData, array $criteriaArgs ) {
428465 // Call the validation function and store the result.
429 - return call_user_func_array( $validationFunction, array_merge( array( $value ), $criteriaArgs ) );
 466+ return call_user_func_array( $validationFunction, array_merge( array_merge( array( $value ), $metaData), $criteriaArgs ) );
430467 }
431468
432469 /**
Index: trunk/extensions/Validator/Validator_Manager.php
@@ -20,7 +20,7 @@
2121 *
2222 * @author Jeroen De Dauw
2323 *
24 - * FIXME: missing params should result in a no-go, no matter of the error level, as they can/are not defaulted.
 24+ * FIXME: missing required params should result in a no-go, no matter of the error level, as they can/are not defaulted.
2525 *
2626 * TODO: make a distinction between fatal errors and regular errors by using 2 seperate error levels.
2727 */
@@ -45,7 +45,7 @@
4646 $validator->setParameterInfo( $parameterInfo );
4747 $validator->parseAndSetParams( $rawParameters, $defaultParams );
4848
49 - $hasNoErrors = $validator->validateParameters();
 49+ $hasNoErrors = $validator->validateAndFormatParameters();
5050 $hasFatalError = $hasNoErrors ? false : $this->hasFatalError();
5151
5252 if ( !$hasNoErrors ) {
@@ -75,7 +75,7 @@
7676 $has = true;
7777 break;
7878 }
79 - }
 79+ }
8080 }
8181
8282 return $has;
Index: trunk/extensions/Validator/TopologicalSort.php
@@ -7,7 +7,7 @@
88 *
99 * usage:
1010 * $t = new TopologicalSort($dependency_pairs);
11 - * $load_order = $t->tsort();
 11+ * $load_order = $t->doSort();
1212 *
1313 * where dependency_pairs is in the form:
1414 * $name => (depends on) $value
@@ -20,79 +20,92 @@
2121 * TODO: Use in revised version of Validator class
2222 */
2323 class TopologicalSort {
24 - var $nodes = array ();
2524
 25+ private $nodes = array();
 26+ private $looseNodes = array();
 27+
2628 /**
2729 * Dependency pairs are a list of arrays in the form
2830 * $name => $val where $key must come before $val in load order.
2931 */
30 - function TopologicalSort($dependencies = array(), $parse = false) {
31 - if ($parse) {
32 - $dependencies = $this->parseDependencyList ( $dependencies );
 32+ function TopologicalSort( $dependencies = array(), $parse = true ) {
 33+ $rawDependencies = $dependencies;
 34+
 35+ if ( $parse ) {
 36+ $dependencies = $this->parseDependencyList( $dependencies );
3337 }
34 -
 38+
 39+ // Store items that have no dependencies, and therefore no nodes.
 40+ foreach( $rawDependencies as $item => $dependency ) {
 41+ if ( !in_array( $item, $dependencies ) && !array_key_exists( $item, $dependencies ) ) {
 42+ $this->looseNodes[] = $item;
 43+ }
 44+ }
 45+
3546 // turn pairs into double-linked node tree
3647 foreach ( $dependencies as $key => $dpair ) {
3748 list ( $module, $dependency ) = each ( $dpair );
38 - if ( !isset ( $this->nodes [$module] )) $this->nodes [$module] = new TSNode ( $module );
39 - if ( !isset ( $this->nodes [$dependency] )) $this->nodes [$dependency] = new TSNode ( $dependency );
40 - if ( !in_array ( $dependency, $this->nodes [$module]->children )) $this->nodes [$module]->children [] = $dependency;
41 - if ( !in_array ( $module, $this->nodes [$dependency]->parents )) $this->nodes [$dependency]->parents [] = $module;
 49+ if ( !isset( $this->nodes[$module] ) ) $this->nodes[$module] = new TSNode( $module );
 50+ if ( !isset( $this->nodes[$dependency] ) ) $this->nodes[$dependency] = new TSNode( $dependency );
 51+ if ( !in_array( $dependency, $this->nodes[$module]->children ) ) $this->nodes[$module]->children[] = $dependency;
 52+ if ( !in_array( $module, $this->nodes[$dependency]->parents ) ) $this->nodes[$dependency]->parents[] = $module;
4253 }
4354 }
4455
4556 /**
46 - * Perform Topological Sort
 57+ * Perform Topological Sort.
4758 *
4859 * @param array $nodes optional array of node objects may be passed.
4960 * Default is $this->nodes created in constructor.
5061 *
5162 * @return sorted array
5263 */
53 - function tsort($nodes = array()) {
 64+ public function doSort( array $nodes = array() ) {
5465 // use this->nodes if it is populated and no param passed
55 - if (! @count ( $nodes ) && count ( $this->nodes )) {
 66+ if ( !count( $nodes ) && count( $this->nodes ) ) {
5667 $nodes = $this->nodes;
57 - }
 68+ }
5869
59 -
6070 // get nodes without parents
61 - $root_nodes = array_values ( $this->getRootNodes ( $nodes ) );
 71+ $root_nodes = array_values( $this->getRootNodes( $nodes ) );
6272
6373 // begin algorithm
64 - $sorted = array ();
65 - while ( count ( $nodes ) > 0 ) {
 74+ $sorted = array();
 75+ while ( count( $nodes ) > 0 ) {
6676 // check for circular reference
67 - if (count ( $root_nodes ) == 0)
68 - return false;
 77+ if ( count( $root_nodes ) == 0 ) return false;
6978
 79+
7080 // remove this node from root_nodes
7181 // and add it to the output
72 - $n = array_pop ( $root_nodes );
73 - $sorted [] = $n->name;
 82+ $n = array_pop( $root_nodes );
 83+ $sorted[] = $n->name;
7484
7585 // for each of its children
7686 // queue the new node finally remove the original
77 - for($i = (count ( $n->children ) - 1); $i >= 0; $i --) {
78 - $childnode = $n->children [$i];
 87+ for ( $i = count( $n->children ) - 1; $i >= 0; $i -- ) {
 88+ $childnode = $n->children[$i];
7989 // remove the link from this node to its
8090 // children ($nodes[$n->name]->children[$i]) AND
8191 // remove the link from each child to this
8292 // parent ($nodes[$childnode]->parents[?]) THEN
8393 // remove this child from this node
84 - unset ( $nodes [$n->name]->children [$i] );
85 - $parent_position = array_search ( $n->name, $nodes [$childnode]->parents );
86 - unset ( $nodes [$childnode]->parents [$parent_position] );
 94+ unset( $nodes[$n->name]->children[$i] );
 95+ $parent_position = array_search ( $n->name, $nodes[$childnode]->parents );
 96+ unset( $nodes[$childnode]->parents[$parent_position] );
8797 // check if this child has other parents
8898 // if not, add it to the root nodes list
89 - if (! count ( $nodes [$childnode]->parents ))
90 - array_push ( $root_nodes, $nodes [$childnode] );
 99+ if ( !count( $nodes[$childnode]->parents ) ) {
 100+ array_push( $root_nodes, $nodes [$childnode] );
 101+ }
91102 }
92103
93104 // nodes.Remove(n);
94 - unset ( $nodes [$n->name] );
 105+ unset( $nodes[$n->name] );
95106 }
96 - return $sorted;
 107+
 108+ // Return the result with the loose nodes (items with no dependencies) appended.
 109+ return array_merge( $sorted, $this->looseNodes );
97110 }
98111
99112 /**
@@ -102,11 +115,11 @@
103116 *
104117 * @return array of node objects
105118 */
106 - function getRootNodes( array $nodes ) {
 119+ private function getRootNodes( array $nodes ) {
107120 $output = array ();
108121
109122 foreach ( $nodes as $name => $node ) {
110 - if ( !count ( $node->parents )) {
 123+ if ( !count ( $node->parents ) ) {
111124 $output[$name] = $node;
112125 }
113126 }
@@ -124,11 +137,11 @@
125138 * ...etc
126139 * );
127140 *
128 - * @param array $dlist Array of dependency pairs for use as parameter in tsort method
 141+ * @param array $dlist Array of dependency pairs for use as parameter in doSort method
129142 *
130143 * @return array
131144 */
132 - function parseDependencyList( array $dlist = array() ) {
 145+ private function parseDependencyList( array $dlist = array() ) {
133146 $output = array();
134147
135148 foreach ( $dlist as $name => $dependencies ) {
@@ -149,7 +162,7 @@
150163 public $children = array();
151164 public $parents = array();
152165
153 - function TSNode( $name = '' ) {
 166+ public function TSNode( $name = '' ) {
154167 $this->name = $name;
155168 }
156169 }
\ No newline at end of file
Index: trunk/extensions/Validator/Validator.php
@@ -24,7 +24,7 @@
2525 die( 'Not an entry point.' );
2626 }
2727
28 -define( 'Validator_VERSION', '0.3 a2' );
 28+define( 'Validator_VERSION', '0.3 a3' );
2929
3030 // Constants indicating the strictness of the parameter validation.
3131 define( 'Validator_ERRORS_NONE', 0 );
@@ -43,11 +43,12 @@
4444 // Register the internationalization file.
4545 $wgExtensionMessagesFiles['Validator'] = $egValidatorDir . 'Validator.i18n.php';
4646
47 -// Autoload the general classes
 47+// Autoload the general classes.
4848 $wgAutoloadClasses['Validator'] = $egValidatorDir . 'Validator.class.php';
4949 $wgAutoloadClasses['ValidatorFunctions'] = $egValidatorDir . 'Validator_Functions.php';
5050 $wgAutoloadClasses['ValidatorFormats'] = $egValidatorDir . 'Validator_Formats.php';
5151 $wgAutoloadClasses['ValidatorManager'] = $egValidatorDir . 'Validator_Manager.php';
 52+$wgAutoloadClasses['TopologicalSort'] = $egValidatorDir . 'TopologicalSort.php';
5253
5354 /**
5455 * Initialization function for the Validator extension.

Follow-up revisions

RevisionCommit summaryAuthorDate
r65675Follow up to r65670jeroendedauw22:11, 29 April 2010
r65712Fixed bug 23356. Follow up to r65670.jeroendedauw19:50, 30 April 2010
r65713Fixed bug 23356. Follow up to r65670.jeroendedauw19:50, 30 April 2010
r65781Follow up to r65670jeroendedauw04:34, 2 May 2010

Status & tagging log