MediaWiki  REL1_31
FSFileBackend.php
Go to the documentation of this file.
1 <?php
23 use Wikimedia\Timestamp\ConvertibleTimestamp;
24 
43  protected $basePath;
44 
46  protected $containerPaths = [];
47 
49  protected $fileMode;
51  protected $dirMode;
52 
54  protected $fileOwner;
55 
57  protected $isWindows;
59  protected $currentUser;
60 
62  protected $hadWarningErrors = [];
63 
74  public function __construct( array $config ) {
75  parent::__construct( $config );
76 
77  $this->isWindows = ( strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN' );
78  // Remove any possible trailing slash from directories
79  if ( isset( $config['basePath'] ) ) {
80  $this->basePath = rtrim( $config['basePath'], '/' ); // remove trailing slash
81  } else {
82  $this->basePath = null; // none; containers must have explicit paths
83  }
84 
85  if ( isset( $config['containerPaths'] ) ) {
86  $this->containerPaths = (array)$config['containerPaths'];
87  foreach ( $this->containerPaths as &$path ) {
88  $path = rtrim( $path, '/' ); // remove trailing slash
89  }
90  }
91 
92  $this->fileMode = isset( $config['fileMode'] ) ? $config['fileMode'] : 0644;
93  $this->dirMode = isset( $config['directoryMode'] ) ? $config['directoryMode'] : 0777;
94  if ( isset( $config['fileOwner'] ) && function_exists( 'posix_getuid' ) ) {
95  $this->fileOwner = $config['fileOwner'];
96  // cache this, assuming it doesn't change
97  $this->currentUser = posix_getpwuid( posix_getuid() )['name'];
98  }
99  }
100 
101  public function getFeatures() {
102  if ( $this->isWindows && version_compare( PHP_VERSION, '7.1', 'lt' ) ) {
103  // PHP before 7.1 used 8-bit code page for filesystem paths on Windows;
104  // See http://php.net/manual/en/migration71.windows-support.php
105  return 0;
106  } else {
108  }
109  }
110 
111  protected function resolveContainerPath( $container, $relStoragePath ) {
112  // Check that container has a root directory
113  if ( isset( $this->containerPaths[$container] ) || isset( $this->basePath ) ) {
114  // Check for sane relative paths (assume the base paths are OK)
115  if ( $this->isLegalRelPath( $relStoragePath ) ) {
116  return $relStoragePath;
117  }
118  }
119 
120  return null;
121  }
122 
129  protected function isLegalRelPath( $path ) {
130  // Check for file names longer than 255 chars
131  if ( preg_match( '![^/]{256}!', $path ) ) { // ext3/NTFS
132  return false;
133  }
134  if ( $this->isWindows ) { // NTFS
135  return !preg_match( '![:*?"<>|]!', $path );
136  } else {
137  return true;
138  }
139  }
140 
149  protected function containerFSRoot( $shortCont, $fullCont ) {
150  if ( isset( $this->containerPaths[$shortCont] ) ) {
151  return $this->containerPaths[$shortCont];
152  } elseif ( isset( $this->basePath ) ) {
153  return "{$this->basePath}/{$fullCont}";
154  }
155 
156  return null; // no container base path defined
157  }
158 
165  protected function resolveToFSPath( $storagePath ) {
166  list( $fullCont, $relPath ) = $this->resolveStoragePathReal( $storagePath );
167  if ( $relPath === null ) {
168  return null; // invalid
169  }
170  list( , $shortCont, ) = FileBackend::splitStoragePath( $storagePath );
171  $fsPath = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
172  if ( $relPath != '' ) {
173  $fsPath .= "/{$relPath}";
174  }
175 
176  return $fsPath;
177  }
178 
179  public function isPathUsableInternal( $storagePath ) {
180  $fsPath = $this->resolveToFSPath( $storagePath );
181  if ( $fsPath === null ) {
182  return false; // invalid
183  }
184  $parentDir = dirname( $fsPath );
185 
186  if ( file_exists( $fsPath ) ) {
187  $ok = is_file( $fsPath ) && is_writable( $fsPath );
188  } else {
189  $ok = is_dir( $parentDir ) && is_writable( $parentDir );
190  }
191 
192  if ( $this->fileOwner !== null && $this->currentUser !== $this->fileOwner ) {
193  $ok = false;
194  trigger_error( __METHOD__ . ": PHP process owner is not '{$this->fileOwner}'." );
195  }
196 
197  return $ok;
198  }
199 
200  protected function doCreateInternal( array $params ) {
201  $status = $this->newStatus();
202 
203  $dest = $this->resolveToFSPath( $params['dst'] );
204  if ( $dest === null ) {
205  $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
206 
207  return $status;
208  }
209 
210  if ( !empty( $params['async'] ) ) { // deferred
211  $tempFile = TempFSFile::factory( 'create_', 'tmp', $this->tmpDirectory );
212  if ( !$tempFile ) {
213  $status->fatal( 'backend-fail-create', $params['dst'] );
214 
215  return $status;
216  }
217  $this->trapWarnings();
218  $bytes = file_put_contents( $tempFile->getPath(), $params['content'] );
219  $this->untrapWarnings();
220  if ( $bytes === false ) {
221  $status->fatal( 'backend-fail-create', $params['dst'] );
222 
223  return $status;
224  }
225  $cmd = implode( ' ', [
226  $this->isWindows ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
227  escapeshellarg( $this->cleanPathSlashes( $tempFile->getPath() ) ),
228  escapeshellarg( $this->cleanPathSlashes( $dest ) )
229  ] );
230  $handler = function ( $errors, StatusValue $status, array $params, $cmd ) {
231  if ( $errors !== '' && !( $this->isWindows && $errors[0] === " " ) ) {
232  $status->fatal( 'backend-fail-create', $params['dst'] );
233  trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
234  }
235  };
236  $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd, $dest );
237  $tempFile->bind( $status->value );
238  } else { // immediate write
239  $this->trapWarnings();
240  $bytes = file_put_contents( $dest, $params['content'] );
241  $this->untrapWarnings();
242  if ( $bytes === false ) {
243  $status->fatal( 'backend-fail-create', $params['dst'] );
244 
245  return $status;
246  }
247  $this->chmod( $dest );
248  }
249 
250  return $status;
251  }
252 
253  protected function doStoreInternal( array $params ) {
254  $status = $this->newStatus();
255 
256  $dest = $this->resolveToFSPath( $params['dst'] );
257  if ( $dest === null ) {
258  $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
259 
260  return $status;
261  }
262 
263  if ( !empty( $params['async'] ) ) { // deferred
264  $cmd = implode( ' ', [
265  $this->isWindows ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
266  escapeshellarg( $this->cleanPathSlashes( $params['src'] ) ),
267  escapeshellarg( $this->cleanPathSlashes( $dest ) )
268  ] );
269  $handler = function ( $errors, StatusValue $status, array $params, $cmd ) {
270  if ( $errors !== '' && !( $this->isWindows && $errors[0] === " " ) ) {
271  $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
272  trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
273  }
274  };
275  $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd, $dest );
276  } else { // immediate write
277  $this->trapWarnings();
278  $ok = copy( $params['src'], $dest );
279  $this->untrapWarnings();
280  // In some cases (at least over NFS), copy() returns true when it fails
281  if ( !$ok || ( filesize( $params['src'] ) !== filesize( $dest ) ) ) {
282  if ( $ok ) { // PHP bug
283  unlink( $dest ); // remove broken file
284  trigger_error( __METHOD__ . ": copy() failed but returned true." );
285  }
286  $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
287 
288  return $status;
289  }
290  $this->chmod( $dest );
291  }
292 
293  return $status;
294  }
295 
296  protected function doCopyInternal( array $params ) {
297  $status = $this->newStatus();
298 
299  $source = $this->resolveToFSPath( $params['src'] );
300  if ( $source === null ) {
301  $status->fatal( 'backend-fail-invalidpath', $params['src'] );
302 
303  return $status;
304  }
305 
306  $dest = $this->resolveToFSPath( $params['dst'] );
307  if ( $dest === null ) {
308  $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
309 
310  return $status;
311  }
312 
313  if ( !is_file( $source ) ) {
314  if ( empty( $params['ignoreMissingSource'] ) ) {
315  $status->fatal( 'backend-fail-copy', $params['src'] );
316  }
317 
318  return $status; // do nothing; either OK or bad status
319  }
320 
321  if ( !empty( $params['async'] ) ) { // deferred
322  $cmd = implode( ' ', [
323  $this->isWindows ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
324  escapeshellarg( $this->cleanPathSlashes( $source ) ),
325  escapeshellarg( $this->cleanPathSlashes( $dest ) )
326  ] );
327  $handler = function ( $errors, StatusValue $status, array $params, $cmd ) {
328  if ( $errors !== '' && !( $this->isWindows && $errors[0] === " " ) ) {
329  $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
330  trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
331  }
332  };
333  $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd, $dest );
334  } else { // immediate write
335  $this->trapWarnings();
336  $ok = ( $source === $dest ) ? true : copy( $source, $dest );
337  $this->untrapWarnings();
338  // In some cases (at least over NFS), copy() returns true when it fails
339  if ( !$ok || ( filesize( $source ) !== filesize( $dest ) ) ) {
340  if ( $ok ) { // PHP bug
341  $this->trapWarnings();
342  unlink( $dest ); // remove broken file
343  $this->untrapWarnings();
344  trigger_error( __METHOD__ . ": copy() failed but returned true." );
345  }
346  $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
347 
348  return $status;
349  }
350  $this->chmod( $dest );
351  }
352 
353  return $status;
354  }
355 
356  protected function doMoveInternal( array $params ) {
357  $status = $this->newStatus();
358 
359  $source = $this->resolveToFSPath( $params['src'] );
360  if ( $source === null ) {
361  $status->fatal( 'backend-fail-invalidpath', $params['src'] );
362 
363  return $status;
364  }
365 
366  $dest = $this->resolveToFSPath( $params['dst'] );
367  if ( $dest === null ) {
368  $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
369 
370  return $status;
371  }
372 
373  if ( !is_file( $source ) ) {
374  if ( empty( $params['ignoreMissingSource'] ) ) {
375  $status->fatal( 'backend-fail-move', $params['src'] );
376  }
377 
378  return $status; // do nothing; either OK or bad status
379  }
380 
381  if ( !empty( $params['async'] ) ) { // deferred
382  $cmd = implode( ' ', [
383  $this->isWindows ? 'MOVE /Y' : 'mv', // (overwrite)
384  escapeshellarg( $this->cleanPathSlashes( $source ) ),
385  escapeshellarg( $this->cleanPathSlashes( $dest ) )
386  ] );
387  $handler = function ( $errors, StatusValue $status, array $params, $cmd ) {
388  if ( $errors !== '' && !( $this->isWindows && $errors[0] === " " ) ) {
389  $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
390  trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
391  }
392  };
393  $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd );
394  } else { // immediate write
395  $this->trapWarnings();
396  $ok = ( $source === $dest ) ? true : rename( $source, $dest );
397  $this->untrapWarnings();
398  clearstatcache(); // file no longer at source
399  if ( !$ok ) {
400  $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
401 
402  return $status;
403  }
404  }
405 
406  return $status;
407  }
408 
409  protected function doDeleteInternal( array $params ) {
410  $status = $this->newStatus();
411 
412  $source = $this->resolveToFSPath( $params['src'] );
413  if ( $source === null ) {
414  $status->fatal( 'backend-fail-invalidpath', $params['src'] );
415 
416  return $status;
417  }
418 
419  if ( !is_file( $source ) ) {
420  if ( empty( $params['ignoreMissingSource'] ) ) {
421  $status->fatal( 'backend-fail-delete', $params['src'] );
422  }
423 
424  return $status; // do nothing; either OK or bad status
425  }
426 
427  if ( !empty( $params['async'] ) ) { // deferred
428  $cmd = implode( ' ', [
429  $this->isWindows ? 'DEL' : 'unlink',
430  escapeshellarg( $this->cleanPathSlashes( $source ) )
431  ] );
432  $handler = function ( $errors, StatusValue $status, array $params, $cmd ) {
433  if ( $errors !== '' && !( $this->isWindows && $errors[0] === " " ) ) {
434  $status->fatal( 'backend-fail-delete', $params['src'] );
435  trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
436  }
437  };
438  $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd );
439  } else { // immediate write
440  $this->trapWarnings();
441  $ok = unlink( $source );
442  $this->untrapWarnings();
443  if ( !$ok ) {
444  $status->fatal( 'backend-fail-delete', $params['src'] );
445 
446  return $status;
447  }
448  }
449 
450  return $status;
451  }
452 
459  protected function doPrepareInternal( $fullCont, $dirRel, array $params ) {
460  $status = $this->newStatus();
461  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
462  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
463  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
464  $existed = is_dir( $dir ); // already there?
465  // Create the directory and its parents as needed...
466  $this->trapWarnings();
467  if ( !$existed && !mkdir( $dir, $this->dirMode, true ) && !is_dir( $dir ) ) {
468  $this->logger->error( __METHOD__ . ": cannot create directory $dir" );
469  $status->fatal( 'directorycreateerror', $params['dir'] ); // fails on races
470  } elseif ( !is_writable( $dir ) ) {
471  $this->logger->error( __METHOD__ . ": directory $dir is read-only" );
472  $status->fatal( 'directoryreadonlyerror', $params['dir'] );
473  } elseif ( !is_readable( $dir ) ) {
474  $this->logger->error( __METHOD__ . ": directory $dir is not readable" );
475  $status->fatal( 'directorynotreadableerror', $params['dir'] );
476  }
477  $this->untrapWarnings();
478  // Respect any 'noAccess' or 'noListing' flags...
479  if ( is_dir( $dir ) && !$existed ) {
480  $status->merge( $this->doSecureInternal( $fullCont, $dirRel, $params ) );
481  }
482 
483  return $status;
484  }
485 
486  protected function doSecureInternal( $fullCont, $dirRel, array $params ) {
487  $status = $this->newStatus();
488  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
489  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
490  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
491  // Seed new directories with a blank index.html, to prevent crawling...
492  if ( !empty( $params['noListing'] ) && !file_exists( "{$dir}/index.html" ) ) {
493  $this->trapWarnings();
494  $bytes = file_put_contents( "{$dir}/index.html", $this->indexHtmlPrivate() );
495  $this->untrapWarnings();
496  if ( $bytes === false ) {
497  $status->fatal( 'backend-fail-create', $params['dir'] . '/index.html' );
498  }
499  }
500  // Add a .htaccess file to the root of the container...
501  if ( !empty( $params['noAccess'] ) && !file_exists( "{$contRoot}/.htaccess" ) ) {
502  $this->trapWarnings();
503  $bytes = file_put_contents( "{$contRoot}/.htaccess", $this->htaccessPrivate() );
504  $this->untrapWarnings();
505  if ( $bytes === false ) {
506  $storeDir = "mwstore://{$this->name}/{$shortCont}";
507  $status->fatal( 'backend-fail-create', "{$storeDir}/.htaccess" );
508  }
509  }
510 
511  return $status;
512  }
513 
514  protected function doPublishInternal( $fullCont, $dirRel, array $params ) {
515  $status = $this->newStatus();
516  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
517  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
518  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
519  // Unseed new directories with a blank index.html, to allow crawling...
520  if ( !empty( $params['listing'] ) && is_file( "{$dir}/index.html" ) ) {
521  $exists = ( file_get_contents( "{$dir}/index.html" ) === $this->indexHtmlPrivate() );
522  $this->trapWarnings();
523  if ( $exists && !unlink( "{$dir}/index.html" ) ) { // reverse secure()
524  $status->fatal( 'backend-fail-delete', $params['dir'] . '/index.html' );
525  }
526  $this->untrapWarnings();
527  }
528  // Remove the .htaccess file from the root of the container...
529  if ( !empty( $params['access'] ) && is_file( "{$contRoot}/.htaccess" ) ) {
530  $exists = ( file_get_contents( "{$contRoot}/.htaccess" ) === $this->htaccessPrivate() );
531  $this->trapWarnings();
532  if ( $exists && !unlink( "{$contRoot}/.htaccess" ) ) { // reverse secure()
533  $storeDir = "mwstore://{$this->name}/{$shortCont}";
534  $status->fatal( 'backend-fail-delete', "{$storeDir}/.htaccess" );
535  }
536  $this->untrapWarnings();
537  }
538 
539  return $status;
540  }
541 
542  protected function doCleanInternal( $fullCont, $dirRel, array $params ) {
543  $status = $this->newStatus();
544  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
545  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
546  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
547  $this->trapWarnings();
548  if ( is_dir( $dir ) ) {
549  rmdir( $dir ); // remove directory if empty
550  }
551  $this->untrapWarnings();
552 
553  return $status;
554  }
555 
556  protected function doGetFileStat( array $params ) {
557  $source = $this->resolveToFSPath( $params['src'] );
558  if ( $source === null ) {
559  return false; // invalid storage path
560  }
561 
562  $this->trapWarnings(); // don't trust 'false' if there were errors
563  $stat = is_file( $source ) ? stat( $source ) : false; // regular files only
564  $hadError = $this->untrapWarnings();
565 
566  if ( $stat ) {
567  $ct = new ConvertibleTimestamp( $stat['mtime'] );
568 
569  return [
570  'mtime' => $ct->getTimestamp( TS_MW ),
571  'size' => $stat['size']
572  ];
573  } elseif ( !$hadError ) {
574  return false; // file does not exist
575  } else {
576  return null; // failure
577  }
578  }
579 
580  protected function doClearCache( array $paths = null ) {
581  clearstatcache(); // clear the PHP file stat cache
582  }
583 
584  protected function doDirectoryExists( $fullCont, $dirRel, array $params ) {
585  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
586  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
587  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
588 
589  $this->trapWarnings(); // don't trust 'false' if there were errors
590  $exists = is_dir( $dir );
591  $hadError = $this->untrapWarnings();
592 
593  return $hadError ? null : $exists;
594  }
595 
603  public function getDirectoryListInternal( $fullCont, $dirRel, array $params ) {
604  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
605  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
606  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
607  $exists = is_dir( $dir );
608  if ( !$exists ) {
609  $this->logger->warning( __METHOD__ . "() given directory does not exist: '$dir'\n" );
610 
611  return []; // nothing under this dir
612  } elseif ( !is_readable( $dir ) ) {
613  $this->logger->warning( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
614 
615  return null; // bad permissions?
616  }
617 
618  return new FSFileBackendDirList( $dir, $params );
619  }
620 
628  public function getFileListInternal( $fullCont, $dirRel, array $params ) {
629  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
630  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
631  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
632  $exists = is_dir( $dir );
633  if ( !$exists ) {
634  $this->logger->warning( __METHOD__ . "() given directory does not exist: '$dir'\n" );
635 
636  return []; // nothing under this dir
637  } elseif ( !is_readable( $dir ) ) {
638  $this->logger->warning( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
639 
640  return null; // bad permissions?
641  }
642 
643  return new FSFileBackendFileList( $dir, $params );
644  }
645 
646  protected function doGetLocalReferenceMulti( array $params ) {
647  $fsFiles = []; // (path => FSFile)
648 
649  foreach ( $params['srcs'] as $src ) {
650  $source = $this->resolveToFSPath( $src );
651  if ( $source === null || !is_file( $source ) ) {
652  $fsFiles[$src] = null; // invalid path or file does not exist
653  } else {
654  $fsFiles[$src] = new FSFile( $source );
655  }
656  }
657 
658  return $fsFiles;
659  }
660 
661  protected function doGetLocalCopyMulti( array $params ) {
662  $tmpFiles = []; // (path => TempFSFile)
663 
664  foreach ( $params['srcs'] as $src ) {
665  $source = $this->resolveToFSPath( $src );
666  if ( $source === null ) {
667  $tmpFiles[$src] = null; // invalid path
668  } else {
669  // Create a new temporary file with the same extension...
671  $tmpFile = TempFSFile::factory( 'localcopy_', $ext, $this->tmpDirectory );
672  if ( !$tmpFile ) {
673  $tmpFiles[$src] = null;
674  } else {
675  $tmpPath = $tmpFile->getPath();
676  // Copy the source file over the temp file
677  $this->trapWarnings();
678  $ok = copy( $source, $tmpPath );
679  $this->untrapWarnings();
680  if ( !$ok ) {
681  $tmpFiles[$src] = null;
682  } else {
683  $this->chmod( $tmpPath );
684  $tmpFiles[$src] = $tmpFile;
685  }
686  }
687  }
688  }
689 
690  return $tmpFiles;
691  }
692 
693  protected function directoriesAreVirtual() {
694  return false;
695  }
696 
702  protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
703  $statuses = [];
704 
705  $pipes = [];
706  foreach ( $fileOpHandles as $index => $fileOpHandle ) {
707  $pipes[$index] = popen( "{$fileOpHandle->cmd} 2>&1", 'r' );
708  }
709 
710  $errs = [];
711  foreach ( $pipes as $index => $pipe ) {
712  // Result will be empty on success in *NIX. On Windows,
713  // it may be something like " 1 file(s) [copied|moved].".
714  $errs[$index] = stream_get_contents( $pipe );
715  fclose( $pipe );
716  }
717 
718  foreach ( $fileOpHandles as $index => $fileOpHandle ) {
719  $status = $this->newStatus();
720  $function = $fileOpHandle->call;
721  $function( $errs[$index], $status, $fileOpHandle->params, $fileOpHandle->cmd );
722  $statuses[$index] = $status;
723  if ( $status->isOK() && $fileOpHandle->chmodPath ) {
724  $this->chmod( $fileOpHandle->chmodPath );
725  }
726  }
727 
728  clearstatcache(); // files changed
729  return $statuses;
730  }
731 
738  protected function chmod( $path ) {
739  $this->trapWarnings();
740  $ok = chmod( $path, $this->fileMode );
741  $this->untrapWarnings();
742 
743  return $ok;
744  }
745 
751  protected function indexHtmlPrivate() {
752  return '';
753  }
754 
760  protected function htaccessPrivate() {
761  return "Deny from all\n";
762  }
763 
770  protected function cleanPathSlashes( $path ) {
771  return $this->isWindows ? strtr( $path, '/', '\\' ) : $path;
772  }
773 
777  protected function trapWarnings() {
778  $this->hadWarningErrors[] = false; // push to stack
779  set_error_handler( [ $this, 'handleWarning' ], E_WARNING );
780  }
781 
787  protected function untrapWarnings() {
788  restore_error_handler(); // restore previous handler
789  return array_pop( $this->hadWarningErrors ); // pop from stack
790  }
791 
798  public function handleWarning( $errno, $errstr ) {
799  $this->logger->error( $errstr ); // more detailed error logging
800  $this->hadWarningErrors[count( $this->hadWarningErrors ) - 1] = true;
801 
802  return true; // suppress from PHP handler
803  }
804 }
805 
810  public $cmd; // string; shell command
811  public $chmodPath; // string; file to chmod
812 
820  public function __construct(
822  ) {
823  $this->backend = $backend;
824  $this->params = $params;
825  $this->call = $call;
826  $this->cmd = $cmd;
827  $this->chmodPath = $chmodPath;
828  }
829 }
830 
838 abstract class FSFileBackendList implements Iterator {
840  protected $iter;
841 
843  protected $suffixStart;
844 
846  protected $pos = 0;
847 
849  protected $params = [];
850 
855  public function __construct( $dir, array $params ) {
856  $path = realpath( $dir ); // normalize
857  if ( $path === false ) {
858  $path = $dir;
859  }
860  $this->suffixStart = strlen( $path ) + 1; // size of "path/to/dir/"
861  $this->params = $params;
862 
863  try {
864  $this->iter = $this->initIterator( $path );
865  } catch ( UnexpectedValueException $e ) {
866  $this->iter = null; // bad permissions? deleted?
867  }
868  }
869 
876  protected function initIterator( $dir ) {
877  if ( !empty( $this->params['topOnly'] ) ) { // non-recursive
878  # Get an iterator that will get direct sub-nodes
879  return new DirectoryIterator( $dir );
880  } else { // recursive
881  # Get an iterator that will return leaf nodes (non-directories)
882  # RecursiveDirectoryIterator extends FilesystemIterator.
883  # FilesystemIterator::SKIP_DOTS default is inconsistent in PHP 5.3.x.
884  $flags = FilesystemIterator::CURRENT_AS_SELF | FilesystemIterator::SKIP_DOTS;
885 
886  return new RecursiveIteratorIterator(
887  new RecursiveDirectoryIterator( $dir, $flags ),
888  RecursiveIteratorIterator::CHILD_FIRST // include dirs
889  );
890  }
891  }
892 
897  public function key() {
898  return $this->pos;
899  }
900 
905  public function current() {
906  return $this->getRelPath( $this->iter->current()->getPathname() );
907  }
908 
913  public function next() {
914  try {
915  $this->iter->next();
916  $this->filterViaNext();
917  } catch ( UnexpectedValueException $e ) { // bad permissions? deleted?
918  throw new FileBackendError( "File iterator gave UnexpectedValueException." );
919  }
920  ++$this->pos;
921  }
922 
927  public function rewind() {
928  $this->pos = 0;
929  try {
930  $this->iter->rewind();
931  $this->filterViaNext();
932  } catch ( UnexpectedValueException $e ) { // bad permissions? deleted?
933  throw new FileBackendError( "File iterator gave UnexpectedValueException." );
934  }
935  }
936 
941  public function valid() {
942  return $this->iter && $this->iter->valid();
943  }
944 
948  protected function filterViaNext() {
949  }
950 
958  protected function getRelPath( $dir ) {
959  $path = realpath( $dir );
960  if ( $path === false ) {
961  $path = $dir;
962  }
963 
964  return strtr( substr( $path, $this->suffixStart ), '\\', '/' );
965  }
966 }
967 
969  protected function filterViaNext() {
970  while ( $this->iter->valid() ) {
971  if ( $this->iter->current()->isDot() || !$this->iter->current()->isDir() ) {
972  $this->iter->next(); // skip non-directories and dot files
973  } else {
974  break;
975  }
976  }
977  }
978 }
979 
981  protected function filterViaNext() {
982  while ( $this->iter->valid() ) {
983  if ( !$this->iter->current()->isFile() ) {
984  $this->iter->next(); // skip non-files and dot files
985  } else {
986  break;
987  }
988  }
989  }
990 }
FileBackend\splitStoragePath
static splitStoragePath( $storagePath)
Split a storage path into a backend name, a container name, and a relative file path.
Definition: FileBackend.php:1447
FSFileBackendList\initIterator
initIterator( $dir)
Return an appropriate iterator object to wrap.
Definition: FSFileBackend.php:876
FSFileBackend\__construct
__construct(array $config)
Definition: FSFileBackend.php:74
FSFileBackend\doCleanInternal
doCleanInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:542
$handler
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check set to true or false to override the $wgMaxImageArea check result gives extension the possibility to transform it themselves $handler
Definition: hooks.txt:903
FSFileOpHandle
Definition: FSFileBackend.php:809
FSFileBackendFileList\filterViaNext
filterViaNext()
Filter out items by advancing to the next ones.
Definition: FSFileBackend.php:981
StatusValue
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: StatusValue.php:42
FSFileBackend\doStoreInternal
doStoreInternal(array $params)
Definition: FSFileBackend.php:253
use
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
Definition: APACHE-LICENSE-2.0.txt:10
FSFileBackend\doClearCache
doClearCache(array $paths=null)
Clears any additional stat caches for storage paths.
Definition: FSFileBackend.php:580
FSFileBackend\doPublishInternal
doPublishInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:514
array
the array() calling protocol came about after MediaWiki 1.4rc1.
FSFileBackend\$currentUser
string $currentUser
OS username running this script.
Definition: FSFileBackend.php:59
FSFileBackend\doPrepareInternal
doPrepareInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:459
FSFileBackendList\current
current()
Definition: FSFileBackend.php:905
FileBackendError
File backend exception for checked exceptions (e.g.
Definition: FileBackendError.php:8
FileBackend\extensionFromPath
static extensionFromPath( $path, $case='lowercase')
Get the final extension from a storage or FS path.
Definition: FileBackend.php:1506
FSFileBackend\$dirMode
int $dirMode
File permission mode.
Definition: FSFileBackend.php:51
$params
$params
Definition: styleTest.css.php:40
FSFileBackend\$hadWarningErrors
array $hadWarningErrors
Definition: FSFileBackend.php:62
FSFileBackend\htaccessPrivate
htaccessPrivate()
Return the text of a .htaccess file to make a directory private.
Definition: FSFileBackend.php:760
FSFileBackend\doGetFileStat
doGetFileStat(array $params)
Definition: FSFileBackend.php:556
FSFileBackend\directoriesAreVirtual
directoriesAreVirtual()
Is this a key/value store where directories are just virtual? Virtual directories exists in so much a...
Definition: FSFileBackend.php:693
FSFileBackend\$fileOwner
string $fileOwner
Required OS username to own files.
Definition: FSFileBackend.php:54
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:37
FSFileBackend\doGetLocalReferenceMulti
doGetLocalReferenceMulti(array $params)
Definition: FSFileBackend.php:646
FSFileBackend\$basePath
string $basePath
Directory holding the container directories.
Definition: FSFileBackend.php:43
FSFileBackendDirList\filterViaNext
filterViaNext()
Filter out items by advancing to the next ones.
Definition: FSFileBackend.php:969
FSFileBackend\doDirectoryExists
doDirectoryExists( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:584
FSFileBackend\getDirectoryListInternal
getDirectoryListInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:603
FSFileBackend\getFeatures
getFeatures()
Get the a bitfield of extra features supported by the backend medium.
Definition: FSFileBackend.php:101
FSFileBackendFileList
Definition: FSFileBackend.php:980
FSFileBackendList\getRelPath
getRelPath( $dir)
Return only the relative path and normalize slashes to FileBackend-style.
Definition: FSFileBackend.php:958
FSFileBackend\containerFSRoot
containerFSRoot( $shortCont, $fullCont)
Given the short (unresolved) and full (resolved) name of a container, return the file system path of ...
Definition: FSFileBackend.php:149
FileBackendStoreOpHandle\$backend
FileBackendStore $backend
Definition: FileBackendStore.php:1860
FSFileBackendDirList
Definition: FSFileBackend.php:968
FileBackend\ATTR_UNICODE_PATHS
const ATTR_UNICODE_PATHS
Definition: FileBackend.php:130
FSFileBackend\doSecureInternal
doSecureInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:486
FSFileBackendList
Wrapper around RecursiveDirectoryIterator/DirectoryIterator that catches exception or does any custom...
Definition: FSFileBackend.php:838
FSFileBackend\doExecuteOpHandlesInternal
doExecuteOpHandlesInternal(array $fileOpHandles)
Definition: FSFileBackend.php:702
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
FSFileBackend\$containerPaths
array $containerPaths
Map of container names to root paths for custom container paths.
Definition: FSFileBackend.php:46
FSFileBackend\resolveToFSPath
resolveToFSPath( $storagePath)
Get the absolute file system path for a storage path.
Definition: FSFileBackend.php:165
FSFileBackendList\$iter
Iterator $iter
Definition: FSFileBackend.php:840
TempFSFile\factory
static factory( $prefix, $extension='', $tmpDirectory=null)
Make a new temporary file on the file system.
Definition: TempFSFile.php:55
FSFileBackend\doDeleteInternal
doDeleteInternal(array $params)
Definition: FSFileBackend.php:409
FSFileBackend\$fileMode
int $fileMode
File permission mode.
Definition: FSFileBackend.php:49
FSFileBackendList\valid
valid()
Definition: FSFileBackend.php:941
FSFileBackend\resolveContainerPath
resolveContainerPath( $container, $relStoragePath)
Resolve a relative storage path, checking if it's allowed by the backend.
Definition: FSFileBackend.php:111
FSFileBackendList\$pos
int $pos
Definition: FSFileBackend.php:846
FileBackendStore\resolveStoragePathReal
resolveStoragePathReal( $storagePath)
Like resolveStoragePath() except null values are returned if the container is sharded and the shard c...
Definition: FileBackendStore.php:1469
FileBackendStore
Base class for all backends using particular storage medium.
Definition: FileBackendStore.php:38
$status
Status::newGood()` to allow deletion, and then `return false` from the hook function. Ensure you consume the 'ChangeTagAfterDelete' hook to carry out custom deletion actions. $tag:name of the tag $user:user initiating the action & $status:Status object. See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use. & $tags:list of all active tags. Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function. Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action or null $user:User who performed the tagging when the tagging is subsequent to the action or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change. & $allowedTags:List of all the tags the user is allowed to add. Any tags the user wants to add( $addTags) that are not in this array will cause it to fail. You may add or remove tags to this array as required. $addTags:List of tags user intends to add. $user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed. $performer:The User who will perform the change $user:The User whose groups will be changed & $add:The groups that will be added & $remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation. $collationName:Name of the collation in question & $collationObject:Null. Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user 's email has been confirmed successfully. $user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object. Called by Content::getParserOutput after parsing has finished. Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML). $content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput. May be used to override the normal model-specific rendering of page content. $content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering. To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state. $generateHtml:boolean, indicating whether full HTML should be generated. If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object. & $output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title. May be used to assign a different model for that title. $title:the Title in question & $model:the model name. Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers. Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core. $modeName:the requested content model name & $handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page. This is especially useful to prevent some content models to be used in some special location. $contentModel:ID of the content model in question $title:the Title in question. & $ok:Output parameter, whether it is OK to use $contentModel on $title. Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok. 'ContribsPager::getQueryInfo':Before the contributions query is about to run & $pager:Pager object for contributions & $queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions & $data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title & $tools:Array of tool links $specialPage:SpecialPage instance for context and services. Can be either SpecialContributions or DeletedContributionsPage. Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested. Handler functions that modify $result should generally return false to disable further attempts at conversion. $content:The Content object to be converted. $toModel:The ID of the content model to convert to. $lossy:boolean indicating whether lossy conversion is allowed. & $result:Output parameter, in case the handler function wants to provide a converted Content object. Note that $result->getContentModel() must return $toModel. 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g. for a special namespace, etc. $article:Article being edited $user:User performing the edit 'DatabaseOraclePostInit':Called after initialising an Oracle database $db:the DatabaseOracle object 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery & $data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished. Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DeleteUnknownPreferences':Called by the cleanupPreferences.php maintenance script to build a WHERE clause with which to delete preferences that are not known about. This hook is used by extensions that have dynamically-named preferences that should not be deleted in the usual cleanup process. For example, the Gadgets extension creates preferences prefixed with 'gadget-', and so anything with that prefix is excluded from the deletion. &where:An array that will be passed as the $cond parameter to IDatabase::select() to determine what will be deleted from the user_properties table. $db:The IDatabase object, useful for accessing $db->buildLike() etc. 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function. $differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable 's value is null. This hook can be used to inject content into said class member variable. $differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the "mark as patrolled" link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink". $differenceEngine:DifferenceEngine object & $markAsPatrolledLink:The "mark as patrolled" link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter. For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions. & $rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision 's author, whether the revision was marked as a minor edit or not, etc. $differenceEngine:DifferenceEngine object & $newHeader:The string containing the various #mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition: hooks.txt:1255
FileBackendStoreOpHandle
FileBackendStore helper class for performing asynchronous file operations.
Definition: FileBackendStore.php:1856
FSFile
Class representing a non-directory file on the file system.
Definition: FSFile.php:29
FSFileBackend\$isWindows
bool $isWindows
Definition: FSFileBackend.php:57
FSFileBackendList\next
next()
Definition: FSFileBackend.php:913
FSFileBackend
Class for a file system (FS) based file backend.
Definition: FSFileBackend.php:41
FSFileBackendList\key
key()
Definition: FSFileBackend.php:897
FSFileBackend\indexHtmlPrivate
indexHtmlPrivate()
Return the text of an index.html file to hide directory listings.
Definition: FSFileBackend.php:751
FSFileBackend\cleanPathSlashes
cleanPathSlashes( $path)
Clean up directory separators for the given OS.
Definition: FSFileBackend.php:770
$path
$path
Definition: NoLocalSettings.php:25
FSFileOpHandle\__construct
__construct(FSFileBackend $backend, array $params, $call, $cmd, $chmodPath=null)
Definition: FSFileBackend.php:820
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:22
FSFileBackend\doCreateInternal
doCreateInternal(array $params)
Definition: FSFileBackend.php:200
FSFileBackend\chmod
chmod( $path)
Chmod a file, suppressing the warnings.
Definition: FSFileBackend.php:738
FSFileBackendList\$suffixStart
int $suffixStart
Definition: FSFileBackend.php:843
FileBackendStoreOpHandle\$params
array $params
Definition: FileBackendStore.php:1858
$source
$source
Definition: mwdoc-filter.php:46
FSFileBackend\isLegalRelPath
isLegalRelPath( $path)
Sanity check a relative file system path for validity.
Definition: FSFileBackend.php:129
FSFileBackendList\rewind
rewind()
Definition: FSFileBackend.php:927
FSFileBackend\isPathUsableInternal
isPathUsableInternal( $storagePath)
Check if a file can be created or changed at a given storage path.
Definition: FSFileBackend.php:179
FileBackend\copy
copy(array $params, array $opts=[])
Performs a single copy operation.
Definition: FileBackend.php:502
FSFileBackend\handleWarning
handleWarning( $errno, $errstr)
Definition: FSFileBackend.php:798
FSFileBackend\untrapWarnings
untrapWarnings()
Stop listening for E_WARNING errors and return true if any happened.
Definition: FSFileBackend.php:787
FSFileBackend\doGetLocalCopyMulti
doGetLocalCopyMulti(array $params)
Definition: FSFileBackend.php:661
FSFileOpHandle\$cmd
$cmd
Definition: FSFileBackend.php:810
$ext
$ext
Definition: router.php:55
FSFileBackend\doCopyInternal
doCopyInternal(array $params)
Definition: FSFileBackend.php:296
FSFileBackendList\$params
array $params
Definition: FSFileBackend.php:849
FSFileBackend\getFileListInternal
getFileListInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:628
FSFileBackend\trapWarnings
trapWarnings()
Listen for E_WARNING errors and track whether any happen.
Definition: FSFileBackend.php:777
FileBackendStoreOpHandle\$call
$call
Definition: FileBackendStore.php:1864
FileBackend\newStatus
newStatus()
Yields the result of the status wrapper callback on either:
Definition: FileBackend.php:1597
FSFileBackendList\__construct
__construct( $dir, array $params)
Definition: FSFileBackend.php:855
FSFileBackend\doMoveInternal
doMoveInternal(array $params)
Definition: FSFileBackend.php:356
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2171
FSFileOpHandle\$chmodPath
$chmodPath
Definition: FSFileBackend.php:811
FSFileBackendList\filterViaNext
filterViaNext()
Filter out items by advancing to the next ones.
Definition: FSFileBackend.php:948