Index: trunk/extensions/DataTransclusion/tests/DataTransclusionTest.php |
— | — | @@ -403,7 +403,7 @@ |
404 | 404 | 'url' => 'http://acme.com/{name}', |
405 | 405 | 'dataFormat' => 'php', |
406 | 406 | 'dataPath' => 'response/content/@0', |
407 | | - 'valuePath' => 'value', |
| 407 | + 'fieldPathes' => array( 'id' => 'id/value', 'name' => 'name/value', 'info' => 'info/value', ), |
408 | 408 | 'errorPath' => 'response/error', |
409 | 409 | ); |
410 | 410 | |
— | — | @@ -420,7 +420,7 @@ |
421 | 421 | |
422 | 422 | $rec = array( |
423 | 423 | "name" => array( 'type' => 'string', 'value' => "foo" ), |
424 | | - "id" => array( 'type' => 'int', 'value' => 3 ), |
| 424 | + "id" => 3, |
425 | 425 | "info" => array( 'type' => 'string', 'value' => "test X" ), |
426 | 426 | ); |
427 | 427 | |
— | — | @@ -438,6 +438,9 @@ |
439 | 439 | $this->assertEquals( $err, 'test error' ); |
440 | 440 | $this->assertEquals( $rec['id'], 3 ); |
441 | 441 | |
| 442 | + //TODO: test extractField, with fancy snytax! |
| 443 | + //TODO: test flattenRecord |
| 444 | + |
442 | 445 | //////////////////////// |
443 | 446 | $spec['url'] = 'file://' . dirname( realpath( __FILE__ ) ) . '/test-data-name-{name}.pser'; |
444 | 447 | $spec['dataFormat'] = 'php'; |
Index: trunk/extensions/DataTransclusion/tests/fetchRecord.php |
— | — | @@ -0,0 +1,31 @@ |
| 2 | +<?php |
| 3 | +if ( isset( $GET_ ) ) { |
| 4 | + echo( "This file cannot be run from the web.\n" ); |
| 5 | + die( 1 ); |
| 6 | +} |
| 7 | + |
| 8 | +if ( getenv( 'MW_INSTALL_PATH' ) ) { |
| 9 | + $IP = getenv( 'MW_INSTALL_PATH' ); |
| 10 | +} else { |
| 11 | + $dir = dirname( __FILE__ ); |
| 12 | + |
| 13 | + if ( file_exists( "$dir/../../LocalSettings.php" ) ) $IP = "$dir/../.."; |
| 14 | + else if ( file_exists( "$dir/../../../LocalSettings.php" ) ) $IP = "$dir/../../.."; |
| 15 | + else if ( file_exists( "$dir/../../phase3/LocalSettings.php" ) ) $IP = "$dir/../../phase3"; |
| 16 | + else if ( file_exists( "$dir/../../../phase3/LocalSettings.php" ) ) $IP = "$dir/../../../phase3"; |
| 17 | + else $IP = $dir; |
| 18 | +} |
| 19 | + |
| 20 | +require_once( "$IP/maintenance/commandLine.inc" ); |
| 21 | + |
| 22 | +$dir = dirname( __FILE__ ); |
| 23 | +$dtbase = dirname( realpath( $dir ) ); |
| 24 | + |
| 25 | +$src = $args[0]; |
| 26 | +$field = $args[1]; |
| 27 | +$value = $args[2]; |
| 28 | + |
| 29 | +$source = DataTransclusionHandler::getDataSource( $src ); |
| 30 | +$data = $source->fetchRecord( $field, $value, null ); |
| 31 | + |
| 32 | +print_r( $data ); |
\ No newline at end of file |
Property changes on: trunk/extensions/DataTransclusion/tests/fetchRecord.php |
___________________________________________________________________ |
Name: svn:mergeinfo |
1 | 33 | + |
Name: svn:eol-style |
2 | 34 | + native |
Index: trunk/extensions/DataTransclusion/WebDataTransclusionSource.php |
— | — | @@ -38,10 +38,14 @@ |
39 | 39 | * a "meta-key" of the form @@N, where N is an integer. A meta-key refers to the |
40 | 40 | * Nth entry in an associative array: @1 would be "bar" in array( 'x' => "foo", 'y' => "bar" ). |
41 | 41 | * For more complex retrieval of the record, override extractRecord(). REQUIRED. |
42 | | - * * $spec['valuePath']: "path" to the actual field values inside the record associated |
43 | | - * with each field. Optional, should only be specified if field values are returned |
44 | | - * as complex records instead of simple values. For more complex processing, override |
45 | | - * the method sanitizeRecord(). |
| 42 | + * * $spec['fieldPathes']: an associative array giving a "path" for each fied which points |
| 43 | + * to the actual field values inside the record, that is, the structure that |
| 44 | + * $spec['dataPath'] resolved to. Useful when field values are returned as complex |
| 45 | + * records. For more complex processing, override the method flattenRecord(). |
| 46 | + * If given, $spec['fieldNames'] defaults to array_keys( $spec['fieldPathes'] ). |
| 47 | + * * $spec['fieldNames']: names of all fields present in each record. |
| 48 | + * Fields not listed here will not be available on the wiki, |
| 49 | + * even if they are returned by the data source. Required if fieldPathes is not given. |
46 | 50 | * * $spec['errorPath']: "path" to error messages in the structure returned from the |
47 | 51 | * HTTP request. The path is evaluated as deswcribed for $spec['dataPath']. If an |
48 | 52 | * entry is found at the given position in the response structure, the request |
— | — | @@ -57,16 +61,26 @@ |
58 | 62 | class WebDataTransclusionSource extends DataTransclusionSource { |
59 | 63 | |
60 | 64 | function __construct( $spec ) { |
| 65 | + if ( !isset( $spec['fieldNames'] ) && isset( $spec['fieldPathes'] ) ) { |
| 66 | + $spec['fieldNames'] = array_keys( $spec['fieldPathes'] ); |
| 67 | + } |
| 68 | + |
61 | 69 | DataTransclusionSource::__construct( $spec ); |
62 | 70 | |
63 | 71 | $this->url = $spec[ 'url' ]; |
64 | 72 | $this->dataFormat = @$spec[ 'dataFormat' ]; |
65 | | - $this->dataPath = DataTransclusionSource::splitList( @$spec[ 'dataPath' ] ); |
66 | | - $this->valuePath = DataTransclusionSource::splitList( @$spec[ 'valuePath' ] ); |
67 | | - $this->errorPath = DataTransclusionSource::splitList( @$spec[ 'errorPath' ] ); |
| 73 | + $this->dataPath = DataTransclusionSource::splitList( @$spec[ 'dataPath' ], '/' ); |
| 74 | + $this->fieldPathes = @$spec[ 'fieldPathes' ]; |
| 75 | + $this->errorPath = DataTransclusionSource::splitList( @$spec[ 'errorPath' ], '/' ); |
68 | 76 | $this->httpOptions = @$spec[ 'httpOptions' ]; |
69 | 77 | $this->timeout = @$spec[ 'timeout' ]; |
70 | 78 | |
| 79 | + if ( $this->fieldPathes ) { |
| 80 | + foreach ( $this->fieldPathes as $i => $p ) { |
| 81 | + $this->fieldPathes[ $i ] = DataTransclusionSource::splitList( $p, '/' ); |
| 82 | + } |
| 83 | + } |
| 84 | + |
71 | 85 | if ( !$this->dataFormat ) { |
72 | 86 | $this->dataFormat = 'php'; |
73 | 87 | } |
— | — | @@ -170,6 +184,7 @@ |
171 | 185 | } |
172 | 186 | |
173 | 187 | if ( $format == 'json' || $format == 'js' ) { |
| 188 | + $raw = preg_replace( '/^\s*(var\s)?\w([\w\d]*)\s+=\s*|\s*;\s*$/sim', '', $raw); |
174 | 189 | return FormatJson::decode( $raw, true ); |
175 | 190 | } |
176 | 191 | |
— | — | @@ -191,23 +206,25 @@ |
192 | 207 | public function extractRecord( $data ) { |
193 | 208 | $rec = $this->extractField( $data, $this->dataPath ); |
194 | 209 | |
195 | | - $rec = $this->sanitizeRecord( $rec ); |
| 210 | + $rec = $this->flattenRecord( $rec ); |
196 | 211 | return $rec; |
197 | 212 | } |
198 | 213 | |
199 | | - public function sanitizeRecord( $rec ) { |
200 | | - if ( $this->valuePath !== null && $this->valuePath !== false ) { |
| 214 | + public function flattenRecord( $rec ) { |
| 215 | + if ( !$rec ) return $rec; |
| 216 | + |
| 217 | + if ( $this->fieldPathes ) { |
201 | 218 | $r = array(); |
202 | 219 | |
203 | | - foreach ( $rec as $k => $v ) { |
204 | | - if ( is_array( $v ) || is_object( $v ) ) { |
205 | | - $w = $this->extractField( $v, $this->valuePath ); |
206 | | - //XXX: how to hanlde $w === false failures here? |
| 220 | + foreach ( $this->fieldNames as $k ) { |
| 221 | + if ( isset( $this->fieldPathes[$k] ) ) { |
| 222 | + $path = $this->fieldPathes[$k]; |
| 223 | + $v = $this->extractField( $rec, $path ); |
207 | 224 | } else { |
208 | | - $w = $v; //XXX: ugly default. fail instead?? |
| 225 | + $v = $rec[ $k ]; |
209 | 226 | } |
210 | 227 | |
211 | | - $r[ $k ] = $w; |
| 228 | + $r[ $k ] = $v; |
212 | 229 | } |
213 | 230 | |
214 | 231 | return $r; |
— | — | @@ -217,34 +234,69 @@ |
218 | 235 | } |
219 | 236 | |
220 | 237 | public function extractField( $data, $path ) { |
221 | | - if ( $path == null ) { |
222 | | - return $data; |
| 238 | + if ( is_object( $data ) ) { |
| 239 | + $data = wfObjectToArray( $data ); |
223 | 240 | } |
224 | 241 | |
225 | | - if ( is_string( $path ) ) { |
| 242 | + if ( !is_array( $data ) || $path === '.' ) { |
| 243 | + return $data; |
| 244 | + } |
| 245 | + |
| 246 | + if ( is_string( $path ) || is_int( $path ) ) { |
226 | 247 | return @$data[ $path ]; |
227 | 248 | } |
228 | 249 | |
229 | | - foreach ( $path as $p ) { |
230 | | - if ( is_object( $data ) ) { |
231 | | - $data = wfObjectToArray( $data ); |
| 250 | + if ( !$path ) { |
| 251 | + return $data; |
| 252 | + } |
| 253 | + |
| 254 | + $p = array_shift( $path ); |
| 255 | + |
| 256 | + if ( strpos( $p, '|' ) ) { //alternatives |
| 257 | + $alternatives = explode( '|', $p ); |
| 258 | + foreach ( $alternatives as $a ) { |
| 259 | + $ap = array_merge( array( $a ), $path ); |
| 260 | + $v = $this->extractField( $data, $ap ); |
| 261 | + |
| 262 | + if ( $v !== null && $v !== false ) { |
| 263 | + return $v; |
| 264 | + } |
232 | 265 | } |
| 266 | + } else if ( is_string( $p ) && preg_match( '/^\(([^\w\d])\)$/', $p, $m ) ) { //concat all |
| 267 | + $s = ""; |
| 268 | + foreach ( $data as $d ) { |
| 269 | + $v = $this->extractField( $d, $path ); |
233 | 270 | |
234 | | - // meta-key: index in the list of array-keys. |
235 | | - // e.g. use @0 to grab the first value from an assoc array. |
236 | | - if ( is_string( $p ) && preg_match( '/^@(\d+)$/', $p, $m ) ) { |
237 | | - $i = (int)$m[1]; |
238 | | - $k = array_keys( $data ); |
239 | | - $p = $k[ $i ]; |
| 271 | + if ( $v !== null && $v !== false ) { |
| 272 | + if ( $s != "" ) $s .= $m[1]; |
| 273 | + $s .= $v; |
| 274 | + } |
240 | 275 | } |
241 | 276 | |
| 277 | + return $s; |
| 278 | + } else { |
| 279 | + if ( is_string( $p ) && preg_match( '/^(@)?(\d+)$/', $p, $m ) ) { //numberic index |
| 280 | + $i = (int)$m[2]; |
| 281 | + |
| 282 | + if ( $m[1] ) { //meta-index |
| 283 | + $k = array_keys( $data ); |
| 284 | + $p = $k[ $i ]; |
| 285 | + } |
| 286 | + } |
| 287 | + |
242 | 288 | if ( !isset( $data[ $p ] ) ) { |
243 | 289 | return false; |
244 | 290 | } |
245 | 291 | |
246 | | - $data = $data[ $p ]; |
| 292 | + $next = $data[ $p ]; |
| 293 | + |
| 294 | + if ( $next && $path ) { |
| 295 | + return $this->extractField( $next, $path ); |
| 296 | + } else { |
| 297 | + return $next; |
| 298 | + } |
247 | 299 | } |
248 | 300 | |
249 | | - return $data; |
| 301 | + //TODO: named components. separator?? |
250 | 302 | } |
251 | 303 | } |
Index: trunk/extensions/DataTransclusion/DataTransclusionSource.php |
— | — | @@ -54,7 +54,7 @@ |
55 | 55 | * Lists may be given as arrays or strings with items separated by [,;|]. |
56 | 56 | */ |
57 | 57 | class DataTransclusionSource { |
58 | | - static function splitList( $s ) { |
| 58 | + static function splitList( $s, $chars = ',;|' ) { |
59 | 59 | if ( $s === null || $s === false ) { |
60 | 60 | return $s; |
61 | 61 | } |
— | — | @@ -63,7 +63,7 @@ |
64 | 64 | return $s; |
65 | 65 | } |
66 | 66 | |
67 | | - $list = preg_split( '!\s*[,;|/]\s*!', $s ); |
| 67 | + $list = preg_split( '!\s*[' . $chars . ']\s*!', $s ); |
68 | 68 | |
69 | 69 | return $list; |
70 | 70 | } |