BadFileLookup.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <?php
  2. namespace MediaWiki;
  3. use BagOStuff;
  4. use Hooks;
  5. use MalformedTitleException;
  6. use MediaWiki\Linker\LinkTarget;
  7. use RepoGroup;
  8. use TitleParser;
  9. class BadFileLookup {
  10. /** @var callable Returns contents of blacklist (see comment for isBadFile()) */
  11. private $blacklistCallback;
  12. /** @var BagOStuff Cache of parsed bad image list */
  13. private $cache;
  14. /** @var RepoGroup */
  15. private $repoGroup;
  16. /** @var TitleParser */
  17. private $titleParser;
  18. /** @var array|null Parsed blacklist */
  19. private $badFiles;
  20. /**
  21. * Do not call directly. Use MediaWikiServices.
  22. *
  23. * @param callable $blacklistCallback Callback that returns wikitext of a file blacklist
  24. * @param BagOStuff $cache For caching parsed versions of the blacklist
  25. * @param RepoGroup $repoGroup
  26. * @param TitleParser $titleParser
  27. */
  28. public function __construct(
  29. callable $blacklistCallback,
  30. BagOStuff $cache,
  31. RepoGroup $repoGroup,
  32. TitleParser $titleParser
  33. ) {
  34. $this->blacklistCallback = $blacklistCallback;
  35. $this->cache = $cache;
  36. $this->repoGroup = $repoGroup;
  37. $this->titleParser = $titleParser;
  38. }
  39. /**
  40. * Determine if a file exists on the 'bad image list'.
  41. *
  42. * The format of MediaWiki:Bad_image_list is as follows:
  43. * * Only list items (lines starting with "*") are considered
  44. * * The first link on a line must be a link to a bad file
  45. * * Any subsequent links on the same line are considered to be exceptions,
  46. * i.e. articles where the file may occur inline.
  47. *
  48. * @param string $name The file name to check
  49. * @param LinkTarget|null $contextTitle The page on which the file occurs, if known
  50. * @return bool
  51. */
  52. public function isBadFile( $name, LinkTarget $contextTitle = null ) {
  53. // Handle redirects; callers almost always hit wfFindFile() anyway, so just use that method
  54. // because it has a fast process cache.
  55. $file = $this->repoGroup->findFile( $name );
  56. // XXX If we don't find the file we also don't replace spaces by underscores or otherwise
  57. // validate or normalize the title, is this right?
  58. if ( $file ) {
  59. $name = $file->getTitle()->getDBkey();
  60. }
  61. // Run the extension hook
  62. $bad = false;
  63. if ( !Hooks::run( 'BadImage', [ $name, &$bad ] ) ) {
  64. return (bool)$bad;
  65. }
  66. if ( $this->badFiles === null ) {
  67. // Not used before in this request, try the cache
  68. $blacklist = ( $this->blacklistCallback )();
  69. $key = $this->cache->makeKey( 'bad-image-list', sha1( $blacklist ) );
  70. $this->badFiles = $this->cache->get( $key ) ?: null;
  71. }
  72. if ( $this->badFiles === null ) {
  73. // Cache miss, build the list now
  74. $this->badFiles = [];
  75. $lines = explode( "\n", $blacklist );
  76. foreach ( $lines as $line ) {
  77. // List items only
  78. if ( substr( $line, 0, 1 ) !== '*' ) {
  79. continue;
  80. }
  81. // Find all links
  82. $m = [];
  83. // XXX What is the ':?' doing in the regex? Why not let the TitleParser strip it?
  84. if ( !preg_match_all( '/\[\[:?(.*?)\]\]/', $line, $m ) ) {
  85. continue;
  86. }
  87. $fileDBkey = null;
  88. $exceptions = [];
  89. foreach ( $m[1] as $i => $titleText ) {
  90. try {
  91. $title = $this->titleParser->parseTitle( $titleText );
  92. } catch ( MalformedTitleException $e ) {
  93. continue;
  94. }
  95. if ( $i == 0 ) {
  96. $fileDBkey = $title->getDBkey();
  97. } else {
  98. $exceptions[$title->getNamespace()][$title->getDBkey()] = true;
  99. }
  100. }
  101. if ( $fileDBkey !== null ) {
  102. $this->badFiles[$fileDBkey] = $exceptions;
  103. }
  104. }
  105. $this->cache->set( $key, $this->badFiles, 24 * 60 * 60 );
  106. }
  107. return isset( $this->badFiles[$name] ) && ( !$contextTitle ||
  108. !isset( $this->badFiles[$name][$contextTitle->getNamespace()]
  109. [$contextTitle->getDBkey()] ) );
  110. }
  111. }