MediaWiki  1.23.2
ConfEditor.php
Go to the documentation of this file.
1 <?php
38 class ConfEditor {
40  var $text;
41 
43  var $tokens;
44 
46  var $pos;
47 
49  var $lineNum;
50 
52  var $colNum;
53 
55  var $byteNum;
56 
59 
62 
68 
86 
91  var $pathInfo;
92 
96  var $serial;
97 
102  var $edits;
103 
111  static function test( $text ) {
112  try {
113  $ce = new self( $text );
114  $ce->parse();
115  } catch ( ConfEditorParseError $e ) {
116  return $e->getMessage() . "\n" . $e->highlight( $text );
117  }
118 
119  return "OK";
120  }
121 
125  public function __construct( $text ) {
126  $this->text = $text;
127  }
128 
165  public function edit( $ops ) {
166  $this->parse();
167 
168  $this->edits = array(
169  array( 'copy', 0, strlen( $this->text ) )
170  );
171  foreach ( $ops as $op ) {
172  $type = $op['type'];
173  $path = $op['path'];
174  $value = isset( $op['value'] ) ? $op['value'] : null;
175  $key = isset( $op['key'] ) ? $op['key'] : null;
176 
177  switch ( $type ) {
178  case 'delete':
179  list( $start, $end ) = $this->findDeletionRegion( $path );
180  $this->replaceSourceRegion( $start, $end, false );
181  break;
182  case 'set':
183  if ( isset( $this->pathInfo[$path] ) ) {
184  list( $start, $end ) = $this->findValueRegion( $path );
185  $encValue = $value; // var_export( $value, true );
186  $this->replaceSourceRegion( $start, $end, $encValue );
187  break;
188  }
189  // No existing path, fall through to append
190  $slashPos = strrpos( $path, '/' );
191  $key = var_export( substr( $path, $slashPos + 1 ), true );
192  $path = substr( $path, 0, $slashPos );
193  // Fall through
194  case 'append':
195  // Find the last array element
196  $lastEltPath = $this->findLastArrayElement( $path );
197  if ( $lastEltPath === false ) {
198  throw new MWException( "Can't find any element of array \"$path\"" );
199  }
200  $lastEltInfo = $this->pathInfo[$lastEltPath];
201 
202  // Has it got a comma already?
203  if ( strpos( $lastEltPath, '@extra' ) === false && !$lastEltInfo['hasComma'] ) {
204  // No comma, insert one after the value region
205  list( , $end ) = $this->findValueRegion( $lastEltPath );
206  $this->replaceSourceRegion( $end - 1, $end - 1, ',' );
207  }
208 
209  // Make the text to insert
210  list( $start, $end ) = $this->findDeletionRegion( $lastEltPath );
211 
212  if ( $key === null ) {
213  list( $indent, ) = $this->getIndent( $start );
214  $textToInsert = "$indent$value,";
215  } else {
216  list( $indent, $arrowIndent ) =
217  $this->getIndent( $start, $key, $lastEltInfo['arrowByte'] );
218  $textToInsert = "$indent$key$arrowIndent=> $value,";
219  }
220  $textToInsert .= ( $indent === false ? ' ' : "\n" );
221 
222  // Insert the item
223  $this->replaceSourceRegion( $end, $end, $textToInsert );
224  break;
225  case 'insert':
226  // Find first array element
227  $firstEltPath = $this->findFirstArrayElement( $path );
228  if ( $firstEltPath === false ) {
229  throw new MWException( "Can't find array element of \"$path\"" );
230  }
231  list( $start, ) = $this->findDeletionRegion( $firstEltPath );
232  $info = $this->pathInfo[$firstEltPath];
233 
234  // Make the text to insert
235  if ( $key === null ) {
236  list( $indent, ) = $this->getIndent( $start );
237  $textToInsert = "$indent$value,";
238  } else {
239  list( $indent, $arrowIndent ) =
240  $this->getIndent( $start, $key, $info['arrowByte'] );
241  $textToInsert = "$indent$key$arrowIndent=> $value,";
242  }
243  $textToInsert .= ( $indent === false ? ' ' : "\n" );
244 
245  // Insert the item
246  $this->replaceSourceRegion( $start, $start, $textToInsert );
247  break;
248  default:
249  throw new MWException( "Unrecognised operation: \"$type\"" );
250  }
251  }
252 
253  // Do the edits
254  $out = '';
255  foreach ( $this->edits as $edit ) {
256  if ( $edit[0] == 'copy' ) {
257  $out .= substr( $this->text, $edit[1], $edit[2] - $edit[1] );
258  } else { // if ( $edit[0] == 'insert' )
259  $out .= $edit[1];
260  }
261  }
262 
263  // Do a second parse as a sanity check
264  $this->text = $out;
265  try {
266  $this->parse();
267  } catch ( ConfEditorParseError $e ) {
268  throw new MWException(
269  "Sorry, ConfEditor broke the file during editing and it won't parse anymore: " .
270  $e->getMessage() );
271  }
272 
273  return $out;
274  }
275 
280  function getVars() {
281  $vars = array();
282  $this->parse();
283  foreach ( $this->pathInfo as $path => $data ) {
284  if ( $path[0] != '$' ) {
285  continue;
286  }
287  $trimmedPath = substr( $path, 1 );
288  $name = $data['name'];
289  if ( $name[0] == '@' ) {
290  continue;
291  }
292  if ( $name[0] == '$' ) {
293  $name = substr( $name, 1 );
294  }
295  $parentPath = substr( $trimmedPath, 0,
296  strlen( $trimmedPath ) - strlen( $name ) );
297  if ( substr( $parentPath, -1 ) == '/' ) {
298  $parentPath = substr( $parentPath, 0, -1 );
299  }
300 
301  $value = substr( $this->text, $data['valueStartByte'],
302  $data['valueEndByte'] - $data['valueStartByte']
303  );
304  $this->setVar( $vars, $parentPath, $name,
305  $this->parseScalar( $value ) );
306  }
307 
308  return $vars;
309  }
310 
320  function setVar( &$array, $path, $key, $value ) {
321  $pathArr = explode( '/', $path );
322  $target =& $array;
323  if ( $path !== '' ) {
324  foreach ( $pathArr as $p ) {
325  if ( !isset( $target[$p] ) ) {
326  $target[$p] = array();
327  }
328  $target =& $target[$p];
329  }
330  }
331  if ( !isset( $target[$key] ) ) {
332  $target[$key] = $value;
333  }
334  }
335 
340  function parseScalar( $str ) {
341  if ( $str !== '' && $str[0] == '\'' ) {
342  // Single-quoted string
343  // @todo FIXME: trim() call is due to mystery bug where whitespace gets
344  // appended to the token; without it we ended up reading in the
345  // extra quote on the end!
346  return strtr( substr( trim( $str ), 1, -1 ),
347  array( '\\\'' => '\'', '\\\\' => '\\' ) );
348  }
349  if ( $str !== '' && $str[0] == '"' ) {
350  // Double-quoted string
351  // @todo FIXME: trim() call is due to mystery bug where whitespace gets
352  // appended to the token; without it we ended up reading in the
353  // extra quote on the end!
354  return stripcslashes( substr( trim( $str ), 1, -1 ) );
355  }
356  if ( substr( $str, 0, 4 ) == 'true' ) {
357  return true;
358  }
359  if ( substr( $str, 0, 5 ) == 'false' ) {
360  return false;
361  }
362  if ( substr( $str, 0, 4 ) == 'null' ) {
363  return null;
364  }
365 
366  // Must be some kind of numeric value, so let PHP's weak typing
367  // be useful for a change
368  return $str;
369  }
370 
375  function replaceSourceRegion( $start, $end, $newText = false ) {
376  // Split all copy operations with a source corresponding to the region
377  // in question.
378  $newEdits = array();
379  foreach ( $this->edits as $edit ) {
380  if ( $edit[0] !== 'copy' ) {
381  $newEdits[] = $edit;
382  continue;
383  }
384  $copyStart = $edit[1];
385  $copyEnd = $edit[2];
386  if ( $start >= $copyEnd || $end <= $copyStart ) {
387  // Outside this region
388  $newEdits[] = $edit;
389  continue;
390  }
391  if ( ( $start < $copyStart && $end > $copyStart )
392  || ( $start < $copyEnd && $end > $copyEnd )
393  ) {
394  throw new MWException( "Overlapping regions found, can't do the edit" );
395  }
396  // Split the copy
397  $newEdits[] = array( 'copy', $copyStart, $start );
398  if ( $newText !== false ) {
399  $newEdits[] = array( 'insert', $newText );
400  }
401  $newEdits[] = array( 'copy', $end, $copyEnd );
402  }
403  $this->edits = $newEdits;
404  }
405 
414  function findDeletionRegion( $pathName ) {
415  if ( !isset( $this->pathInfo[$pathName] ) ) {
416  throw new MWException( "Can't find path \"$pathName\"" );
417  }
418  $path = $this->pathInfo[$pathName];
419  // Find the start
420  $this->firstToken();
421  while ( $this->pos != $path['startToken'] ) {
422  $this->nextToken();
423  }
424  $regionStart = $path['startByte'];
425  for ( $offset = -1; $offset >= -$this->pos; $offset-- ) {
426  $token = $this->getTokenAhead( $offset );
427  if ( !$token->isSkip() ) {
428  // If there is other content on the same line, don't move the start point
429  // back, because that will cause the regions to overlap.
430  $regionStart = $path['startByte'];
431  break;
432  }
433  $lfPos = strrpos( $token->text, "\n" );
434  if ( $lfPos === false ) {
435  $regionStart -= strlen( $token->text );
436  } else {
437  // The line start does not include the LF
438  $regionStart -= strlen( $token->text ) - $lfPos - 1;
439  break;
440  }
441  }
442  // Find the end
443  while ( $this->pos != $path['endToken'] ) {
444  $this->nextToken();
445  }
446  $regionEnd = $path['endByte']; // past the end
447  $count = count( $this->tokens );
448  for ( $offset = 0; $offset < $count - $this->pos; $offset++ ) {
449  $token = $this->getTokenAhead( $offset );
450  if ( !$token->isSkip() ) {
451  break;
452  }
453  $lfPos = strpos( $token->text, "\n" );
454  if ( $lfPos === false ) {
455  $regionEnd += strlen( $token->text );
456  } else {
457  // This should point past the LF
458  $regionEnd += $lfPos + 1;
459  break;
460  }
461  }
462 
463  return array( $regionStart, $regionEnd );
464  }
465 
476  function findValueRegion( $pathName ) {
477  if ( !isset( $this->pathInfo[$pathName] ) ) {
478  throw new MWException( "Can't find path \"$pathName\"" );
479  }
480  $path = $this->pathInfo[$pathName];
481  if ( $path['valueStartByte'] === false || $path['valueEndByte'] === false ) {
482  throw new MWException( "Can't find value region for path \"$pathName\"" );
483  }
484 
485  return array( $path['valueStartByte'], $path['valueEndByte'] );
486  }
487 
495  // Try for a real element
496  $lastEltPath = false;
497  foreach ( $this->pathInfo as $candidatePath => $info ) {
498  $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
499  $part2 = substr( $candidatePath, strlen( $path ) + 1, 1 );
500  if ( $part2 == '@' ) {
501  // Do nothing
502  } elseif ( $part1 == "$path/" ) {
503  $lastEltPath = $candidatePath;
504  } elseif ( $lastEltPath !== false ) {
505  break;
506  }
507  }
508  if ( $lastEltPath !== false ) {
509  return $lastEltPath;
510  }
511 
512  // Try for an interstitial element
513  $extraPath = false;
514  foreach ( $this->pathInfo as $candidatePath => $info ) {
515  $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
516  if ( $part1 == "$path/" ) {
517  $extraPath = $candidatePath;
518  } elseif ( $extraPath !== false ) {
519  break;
520  }
521  }
522 
523  return $extraPath;
524  }
525 
533  // Try for an ordinary element
534  foreach ( $this->pathInfo as $candidatePath => $info ) {
535  $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
536  $part2 = substr( $candidatePath, strlen( $path ) + 1, 1 );
537  if ( $part1 == "$path/" && $part2 != '@' ) {
538  return $candidatePath;
539  }
540  }
541 
542  // Try for an interstitial element
543  foreach ( $this->pathInfo as $candidatePath => $info ) {
544  $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
545  if ( $part1 == "$path/" ) {
546  return $candidatePath;
547  }
548  }
549 
550  return false;
551  }
552 
558  function getIndent( $pos, $key = false, $arrowPos = false ) {
559  $arrowIndent = ' ';
560  if ( $pos == 0 || $this->text[$pos - 1] == "\n" ) {
561  $indentLength = strspn( $this->text, " \t", $pos );
562  $indent = substr( $this->text, $pos, $indentLength );
563  } else {
564  $indent = false;
565  }
566  if ( $indent !== false && $arrowPos !== false ) {
567  $arrowIndentLength = $arrowPos - $pos - $indentLength - strlen( $key );
568  if ( $arrowIndentLength > 0 ) {
569  $arrowIndent = str_repeat( ' ', $arrowIndentLength );
570  }
571  }
572 
573  return array( $indent, $arrowIndent );
574  }
575 
580  public function parse() {
581  $this->initParse();
582  $this->pushState( 'file' );
583  $this->pushPath( '@extra-' . ( $this->serial++ ) );
584  $token = $this->firstToken();
585 
586  while ( !$token->isEnd() ) {
587  $state = $this->popState();
588  if ( !$state ) {
589  $this->error( 'internal error: empty state stack' );
590  }
591 
592  switch ( $state ) {
593  case 'file':
594  $this->expect( T_OPEN_TAG );
595  $token = $this->skipSpace();
596  if ( $token->isEnd() ) {
597  break 2;
598  }
599  $this->pushState( 'statement', 'file 2' );
600  break;
601  case 'file 2':
602  $token = $this->skipSpace();
603  if ( $token->isEnd() ) {
604  break 2;
605  }
606  $this->pushState( 'statement', 'file 2' );
607  break;
608  case 'statement':
609  $token = $this->skipSpace();
610  if ( !$this->validatePath( $token->text ) ) {
611  $this->error( "Invalid variable name \"{$token->text}\"" );
612  }
613  $this->nextPath( $token->text );
614  $this->expect( T_VARIABLE );
615  $this->skipSpace();
616  $arrayAssign = false;
617  if ( $this->currentToken()->type == '[' ) {
618  $this->nextToken();
619  $token = $this->skipSpace();
620  if ( !$token->isScalar() ) {
621  $this->error( "expected a string or number for the array key" );
622  }
623  if ( $token->type == T_CONSTANT_ENCAPSED_STRING ) {
624  $text = $this->parseScalar( $token->text );
625  } else {
626  $text = $token->text;
627  }
628  if ( !$this->validatePath( $text ) ) {
629  $this->error( "Invalid associative array name \"$text\"" );
630  }
631  $this->pushPath( $text );
632  $this->nextToken();
633  $this->skipSpace();
634  $this->expect( ']' );
635  $this->skipSpace();
636  $arrayAssign = true;
637  }
638  $this->expect( '=' );
639  $this->skipSpace();
640  $this->startPathValue();
641  if ( $arrayAssign ) {
642  $this->pushState( 'expression', 'array assign end' );
643  } else {
644  $this->pushState( 'expression', 'statement end' );
645  }
646  break;
647  case 'array assign end':
648  case 'statement end':
649  $this->endPathValue();
650  if ( $state == 'array assign end' ) {
651  $this->popPath();
652  }
653  $this->skipSpace();
654  $this->expect( ';' );
655  $this->nextPath( '@extra-' . ( $this->serial++ ) );
656  break;
657  case 'expression':
658  $token = $this->skipSpace();
659  if ( $token->type == T_ARRAY ) {
660  $this->pushState( 'array' );
661  } elseif ( $token->isScalar() ) {
662  $this->nextToken();
663  } elseif ( $token->type == T_VARIABLE ) {
664  $this->nextToken();
665  } else {
666  $this->error( "expected simple expression" );
667  }
668  break;
669  case 'array':
670  $this->skipSpace();
671  $this->expect( T_ARRAY );
672  $this->skipSpace();
673  $this->expect( '(' );
674  $this->skipSpace();
675  $this->pushPath( '@extra-' . ( $this->serial++ ) );
676  if ( $this->isAhead( ')' ) ) {
677  // Empty array
678  $this->pushState( 'array end' );
679  } else {
680  $this->pushState( 'element', 'array end' );
681  }
682  break;
683  case 'array end':
684  $this->skipSpace();
685  $this->popPath();
686  $this->expect( ')' );
687  break;
688  case 'element':
689  $token = $this->skipSpace();
690  // Look ahead to find the double arrow
691  if ( $token->isScalar() && $this->isAhead( T_DOUBLE_ARROW, 1 ) ) {
692  // Found associative element
693  $this->pushState( 'assoc-element', 'element end' );
694  } else {
695  // Not associative
696  $this->nextPath( '@next' );
697  $this->startPathValue();
698  $this->pushState( 'expression', 'element end' );
699  }
700  break;
701  case 'element end':
702  $token = $this->skipSpace();
703  if ( $token->type == ',' ) {
704  $this->endPathValue();
705  $this->markComma();
706  $this->nextToken();
707  $this->nextPath( '@extra-' . ( $this->serial++ ) );
708  // Look ahead to find ending bracket
709  if ( $this->isAhead( ")" ) ) {
710  // Found ending bracket, no continuation
711  $this->skipSpace();
712  } else {
713  // No ending bracket, continue to next element
714  $this->pushState( 'element' );
715  }
716  } elseif ( $token->type == ')' ) {
717  // End array
718  $this->endPathValue();
719  } else {
720  $this->error( "expected the next array element or the end of the array" );
721  }
722  break;
723  case 'assoc-element':
724  $token = $this->skipSpace();
725  if ( !$token->isScalar() ) {
726  $this->error( "expected a string or number for the array key" );
727  }
728  if ( $token->type == T_CONSTANT_ENCAPSED_STRING ) {
729  $text = $this->parseScalar( $token->text );
730  } else {
731  $text = $token->text;
732  }
733  if ( !$this->validatePath( $text ) ) {
734  $this->error( "Invalid associative array name \"$text\"" );
735  }
736  $this->nextPath( $text );
737  $this->nextToken();
738  $this->skipSpace();
739  $this->markArrow();
740  $this->expect( T_DOUBLE_ARROW );
741  $this->skipSpace();
742  $this->startPathValue();
743  $this->pushState( 'expression' );
744  break;
745  }
746  }
747  if ( count( $this->stateStack ) ) {
748  $this->error( 'unexpected end of file' );
749  }
750  $this->popPath();
751  }
752 
756  protected function initParse() {
757  $this->tokens = token_get_all( $this->text );
758  $this->stateStack = array();
759  $this->pathStack = array();
760  $this->firstToken();
761  $this->pathInfo = array();
762  $this->serial = 1;
763  }
764 
769  protected function setPos( $pos ) {
770  $this->pos = $pos;
771  if ( $this->pos >= count( $this->tokens ) ) {
773  } else {
774  $this->currentToken = $this->newTokenObj( $this->tokens[$this->pos] );
775  }
776 
777  return $this->currentToken;
778  }
779 
784  function newTokenObj( $internalToken ) {
785  if ( is_array( $internalToken ) ) {
786  return new ConfEditorToken( $internalToken[0], $internalToken[1] );
787  } else {
788  return new ConfEditorToken( $internalToken, $internalToken );
789  }
790  }
791 
795  function firstToken() {
796  $this->setPos( 0 );
798  $this->lineNum = 1;
799  $this->colNum = 1;
800  $this->byteNum = 0;
801 
802  return $this->currentToken;
803  }
804 
808  function currentToken() {
809  return $this->currentToken;
810  }
811 
815  function nextToken() {
816  if ( $this->currentToken ) {
817  $text = $this->currentToken->text;
818  $lfCount = substr_count( $text, "\n" );
819  if ( $lfCount ) {
820  $this->lineNum += $lfCount;
821  $this->colNum = strlen( $text ) - strrpos( $text, "\n" );
822  } else {
823  $this->colNum += strlen( $text );
824  }
825  $this->byteNum += strlen( $text );
826  }
828  $this->setPos( $this->pos + 1 );
829 
830  return $this->currentToken;
831  }
832 
838  function getTokenAhead( $offset ) {
839  $pos = $this->pos + $offset;
840  if ( $pos >= count( $this->tokens ) || $pos < 0 ) {
841  return ConfEditorToken::newEnd();
842  } else {
843  return $this->newTokenObj( $this->tokens[$pos] );
844  }
845  }
846 
850  function skipSpace() {
851  while ( $this->currentToken && $this->currentToken->isSkip() ) {
852  $this->nextToken();
853  }
854 
855  return $this->currentToken;
856  }
857 
862  function expect( $type ) {
863  if ( $this->currentToken && $this->currentToken->type == $type ) {
864  return $this->nextToken();
865  } else {
866  $this->error( "expected " . $this->getTypeName( $type ) .
867  ", got " . $this->getTypeName( $this->currentToken->type ) );
868  }
869  }
870 
874  function pushState( $nextState, $stateAfterThat = null ) {
875  if ( $stateAfterThat !== null ) {
876  $this->stateStack[] = $stateAfterThat;
877  }
878  $this->stateStack[] = $nextState;
879  }
880 
885  function popState() {
886  return array_pop( $this->stateStack );
887  }
888 
894  function validatePath( $path ) {
895  return strpos( $path, '/' ) === false && substr( $path, 0, 1 ) != '@';
896  }
897 
902  function endPath() {
903  $key = '';
904  foreach ( $this->pathStack as $pathInfo ) {
905  if ( $key !== '' ) {
906  $key .= '/';
907  }
908  $key .= $pathInfo['name'];
909  }
910  $pathInfo['endByte'] = $this->byteNum;
911  $pathInfo['endToken'] = $this->pos;
912  $this->pathInfo[$key] = $pathInfo;
913  }
914 
918  function pushPath( $path ) {
919  $this->pathStack[] = array(
920  'name' => $path,
921  'level' => count( $this->pathStack ) + 1,
922  'startByte' => $this->byteNum,
923  'startToken' => $this->pos,
924  'valueStartToken' => false,
925  'valueStartByte' => false,
926  'valueEndToken' => false,
927  'valueEndByte' => false,
928  'nextArrayIndex' => 0,
929  'hasComma' => false,
930  'arrowByte' => false
931  );
932  }
933 
937  function popPath() {
938  $this->endPath();
939  array_pop( $this->pathStack );
940  }
941 
947  function nextPath( $path ) {
948  $this->endPath();
949  $i = count( $this->pathStack ) - 1;
950  if ( $path == '@next' ) {
951  $nextArrayIndex =& $this->pathStack[$i]['nextArrayIndex'];
952  $this->pathStack[$i]['name'] = $nextArrayIndex;
953  $nextArrayIndex++;
954  } else {
955  $this->pathStack[$i]['name'] = $path;
956  }
957  $this->pathStack[$i] =
958  array(
959  'startByte' => $this->byteNum,
960  'startToken' => $this->pos,
961  'valueStartToken' => false,
962  'valueStartByte' => false,
963  'valueEndToken' => false,
964  'valueEndByte' => false,
965  'hasComma' => false,
966  'arrowByte' => false,
967  ) + $this->pathStack[$i];
968  }
969 
973  function startPathValue() {
974  $path =& $this->pathStack[count( $this->pathStack ) - 1];
975  $path['valueStartToken'] = $this->pos;
976  $path['valueStartByte'] = $this->byteNum;
977  }
978 
982  function endPathValue() {
983  $path =& $this->pathStack[count( $this->pathStack ) - 1];
984  $path['valueEndToken'] = $this->pos;
985  $path['valueEndByte'] = $this->byteNum;
986  }
987 
991  function markComma() {
992  $path =& $this->pathStack[count( $this->pathStack ) - 1];
993  $path['hasComma'] = true;
994  }
995 
999  function markArrow() {
1000  $path =& $this->pathStack[count( $this->pathStack ) - 1];
1001  $path['arrowByte'] = $this->byteNum;
1002  }
1003 
1007  function error( $msg ) {
1008  throw new ConfEditorParseError( $this, $msg );
1009  }
1010 
1015  function getTypeName( $type ) {
1016  if ( is_int( $type ) ) {
1017  return token_name( $type );
1018  } else {
1019  return "\"$type\"";
1020  }
1021  }
1022 
1029  function isAhead( $type, $offset = 0 ) {
1030  $ahead = $offset;
1031  $token = $this->getTokenAhead( $offset );
1032  while ( !$token->isEnd() ) {
1033  if ( $token->isSkip() ) {
1034  $ahead++;
1035  $token = $this->getTokenAhead( $ahead );
1036  continue;
1037  } elseif ( $token->type == $type ) {
1038  // Found the type
1039  return true;
1040  } else {
1041  // Not found
1042  return false;
1043  }
1044  }
1045 
1046  return false;
1047  }
1048 
1052  function prevToken() {
1053  return $this->prevToken;
1054  }
1055 
1059  function dumpTokens() {
1060  $out = '';
1061  foreach ( $this->tokens as $token ) {
1062  $obj = $this->newTokenObj( $token );
1063  $out .= sprintf( "%-28s %s\n",
1064  $this->getTypeName( $obj->type ),
1065  addcslashes( $obj->text, "\0..\37" ) );
1066  }
1067  echo "<pre>" . htmlspecialchars( $out ) . "</pre>";
1068  }
1069 }
1070 
1076 
1077  function __construct( $editor, $msg ) {
1078  $this->lineNum = $editor->lineNum;
1079  $this->colNum = $editor->colNum;
1080  parent::__construct( "Parse error on line {$editor->lineNum} " .
1081  "col {$editor->colNum}: $msg" );
1082  }
1083 
1084  function highlight( $text ) {
1085  $lines = StringUtils::explode( "\n", $text );
1086  foreach ( $lines as $lineNum => $line ) {
1087  if ( $lineNum == $this->lineNum - 1 ) {
1088  return "$line\n" . str_repeat( ' ', $this->colNum - 1 ) . "^\n";
1089  }
1090  }
1091 
1092  return '';
1093  }
1094 }
1095 
1100  var $type, $text;
1101 
1102  static $scalarTypes = array( T_LNUMBER, T_DNUMBER, T_STRING, T_CONSTANT_ENCAPSED_STRING );
1103  static $skipTypes = array( T_WHITESPACE, T_COMMENT, T_DOC_COMMENT );
1104 
1105  static function newEnd() {
1106  return new self( 'END', '' );
1107  }
1108 
1109  function __construct( $type, $text ) {
1110  $this->type = $type;
1111  $this->text = $text;
1112  }
1113 
1114  function isSkip() {
1115  return in_array( $this->type, self::$skipTypes );
1116  }
1117 
1118  function isScalar() {
1119  return in_array( $this->type, self::$scalarTypes );
1120  }
1121 
1122  function isEnd() {
1123  return $this->type == 'END';
1124  }
1125 }
ConfEditorToken\newEnd
static newEnd()
Definition: ConfEditor.php:1105
ConfEditor\pushPath
pushPath( $path)
Go up to a new path level, for example at the start of an array.
Definition: ConfEditor.php:918
ConfEditorParseError\__construct
__construct( $editor, $msg)
Definition: ConfEditor.php:1077
ConfEditor\markComma
markComma()
Mark the comma separator in an array element.
Definition: ConfEditor.php:991
ConfEditorToken\isSkip
isSkip()
Definition: ConfEditor.php:1114
ConfEditor\initParse
initParse()
Initialise a parse.
Definition: ConfEditor.php:756
ConfEditor\$tokens
$tokens
The token array from token_get_all()
Definition: ConfEditor.php:43
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
ConfEditor\popState
popState()
Pop a state from the state stack.
Definition: ConfEditor.php:885
ConfEditorToken\isScalar
isScalar()
Definition: ConfEditor.php:1118
ConfEditor\findDeletionRegion
findDeletionRegion( $pathName)
Finds the source byte region which you would want to delete, if $pathName was to be deleted.
Definition: ConfEditor.php:414
ConfEditorParseError
Exception class for parse errors.
Definition: ConfEditor.php:1074
ConfEditorToken
Class to wrap a token from the tokenizer.
Definition: ConfEditor.php:1099
ConfEditor\$stateStack
$stateStack
The state machine stack.
Definition: ConfEditor.php:67
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
ConfEditor\$pathStack
$pathStack
The path stack is a stack of associative arrays with the following elements: name The name of top lev...
Definition: ConfEditor.php:85
ConfEditor\nextPath
nextPath( $path)
Go to the next path on the same level.
Definition: ConfEditor.php:947
ConfEditor\parse
parse()
Run the parser on the text.
Definition: ConfEditor.php:580
ConfEditor\$prevToken
$prevToken
The previous ConfEditorToken object.
Definition: ConfEditor.php:61
ConfEditor\$colNum
$colNum
The current 1-based column number.
Definition: ConfEditor.php:52
ConfEditor\isAhead
isAhead( $type, $offset=0)
Looks ahead to see if the given type is the next token type, starting from the current position plus ...
Definition: ConfEditor.php:1029
ConfEditorToken\$text
$text
Definition: ConfEditor.php:1100
ConfEditor\$pathInfo
$pathInfo
The elements of the top of the pathStack for every path encountered, indexed by slash-separated path.
Definition: ConfEditor.php:91
ConfEditorParseError\$lineNum
$lineNum
Definition: ConfEditor.php:1075
ConfEditorToken\$skipTypes
static $skipTypes
Definition: ConfEditor.php:1103
ConfEditorToken\__construct
__construct( $type, $text)
Definition: ConfEditor.php:1109
ConfEditor\getTokenAhead
getTokenAhead( $offset)
Get the token $offset steps ahead of the current position.
Definition: ConfEditor.php:838
ConfEditor\$pos
$pos
The current position in the token array.
Definition: ConfEditor.php:46
ConfEditor\$edits
$edits
Editor state.
Definition: ConfEditor.php:102
ConfEditor\test
static test( $text)
Simple entry point for command-line testing.
Definition: ConfEditor.php:111
edits
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global then executing the whole list after the page is displayed We don t do anything smart like collating updates to the same table or such because the list is almost always going to have just one item on if so it s not worth the trouble Since there is a job queue in the jobs which is used to update link tables of transcluding pages after edits
Definition: deferred.txt:11
ConfEditor\setVar
setVar(&$array, $path, $key, $value)
Set a value in an array, unless it's set already.
Definition: ConfEditor.php:320
ConfEditor\findValueRegion
findValueRegion( $pathName)
Find the byte region in the source corresponding to the value part.
Definition: ConfEditor.php:476
MWException
MediaWiki exception.
Definition: MWException.php:26
ConfEditorToken\$scalarTypes
static $scalarTypes
Definition: ConfEditor.php:1102
$out
$out
Definition: UtfNormalGenerate.php:167
ConfEditor\markArrow
markArrow()
Mark the arrow separator in an associative array element.
Definition: ConfEditor.php:999
ConfEditor\endPath
endPath()
Internal function to update some things at the end of a path region.
Definition: ConfEditor.php:902
$editor
in this case you re responsible for computing and outputting the entire conflict i the difference between revisions and your text headers and sections & $editor
Definition: hooks.txt:1038
ConfEditor\edit
edit( $ops)
Edit the text.
Definition: ConfEditor.php:165
ConfEditor\$byteNum
$byteNum
The current 0-based byte number.
Definition: ConfEditor.php:55
ConfEditor\findFirstArrayElement
findFirstArrayElement( $path)
Find the path name of first element in the array.
Definition: ConfEditor.php:532
StringUtils\explode
static explode( $separator, $subject)
Workalike for explode() with limited memory usage.
Definition: StringUtils.php:310
ConfEditor\skipSpace
skipSpace()
Advances the current position past any whitespace or comments.
Definition: ConfEditor.php:850
ConfEditor\popPath
popPath()
Go down a path level, for example at the end of an array.
Definition: ConfEditor.php:937
ConfEditor\startPathValue
startPathValue()
Mark the start of the value part of a path.
Definition: ConfEditor.php:973
ConfEditor\error
error( $msg)
Generate a parse error.
Definition: ConfEditor.php:1007
ConfEditorParseError\$colNum
$colNum
Definition: ConfEditor.php:1075
$lines
$lines
Definition: router.php:65
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
ConfEditor\$text
$text
The text to parse.
Definition: ConfEditor.php:40
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
ConfEditorToken\$type
$type
Definition: ConfEditor.php:1100
$line
$line
Definition: cdb.php:57
ConfEditor\getTypeName
getTypeName( $type)
Get a readable name for the given token type.
Definition: ConfEditor.php:1015
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
$value
$value
Definition: styleTest.css.php:45
ConfEditorParseError\highlight
highlight( $text)
Definition: ConfEditor.php:1084
ConfEditor\getVars
getVars()
Get the variables defined in the text.
Definition: ConfEditor.php:280
ConfEditor\getIndent
getIndent( $pos, $key=false, $arrowPos=false)
Get the indent string which sits after a given start position.
Definition: ConfEditor.php:558
ConfEditor
This is a state machine style parser with two internal stacks:
Definition: ConfEditor.php:38
ConfEditor\dumpTokens
dumpTokens()
Echo a reasonably readable representation of the tokenizer array.
Definition: ConfEditor.php:1059
$count
$count
Definition: UtfNormalTest2.php:96
ConfEditor\__construct
__construct( $text)
Construct a new parser.
Definition: ConfEditor.php:125
ConfEditor\endPathValue
endPathValue()
Mark the end of the value part of a path.
Definition: ConfEditor.php:982
ConfEditor\$currentToken
$currentToken
The current ConfEditorToken object.
Definition: ConfEditor.php:58
ConfEditor\nextToken
nextToken()
Advance the current position and return the resulting next token.
Definition: ConfEditor.php:815
type
This document describes the state of Postgres support in and is fairly well maintained The main code is very well while extensions are very hit and miss it is probably the most supported database after MySQL Much of the work in making MediaWiki database agnostic came about through the work of creating Postgres as and are nearing end of but without copying over all the usage comments General notes on the but these can almost always be programmed around *Although Postgres has a true BOOLEAN type
Definition: postgres.txt:22
ConfEditor\setPos
setPos( $pos)
Set the parse position.
Definition: ConfEditor.php:769
ConfEditor\replaceSourceRegion
replaceSourceRegion( $start, $end, $newText=false)
Replace the byte offset region of the source with $newText.
Definition: ConfEditor.php:375
ConfEditor\parseScalar
parseScalar( $str)
Parse a scalar value in PHP.
Definition: ConfEditor.php:340
ConfEditor\firstToken
firstToken()
Reset the parse position.
Definition: ConfEditor.php:795
ConfEditor\validatePath
validatePath( $path)
Returns true if the user input path is valid.
Definition: ConfEditor.php:894
$path
$path
Definition: NoLocalSettings.php:35
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
ConfEditor\currentToken
currentToken()
Get the current token.
Definition: ConfEditor.php:808
ConfEditor\findLastArrayElement
findLastArrayElement( $path)
Find the path name of the last element in the array.
Definition: ConfEditor.php:494
ConfEditor\expect
expect( $type)
Throws an error if the current token is not of the given type, and then advances to the next position...
Definition: ConfEditor.php:862
ConfEditor\$serial
$serial
Next serial number for whitespace placeholder paths (@extra-N)
Definition: ConfEditor.php:96
ConfEditor\newTokenObj
newTokenObj( $internalToken)
Create a ConfEditorToken from an element of token_get_all()
Definition: ConfEditor.php:784
$vars
static configuration should be added through ResourceLoaderGetConfigVars instead & $vars
Definition: hooks.txt:1679
$e
if( $useReadline) $e
Definition: eval.php:66
ConfEditor\pushState
pushState( $nextState, $stateAfterThat=null)
Push a state or two on to the state stack.
Definition: ConfEditor.php:874
ConfEditor\prevToken
prevToken()
Get the previous token object.
Definition: ConfEditor.php:1052
ConfEditor\$lineNum
$lineNum
The current 1-based line number.
Definition: ConfEditor.php:49
ConfEditorToken\isEnd
isEnd()
Definition: ConfEditor.php:1122
$type
$type
Definition: testCompression.php:46