MediaWiki  1.34.0
ApiBase.php
Go to the documentation of this file.
1 <?php
29 
42 abstract class ApiBase extends ContextSource {
43 
44  use ApiBlockInfoTrait;
45 
55  const PARAM_DFLT = 0;
56 
58  const PARAM_ISMULTI = 1;
59 
94  const PARAM_TYPE = 2;
95 
97  const PARAM_MAX = 3;
98 
103  const PARAM_MAX2 = 4;
104 
106  const PARAM_MIN = 5;
107 
110 
112  const PARAM_DEPRECATED = 7;
113 
118  const PARAM_REQUIRED = 8;
119 
125 
131  const PARAM_HELP_MSG = 10;
132 
139 
149 
155  const PARAM_VALUE_LINKS = 13;
156 
165 
173 
180 
187  const PARAM_ALL = 17;
188 
194 
200  const PARAM_SENSITIVE = 19;
201 
210 
216 
223 
228  const PARAM_MAX_BYTES = 23;
229 
234  const PARAM_MAX_CHARS = 24;
235 
253 
256  const ALL_DEFAULT_STRING = '*';
257 
259  const LIMIT_BIG1 = 500;
261  const LIMIT_BIG2 = 5000;
263  const LIMIT_SML1 = 50;
265  const LIMIT_SML2 = 500;
266 
273 
275  private static $extensionInfo = null;
276 
278  private static $filterIDsCache = [];
279 
281  private static $blockMsgMap = [
282  'blockedtext' => [ 'apierror-blocked', 'blocked' ],
283  'blockedtext-partial' => [ 'apierror-blocked-partial', 'blocked' ],
284  'autoblockedtext' => [ 'apierror-autoblocked', 'autoblocked' ],
285  'systemblockedtext' => [ 'apierror-systemblocked', 'blocked' ],
286  'blockedtext-composite' => [ 'apierror-blocked', 'blocked' ],
287  ];
288 
290  private $mMainModule;
293  private $mReplicaDB = null;
294  private $mParamCache = [];
296  private $mModuleSource = false;
297 
303  public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
304  $this->mMainModule = $mainModule;
305  $this->mModuleName = $moduleName;
306  $this->mModulePrefix = $modulePrefix;
307 
308  if ( !$this->isMain() ) {
309  $this->setContext( $mainModule->getContext() );
310  }
311  }
312 
313  /************************************************************************/
334  abstract public function execute();
335 
341  public function getModuleManager() {
342  return null;
343  }
344 
354  public function getCustomPrinter() {
355  return null;
356  }
357 
369  protected function getExamplesMessages() {
370  return [];
371  }
372 
378  public function getHelpUrls() {
379  return [];
380  }
381 
394  protected function getAllowedParams( /* $flags = 0 */ ) {
395  // int $flags is not declared because it causes "Strict standards"
396  // warning. Most derived classes do not implement it.
397  return [];
398  }
399 
404  public function shouldCheckMaxlag() {
405  return true;
406  }
407 
412  public function isReadMode() {
413  return true;
414  }
415 
427  public function isWriteMode() {
428  return false;
429  }
430 
435  public function mustBePosted() {
436  return $this->needsToken() !== false;
437  }
438 
444  public function isDeprecated() {
445  return false;
446  }
447 
454  public function isInternal() {
455  return false;
456  }
457 
476  public function needsToken() {
477  return false;
478  }
479 
489  protected function getWebUITokenSalt( array $params ) {
490  return null;
491  }
492 
505  public function getConditionalRequestData( $condition ) {
506  return null;
507  }
508 
511  /************************************************************************/
520  public function getModuleName() {
521  return $this->mModuleName;
522  }
523 
528  public function getModulePrefix() {
529  return $this->mModulePrefix;
530  }
531 
536  public function getMain() {
537  return $this->mMainModule;
538  }
539 
545  public function isMain() {
546  return $this === $this->mMainModule;
547  }
548 
554  public function getParent() {
555  return $this->isMain() ? null : $this->getMain();
556  }
557 
568  public function lacksSameOriginSecurity() {
569  // Main module has this method overridden
570  // Safety - avoid infinite loop:
571  if ( $this->isMain() ) {
572  self::dieDebug( __METHOD__, 'base method was called on main module.' );
573  }
574 
575  return $this->getMain()->lacksSameOriginSecurity();
576  }
577 
584  public function getModulePath() {
585  if ( $this->isMain() ) {
586  return 'main';
587  } elseif ( $this->getParent()->isMain() ) {
588  return $this->getModuleName();
589  } else {
590  return $this->getParent()->getModulePath() . '+' . $this->getModuleName();
591  }
592  }
593 
602  public function getModuleFromPath( $path ) {
603  $module = $this->getMain();
604  if ( $path === 'main' ) {
605  return $module;
606  }
607 
608  $parts = explode( '+', $path );
609  if ( count( $parts ) === 1 ) {
610  // In case the '+' was typed into URL, it resolves as a space
611  $parts = explode( ' ', $path );
612  }
613 
614  $count = count( $parts );
615  for ( $i = 0; $i < $count; $i++ ) {
616  $parent = $module;
617  $manager = $parent->getModuleManager();
618  if ( $manager === null ) {
619  $errorPath = implode( '+', array_slice( $parts, 0, $i ) );
620  $this->dieWithError( [ 'apierror-badmodule-nosubmodules', $errorPath ], 'badmodule' );
621  }
622  $module = $manager->getModule( $parts[$i] );
623 
624  if ( $module === null ) {
625  $errorPath = $i ? implode( '+', array_slice( $parts, 0, $i ) ) : $parent->getModuleName();
626  $this->dieWithError(
627  [ 'apierror-badmodule-badsubmodule', $errorPath, wfEscapeWikiText( $parts[$i] ) ],
628  'badmodule'
629  );
630  }
631  }
632 
633  return $module;
634  }
635 
640  public function getResult() {
641  // Main module has getResult() method overridden
642  // Safety - avoid infinite loop:
643  if ( $this->isMain() ) {
644  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
645  }
646 
647  return $this->getMain()->getResult();
648  }
649 
654  public function getErrorFormatter() {
655  // Main module has getErrorFormatter() method overridden
656  // Safety - avoid infinite loop:
657  if ( $this->isMain() ) {
658  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
659  }
660 
661  return $this->getMain()->getErrorFormatter();
662  }
663 
668  protected function getDB() {
669  if ( !isset( $this->mReplicaDB ) ) {
670  $this->mReplicaDB = wfGetDB( DB_REPLICA, 'api' );
671  }
672 
673  return $this->mReplicaDB;
674  }
675 
680  public function getContinuationManager() {
681  // Main module has getContinuationManager() method overridden
682  // Safety - avoid infinite loop:
683  if ( $this->isMain() ) {
684  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
685  }
686 
687  return $this->getMain()->getContinuationManager();
688  }
689 
694  public function setContinuationManager( ApiContinuationManager $manager = null ) {
695  // Main module has setContinuationManager() method overridden
696  // Safety - avoid infinite loop:
697  if ( $this->isMain() ) {
698  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
699  }
700 
701  $this->getMain()->setContinuationManager( $manager );
702  }
703 
710  protected function getPermissionManager(): PermissionManager {
711  return MediaWikiServices::getInstance()->getPermissionManager();
712  }
713 
716  /************************************************************************/
728  public function dynamicParameterDocumentation() {
729  return null;
730  }
731 
739  public function encodeParamName( $paramName ) {
740  if ( is_array( $paramName ) ) {
741  return array_map( function ( $name ) {
742  return $this->mModulePrefix . $name;
743  }, $paramName );
744  } else {
745  return $this->mModulePrefix . $paramName;
746  }
747  }
748 
761  public function extractRequestParams( $options = [] ) {
762  if ( is_bool( $options ) ) {
763  $options = [ 'parseLimit' => $options ];
764  }
765  $options += [
766  'parseLimit' => true,
767  'safeMode' => false,
768  ];
769 
770  $parseLimit = (bool)$options['parseLimit'];
771 
772  // Cache parameters, for performance and to avoid T26564.
773  if ( !isset( $this->mParamCache[$parseLimit] ) ) {
774  $params = $this->getFinalParams() ?: [];
775  $results = [];
776  $warned = [];
777 
778  // Process all non-templates and save templates for secondary
779  // processing.
780  $toProcess = [];
781  foreach ( $params as $paramName => $paramSettings ) {
782  if ( isset( $paramSettings[self::PARAM_TEMPLATE_VARS] ) ) {
783  $toProcess[] = [ $paramName, $paramSettings[self::PARAM_TEMPLATE_VARS], $paramSettings ];
784  } else {
785  try {
786  $results[$paramName] = $this->getParameterFromSettings(
787  $paramName, $paramSettings, $parseLimit
788  );
789  } catch ( ApiUsageException $ex ) {
790  $results[$paramName] = $ex;
791  }
792  }
793  }
794 
795  // Now process all the templates by successively replacing the
796  // placeholders with all client-supplied values.
797  // This bit duplicates JavaScript logic in
798  // ApiSandbox.PageLayout.prototype.updateTemplatedParams().
799  // If you update this, see if that needs updating too.
800  while ( $toProcess ) {
801  list( $name, $targets, $settings ) = array_shift( $toProcess );
802 
803  foreach ( $targets as $placeholder => $target ) {
804  if ( !array_key_exists( $target, $results ) ) {
805  // The target wasn't processed yet, try the next one.
806  // If all hit this case, the parameter has no expansions.
807  continue;
808  }
809  if ( !is_array( $results[$target] ) || !$results[$target] ) {
810  // The target was processed but has no (valid) values.
811  // That means it has no expansions.
812  break;
813  }
814 
815  // Expand this target in the name and all other targets,
816  // then requeue if there are more targets left or put in
817  // $results if all are done.
818  unset( $targets[$placeholder] );
819  $placeholder = '{' . $placeholder . '}';
820  // @phan-suppress-next-line PhanTypeNoAccessiblePropertiesForeach
821  foreach ( $results[$target] as $value ) {
822  if ( !preg_match( '/^[^{}]*$/', $value ) ) {
823  // Skip values that make invalid parameter names.
824  $encTargetName = $this->encodeParamName( $target );
825  if ( !isset( $warned[$encTargetName][$value] ) ) {
826  $warned[$encTargetName][$value] = true;
827  $this->addWarning( [
828  'apiwarn-ignoring-invalid-templated-value',
829  wfEscapeWikiText( $encTargetName ),
830  wfEscapeWikiText( $value ),
831  ] );
832  }
833  continue;
834  }
835 
836  $newName = str_replace( $placeholder, $value, $name );
837  if ( !$targets ) {
838  try {
839  $results[$newName] = $this->getParameterFromSettings( $newName, $settings, $parseLimit );
840  } catch ( ApiUsageException $ex ) {
841  $results[$newName] = $ex;
842  }
843  } else {
844  $newTargets = [];
845  foreach ( $targets as $k => $v ) {
846  $newTargets[$k] = str_replace( $placeholder, $value, $v );
847  }
848  $toProcess[] = [ $newName, $newTargets, $settings ];
849  }
850  }
851  break;
852  }
853  }
854 
855  $this->mParamCache[$parseLimit] = $results;
856  }
857 
858  $ret = $this->mParamCache[$parseLimit];
859  if ( !$options['safeMode'] ) {
860  foreach ( $ret as $v ) {
861  if ( $v instanceof ApiUsageException ) {
862  throw $v;
863  }
864  }
865  }
866 
867  return $this->mParamCache[$parseLimit];
868  }
869 
876  protected function getParameter( $paramName, $parseLimit = true ) {
877  $ret = $this->extractRequestParams( [
878  'parseLimit' => $parseLimit,
879  'safeMode' => true,
880  ] )[$paramName];
881  if ( $ret instanceof ApiUsageException ) {
882  throw $ret;
883  }
884  return $ret;
885  }
886 
893  public function requireOnlyOneParameter( $params, $required /*...*/ ) {
894  $required = func_get_args();
895  array_shift( $required );
896 
897  $intersection = array_intersect( array_keys( array_filter( $params,
898  [ $this, 'parameterNotEmpty' ] ) ), $required );
899 
900  if ( count( $intersection ) > 1 ) {
901  $this->dieWithError( [
902  'apierror-invalidparammix',
903  Message::listParam( array_map(
904  function ( $p ) {
905  return '<var>' . $this->encodeParamName( $p ) . '</var>';
906  },
907  array_values( $intersection )
908  ) ),
909  count( $intersection ),
910  ] );
911  } elseif ( count( $intersection ) == 0 ) {
912  $this->dieWithError( [
913  'apierror-missingparam-one-of',
914  Message::listParam( array_map(
915  function ( $p ) {
916  return '<var>' . $this->encodeParamName( $p ) . '</var>';
917  },
918  array_values( $required )
919  ) ),
920  count( $required ),
921  ], 'missingparam' );
922  }
923  }
924 
931  public function requireMaxOneParameter( $params, $required /*...*/ ) {
932  $required = func_get_args();
933  array_shift( $required );
934 
935  $intersection = array_intersect( array_keys( array_filter( $params,
936  [ $this, 'parameterNotEmpty' ] ) ), $required );
937 
938  if ( count( $intersection ) > 1 ) {
939  $this->dieWithError( [
940  'apierror-invalidparammix',
941  Message::listParam( array_map(
942  function ( $p ) {
943  return '<var>' . $this->encodeParamName( $p ) . '</var>';
944  },
945  array_values( $intersection )
946  ) ),
947  count( $intersection ),
948  ] );
949  }
950  }
951 
959  public function requireAtLeastOneParameter( $params, $required /*...*/ ) {
960  $required = func_get_args();
961  array_shift( $required );
962 
963  $intersection = array_intersect(
964  array_keys( array_filter( $params, [ $this, 'parameterNotEmpty' ] ) ),
965  $required
966  );
967 
968  if ( count( $intersection ) == 0 ) {
969  $this->dieWithError( [
970  'apierror-missingparam-at-least-one-of',
971  Message::listParam( array_map(
972  function ( $p ) {
973  return '<var>' . $this->encodeParamName( $p ) . '</var>';
974  },
975  array_values( $required )
976  ) ),
977  count( $required ),
978  ], 'missingparam' );
979  }
980  }
981 
989  public function requirePostedParameters( $params, $prefix = 'prefix' ) {
990  // Skip if $wgDebugAPI is set or we're in internal mode
991  if ( $this->getConfig()->get( 'DebugAPI' ) || $this->getMain()->isInternalMode() ) {
992  return;
993  }
994 
995  $queryValues = $this->getRequest()->getQueryValuesOnly();
996  $badParams = [];
997  foreach ( $params as $param ) {
998  if ( $prefix !== 'noprefix' ) {
999  $param = $this->encodeParamName( $param );
1000  }
1001  if ( array_key_exists( $param, $queryValues ) ) {
1002  $badParams[] = $param;
1003  }
1004  }
1005 
1006  if ( $badParams ) {
1007  $this->dieWithError(
1008  [ 'apierror-mustpostparams', implode( ', ', $badParams ), count( $badParams ) ]
1009  );
1010  }
1011  }
1012 
1019  private function parameterNotEmpty( $x ) {
1020  return !is_null( $x ) && $x !== false;
1021  }
1022 
1034  public function getTitleOrPageId( $params, $load = false ) {
1035  $this->requireOnlyOneParameter( $params, 'title', 'pageid' );
1036 
1037  $pageObj = null;
1038  if ( isset( $params['title'] ) ) {
1039  $titleObj = Title::newFromText( $params['title'] );
1040  if ( !$titleObj || $titleObj->isExternal() ) {
1041  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] );
1042  }
1043  if ( !$titleObj->canExist() ) {
1044  $this->dieWithError( 'apierror-pagecannotexist' );
1045  }
1046  $pageObj = WikiPage::factory( $titleObj );
1047  if ( $load !== false ) {
1048  $pageObj->loadPageData( $load );
1049  }
1050  } elseif ( isset( $params['pageid'] ) ) {
1051  if ( $load === false ) {
1052  $load = 'fromdb';
1053  }
1054  $pageObj = WikiPage::newFromID( $params['pageid'], $load );
1055  if ( !$pageObj ) {
1056  $this->dieWithError( [ 'apierror-nosuchpageid', $params['pageid'] ] );
1057  }
1058  }
1059 
1060  return $pageObj;
1061  }
1062 
1071  public function getTitleFromTitleOrPageId( $params ) {
1072  $this->requireOnlyOneParameter( $params, 'title', 'pageid' );
1073 
1074  $titleObj = null;
1075  if ( isset( $params['title'] ) ) {
1076  $titleObj = Title::newFromText( $params['title'] );
1077  if ( !$titleObj || $titleObj->isExternal() ) {
1078  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] );
1079  }
1080  return $titleObj;
1081  } elseif ( isset( $params['pageid'] ) ) {
1082  $titleObj = Title::newFromID( $params['pageid'] );
1083  if ( !$titleObj ) {
1084  $this->dieWithError( [ 'apierror-nosuchpageid', $params['pageid'] ] );
1085  }
1086  }
1087 
1088  return $titleObj;
1089  }
1090 
1099  protected function getWatchlistValue( $watchlist, $titleObj, $userOption = null ) {
1100  $userWatching = $this->getUser()->isWatched( $titleObj, User::IGNORE_USER_RIGHTS );
1101 
1102  switch ( $watchlist ) {
1103  case 'watch':
1104  return true;
1105 
1106  case 'unwatch':
1107  return false;
1108 
1109  case 'preferences':
1110  # If the user is already watching, don't bother checking
1111  if ( $userWatching ) {
1112  return true;
1113  }
1114  # If no user option was passed, use watchdefault and watchcreations
1115  if ( is_null( $userOption ) ) {
1116  return $this->getUser()->getBoolOption( 'watchdefault' ) ||
1117  $this->getUser()->getBoolOption( 'watchcreations' ) && !$titleObj->exists();
1118  }
1119 
1120  # Watch the article based on the user preference
1121  return $this->getUser()->getBoolOption( $userOption );
1122 
1123  case 'nochange':
1124  return $userWatching;
1125 
1126  default:
1127  return $userWatching;
1128  }
1129  }
1130 
1140  protected function getParameterFromSettings( $paramName, $paramSettings, $parseLimit ) {
1141  // Some classes may decide to change parameter names
1142  $encParamName = $this->encodeParamName( $paramName );
1143 
1144  // Shorthand
1145  if ( !is_array( $paramSettings ) ) {
1146  $paramSettings = [
1147  self::PARAM_DFLT => $paramSettings,
1148  ];
1149  }
1150 
1151  $default = $paramSettings[self::PARAM_DFLT] ?? null;
1152  $multi = $paramSettings[self::PARAM_ISMULTI] ?? false;
1153  $multiLimit1 = $paramSettings[self::PARAM_ISMULTI_LIMIT1] ?? null;
1154  $multiLimit2 = $paramSettings[self::PARAM_ISMULTI_LIMIT2] ?? null;
1155  $type = $paramSettings[self::PARAM_TYPE] ?? null;
1156  $dupes = $paramSettings[self::PARAM_ALLOW_DUPLICATES] ?? false;
1157  $deprecated = $paramSettings[self::PARAM_DEPRECATED] ?? false;
1158  $deprecatedValues = $paramSettings[self::PARAM_DEPRECATED_VALUES] ?? [];
1159  $required = $paramSettings[self::PARAM_REQUIRED] ?? false;
1160  $allowAll = $paramSettings[self::PARAM_ALL] ?? false;
1161 
1162  // When type is not given, and no choices, the type is the same as $default
1163  if ( !isset( $type ) ) {
1164  if ( isset( $default ) ) {
1165  $type = gettype( $default );
1166  } else {
1167  $type = 'NULL'; // allow everything
1168  }
1169  }
1170 
1171  if ( $type == 'password' || !empty( $paramSettings[self::PARAM_SENSITIVE] ) ) {
1172  $this->getMain()->markParamsSensitive( $encParamName );
1173  }
1174 
1175  if ( $type == 'boolean' ) {
1176  if ( isset( $default ) && $default !== false ) {
1177  // Having a default value of anything other than 'false' is not allowed
1179  __METHOD__,
1180  "Boolean param $encParamName's default is set to '$default'. " .
1181  'Boolean parameters must default to false.'
1182  );
1183  }
1184 
1185  $value = $this->getMain()->getCheck( $encParamName );
1186  $provided = $value;
1187  } elseif ( $type == 'upload' ) {
1188  if ( isset( $default ) ) {
1189  // Having a default value is not allowed
1191  __METHOD__,
1192  "File upload param $encParamName's default is set to " .
1193  "'$default'. File upload parameters may not have a default." );
1194  }
1195  if ( $multi ) {
1196  self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
1197  }
1198  $value = $this->getMain()->getUpload( $encParamName );
1199  $provided = $value->exists();
1200  if ( !$value->exists() ) {
1201  // This will get the value without trying to normalize it
1202  // (because trying to normalize a large binary file
1203  // accidentally uploaded as a field fails spectacularly)
1204  $value = $this->getMain()->getRequest()->unsetVal( $encParamName );
1205  if ( $value !== null ) {
1206  $this->dieWithError(
1207  [ 'apierror-badupload', $encParamName ],
1208  "badupload_{$encParamName}"
1209  );
1210  }
1211  }
1212  } else {
1213  $value = $this->getMain()->getVal( $encParamName, $default );
1214  $provided = $this->getMain()->getCheck( $encParamName );
1215 
1216  if ( isset( $value ) && $type == 'namespace' ) {
1217  $type = MediaWikiServices::getInstance()->getNamespaceInfo()->
1218  getValidNamespaces();
1219  if ( isset( $paramSettings[self::PARAM_EXTRA_NAMESPACES] ) &&
1220  is_array( $paramSettings[self::PARAM_EXTRA_NAMESPACES] )
1221  ) {
1222  $type = array_merge( $type, $paramSettings[self::PARAM_EXTRA_NAMESPACES] );
1223  }
1224  // Namespace parameters allow ALL_DEFAULT_STRING to be used to
1225  // specify all namespaces irrespective of PARAM_ALL.
1226  $allowAll = true;
1227  }
1228  if ( isset( $value ) && $type == 'submodule' ) {
1229  if ( isset( $paramSettings[self::PARAM_SUBMODULE_MAP] ) ) {
1230  $type = array_keys( $paramSettings[self::PARAM_SUBMODULE_MAP] );
1231  } else {
1232  $type = $this->getModuleManager()->getNames( $paramName );
1233  }
1234  }
1235 
1236  $request = $this->getMain()->getRequest();
1237  $rawValue = $request->getRawVal( $encParamName );
1238  if ( $rawValue === null ) {
1239  $rawValue = $default;
1240  }
1241 
1242  // Preserve U+001F for self::parseMultiValue(), or error out if that won't be called
1243  if ( isset( $value ) && substr( $rawValue, 0, 1 ) === "\x1f" ) {
1244  if ( $multi ) {
1245  // This loses the potential checkTitleEncoding() transformation done by
1246  // WebRequest for $_GET. Let's call that a feature.
1247  $value = implode( "\x1f", $request->normalizeUnicode( explode( "\x1f", $rawValue ) ) );
1248  } else {
1249  $this->dieWithError( 'apierror-badvalue-notmultivalue', 'badvalue_notmultivalue' );
1250  }
1251  }
1252 
1253  // Check for NFC normalization, and warn
1254  if ( $rawValue !== $value ) {
1255  $this->handleParamNormalization( $paramName, $value, $rawValue );
1256  }
1257  }
1258 
1259  $allSpecifier = ( is_string( $allowAll ) ? $allowAll : self::ALL_DEFAULT_STRING );
1260  if ( $allowAll && $multi && is_array( $type ) && in_array( $allSpecifier, $type, true ) ) {
1262  __METHOD__,
1263  "For param $encParamName, PARAM_ALL collides with a possible value" );
1264  }
1265  if ( isset( $value ) && ( $multi || is_array( $type ) ) ) {
1266  $value = $this->parseMultiValue(
1267  $encParamName,
1268  $value,
1269  $multi,
1270  is_array( $type ) ? $type : null,
1271  $allowAll ? $allSpecifier : null,
1272  $multiLimit1,
1273  $multiLimit2
1274  );
1275  }
1276 
1277  if ( isset( $value ) ) {
1278  // More validation only when choices were not given
1279  // choices were validated in parseMultiValue()
1280  if ( !is_array( $type ) ) {
1281  switch ( $type ) {
1282  case 'NULL': // nothing to do
1283  break;
1284  case 'string':
1285  case 'text':
1286  case 'password':
1287  if ( $required && $value === '' ) {
1288  $this->dieWithError( [ 'apierror-missingparam', $encParamName ] );
1289  }
1290  break;
1291  case 'integer': // Force everything using intval() and optionally validate limits
1292  $min = $paramSettings[self::PARAM_MIN] ?? null;
1293  $max = $paramSettings[self::PARAM_MAX] ?? null;
1294  $enforceLimits = $paramSettings[self::PARAM_RANGE_ENFORCE] ?? false;
1295 
1296  if ( is_array( $value ) ) {
1297  $value = array_map( 'intval', $value );
1298  if ( !is_null( $min ) || !is_null( $max ) ) {
1299  foreach ( $value as &$v ) {
1300  $this->validateLimit( $paramName, $v, $min, $max, null, $enforceLimits );
1301  }
1302  }
1303  } else {
1304  $value = (int)$value;
1305  if ( !is_null( $min ) || !is_null( $max ) ) {
1306  $this->validateLimit( $paramName, $value, $min, $max, null, $enforceLimits );
1307  }
1308  }
1309  break;
1310  case 'limit':
1311  // Must be a number or 'max'
1312  if ( $value !== 'max' ) {
1313  $value = (int)$value;
1314  }
1315  if ( $multi ) {
1316  self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
1317  }
1318  if ( !$parseLimit ) {
1319  // Don't do min/max validation and don't parse 'max'
1320  break;
1321  }
1322  if ( !isset( $paramSettings[self::PARAM_MAX] )
1323  || !isset( $paramSettings[self::PARAM_MAX2] )
1324  ) {
1326  __METHOD__,
1327  "MAX1 or MAX2 are not defined for the limit $encParamName"
1328  );
1329  }
1330  if ( $value === 'max' ) {
1331  $value = $this->getMain()->canApiHighLimits()
1332  ? $paramSettings[self::PARAM_MAX2]
1333  : $paramSettings[self::PARAM_MAX];
1334  $this->getResult()->addParsedLimit( $this->getModuleName(), $value );
1335  } else {
1336  $this->validateLimit(
1337  $paramName,
1338  $value,
1339  $paramSettings[self::PARAM_MIN] ?? 0,
1340  $paramSettings[self::PARAM_MAX],
1341  $paramSettings[self::PARAM_MAX2]
1342  );
1343  }
1344  break;
1345  case 'boolean':
1346  if ( $multi ) {
1347  self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
1348  }
1349  break;
1350  case 'timestamp':
1351  if ( is_array( $value ) ) {
1352  foreach ( $value as $key => $val ) {
1353  $value[$key] = $this->validateTimestamp( $val, $encParamName );
1354  }
1355  } else {
1356  $value = $this->validateTimestamp( $value, $encParamName );
1357  }
1358  break;
1359  case 'user':
1360  if ( is_array( $value ) ) {
1361  foreach ( $value as $key => $val ) {
1362  $value[$key] = $this->validateUser( $val, $encParamName );
1363  }
1364  } else {
1365  $value = $this->validateUser( $value, $encParamName );
1366  }
1367  break;
1368  case 'upload': // nothing to do
1369  break;
1370  case 'tags':
1371  // If change tagging was requested, check that the tags are valid.
1372  if ( !is_array( $value ) && !$multi ) {
1373  $value = [ $value ];
1374  }
1375  $tagsStatus = ChangeTags::canAddTagsAccompanyingChange( $value );
1376  if ( !$tagsStatus->isGood() ) {
1377  $this->dieStatus( $tagsStatus );
1378  }
1379  break;
1380  default:
1381  self::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" );
1382  }
1383  }
1384 
1385  // Throw out duplicates if requested
1386  if ( !$dupes && is_array( $value ) ) {
1387  $value = array_unique( $value );
1388  }
1389 
1390  if ( in_array( $type, [ 'NULL', 'string', 'text', 'password' ], true ) ) {
1391  foreach ( (array)$value as $val ) {
1392  if ( isset( $paramSettings[self::PARAM_MAX_BYTES] )
1393  && strlen( $val ) > $paramSettings[self::PARAM_MAX_BYTES]
1394  ) {
1395  $this->dieWithError( [ 'apierror-maxbytes', $encParamName,
1396  $paramSettings[self::PARAM_MAX_BYTES] ] );
1397  }
1398  if ( isset( $paramSettings[self::PARAM_MAX_CHARS] )
1399  && mb_strlen( $val, 'UTF-8' ) > $paramSettings[self::PARAM_MAX_CHARS]
1400  ) {
1401  $this->dieWithError( [ 'apierror-maxchars', $encParamName,
1402  $paramSettings[self::PARAM_MAX_CHARS] ] );
1403  }
1404  }
1405  }
1406 
1407  // Set a warning if a deprecated parameter has been passed
1408  if ( $deprecated && $provided ) {
1409  $feature = $encParamName;
1410  $m = $this;
1411  while ( !$m->isMain() ) {
1412  $p = $m->getParent();
1413  $name = $m->getModuleName();
1414  $param = $p->encodeParamName( $p->getModuleManager()->getModuleGroup( $name ) );
1415  $feature = "{$param}={$name}&{$feature}";
1416  $m = $p;
1417  }
1418  $this->addDeprecation( [ 'apiwarn-deprecation-parameter', $encParamName ], $feature );
1419  }
1420 
1421  // Set a warning if a deprecated parameter value has been passed
1422  $usedDeprecatedValues = $deprecatedValues && $provided
1423  ? array_intersect( array_keys( $deprecatedValues ), (array)$value )
1424  : [];
1425  if ( $usedDeprecatedValues ) {
1426  $feature = "$encParamName=";
1427  $m = $this;
1428  while ( !$m->isMain() ) {
1429  $p = $m->getParent();
1430  $name = $m->getModuleName();
1431  $param = $p->encodeParamName( $p->getModuleManager()->getModuleGroup( $name ) );
1432  $feature = "{$param}={$name}&{$feature}";
1433  $m = $p;
1434  }
1435  foreach ( $usedDeprecatedValues as $v ) {
1436  $msg = $deprecatedValues[$v];
1437  if ( $msg === true ) {
1438  $msg = [ 'apiwarn-deprecation-parameter', "$encParamName=$v" ];
1439  }
1440  $this->addDeprecation( $msg, "$feature$v" );
1441  }
1442  }
1443  } elseif ( $required ) {
1444  $this->dieWithError( [ 'apierror-missingparam', $encParamName ] );
1445  }
1446 
1447  return $value;
1448  }
1449 
1457  protected function handleParamNormalization( $paramName, $value, $rawValue ) {
1458  $encParamName = $this->encodeParamName( $paramName );
1459  $this->addWarning( [ 'apiwarn-badutf8', $encParamName ] );
1460  }
1461 
1469  protected function explodeMultiValue( $value, $limit ) {
1470  if ( substr( $value, 0, 1 ) === "\x1f" ) {
1471  $sep = "\x1f";
1472  $value = substr( $value, 1 );
1473  } else {
1474  $sep = '|';
1475  }
1476 
1477  return explode( $sep, $value, $limit );
1478  }
1479 
1497  protected function parseMultiValue( $valueName, $value, $allowMultiple, $allowedValues,
1498  $allSpecifier = null, $limit1 = null, $limit2 = null
1499  ) {
1500  if ( ( $value === '' || $value === "\x1f" ) && $allowMultiple ) {
1501  return [];
1502  }
1503  $limit1 = $limit1 ?: self::LIMIT_SML1;
1504  $limit2 = $limit2 ?: self::LIMIT_SML2;
1505 
1506  // This is a bit awkward, but we want to avoid calling canApiHighLimits()
1507  // because it unstubs $wgUser
1508  $valuesList = $this->explodeMultiValue( $value, $limit2 + 1 );
1509  $sizeLimit = count( $valuesList ) > $limit1 && $this->mMainModule->canApiHighLimits()
1510  ? $limit2
1511  : $limit1;
1512 
1513  if ( $allowMultiple && is_array( $allowedValues ) && $allSpecifier &&
1514  count( $valuesList ) === 1 && $valuesList[0] === $allSpecifier
1515  ) {
1516  return $allowedValues;
1517  }
1518 
1519  if ( count( $valuesList ) > $sizeLimit ) {
1520  $this->dieWithError(
1521  [ 'apierror-toomanyvalues', $valueName, $sizeLimit ],
1522  "too-many-$valueName"
1523  );
1524  }
1525 
1526  if ( !$allowMultiple && count( $valuesList ) != 1 ) {
1527  // T35482 - Allow entries with | in them for non-multiple values
1528  if ( in_array( $value, $allowedValues, true ) ) {
1529  return $value;
1530  }
1531 
1532  $values = array_map( function ( $v ) {
1533  return '<kbd>' . wfEscapeWikiText( $v ) . '</kbd>';
1534  }, $allowedValues );
1535  $this->dieWithError( [
1536  'apierror-multival-only-one-of',
1537  $valueName,
1538  Message::listParam( $values ),
1539  count( $values ),
1540  ], "multival_$valueName" );
1541  }
1542 
1543  if ( is_array( $allowedValues ) ) {
1544  // Check for unknown values
1545  $unknown = array_map( 'wfEscapeWikiText', array_diff( $valuesList, $allowedValues ) );
1546  if ( count( $unknown ) ) {
1547  if ( $allowMultiple ) {
1548  $this->addWarning( [
1549  'apiwarn-unrecognizedvalues',
1550  $valueName,
1551  Message::listParam( $unknown, 'comma' ),
1552  count( $unknown ),
1553  ] );
1554  } else {
1555  $this->dieWithError(
1556  [ 'apierror-unrecognizedvalue', $valueName, wfEscapeWikiText( $valuesList[0] ) ],
1557  "unknown_$valueName"
1558  );
1559  }
1560  }
1561  // Now throw them out
1562  $valuesList = array_intersect( $valuesList, $allowedValues );
1563  }
1564 
1565  return $allowMultiple ? $valuesList : $valuesList[0];
1566  }
1567 
1578  protected function validateLimit( $paramName, &$value, $min, $max, $botMax = null,
1579  $enforceLimits = false
1580  ) {
1581  if ( !is_null( $min ) && $value < $min ) {
1582  $msg = ApiMessage::create(
1583  [ 'apierror-integeroutofrange-belowminimum',
1584  $this->encodeParamName( $paramName ), $min, $value ],
1585  'integeroutofrange',
1586  [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
1587  );
1588  // @phan-suppress-next-line PhanTypeMismatchArgument
1589  $this->warnOrDie( $msg, $enforceLimits );
1590  $value = $min;
1591  }
1592 
1593  // Minimum is always validated, whereas maximum is checked only if not
1594  // running in internal call mode
1595  if ( $this->getMain()->isInternalMode() ) {
1596  return;
1597  }
1598 
1599  // Optimization: do not check user's bot status unless really needed -- skips db query
1600  // assumes $botMax >= $max
1601  if ( !is_null( $max ) && $value > $max ) {
1602  if ( !is_null( $botMax ) && $this->getMain()->canApiHighLimits() ) {
1603  if ( $value > $botMax ) {
1604  $msg = ApiMessage::create(
1605  [ 'apierror-integeroutofrange-abovebotmax',
1606  $this->encodeParamName( $paramName ), $botMax, $value ],
1607  'integeroutofrange',
1608  [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
1609  );
1610  // @phan-suppress-next-line PhanTypeMismatchArgument
1611  $this->warnOrDie( $msg, $enforceLimits );
1612  $value = $botMax;
1613  }
1614  } else {
1615  $msg = ApiMessage::create(
1616  [ 'apierror-integeroutofrange-abovemax',
1617  $this->encodeParamName( $paramName ), $max, $value ],
1618  'integeroutofrange',
1619  [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
1620  );
1621  // @phan-suppress-next-line PhanTypeMismatchArgument
1622  $this->warnOrDie( $msg, $enforceLimits );
1623  $value = $max;
1624  }
1625  }
1626  }
1627 
1634  protected function validateTimestamp( $value, $encParamName ) {
1635  // Confusing synonyms for the current time accepted by wfTimestamp()
1636  // (wfTimestamp() also accepts various non-strings and the string of 14
1637  // ASCII NUL bytes, but those can't get here)
1638  if ( !$value ) {
1639  $this->addDeprecation(
1640  [ 'apiwarn-unclearnowtimestamp', $encParamName, wfEscapeWikiText( $value ) ],
1641  'unclear-"now"-timestamp'
1642  );
1643  return wfTimestamp( TS_MW );
1644  }
1645 
1646  // Explicit synonym for the current time
1647  if ( $value === 'now' ) {
1648  return wfTimestamp( TS_MW );
1649  }
1650 
1651  $timestamp = wfTimestamp( TS_MW, $value );
1652  if ( $timestamp === false ) {
1653  $this->dieWithError(
1654  [ 'apierror-badtimestamp', $encParamName, wfEscapeWikiText( $value ) ],
1655  "badtimestamp_{$encParamName}"
1656  );
1657  }
1658 
1659  return $timestamp;
1660  }
1661 
1671  final public function validateToken( $token, array $params ) {
1672  $tokenType = $this->needsToken();
1674  if ( !isset( $salts[$tokenType] ) ) {
1675  throw new MWException(
1676  "Module '{$this->getModuleName()}' tried to use token type '$tokenType' " .
1677  'without registering it'
1678  );
1679  }
1680 
1681  $tokenObj = ApiQueryTokens::getToken(
1682  $this->getUser(), $this->getRequest()->getSession(), $salts[$tokenType]
1683  );
1684  if ( $tokenObj->match( $token ) ) {
1685  return true;
1686  }
1687 
1688  $webUiSalt = $this->getWebUITokenSalt( $params );
1689  if ( $webUiSalt !== null && $this->getUser()->matchEditToken(
1690  $token,
1691  $webUiSalt,
1692  $this->getRequest()
1693  ) ) {
1694  return true;
1695  }
1696 
1697  return false;
1698  }
1699 
1706  private function validateUser( $value, $encParamName ) {
1707  if ( ExternalUserNames::isExternal( $value ) && User::newFromName( $value, false ) ) {
1708  return $value;
1709  }
1710 
1711  $name = User::getCanonicalName( $value, 'valid' );
1712  if ( $name !== false ) {
1713  return $name;
1714  }
1715 
1716  if (
1717  // We allow ranges as well, for blocks.
1718  IP::isIPAddress( $value ) ||
1719  // See comment for User::isIP. We don't just call that function
1720  // here because it also returns true for things like
1721  // 300.300.300.300 that are neither valid usernames nor valid IP
1722  // addresses.
1723  preg_match(
1724  '/^' . RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.xxx$/',
1725  $value
1726  )
1727  ) {
1728  return IP::sanitizeIP( $value );
1729  }
1730 
1731  $this->dieWithError(
1732  [ 'apierror-baduser', $encParamName, wfEscapeWikiText( $value ) ],
1733  "baduser_{$encParamName}"
1734  );
1735  }
1736 
1739  /************************************************************************/
1750  protected function setWatch( $watch, $titleObj, $userOption = null ) {
1751  $value = $this->getWatchlistValue( $watch, $titleObj, $userOption );
1752  if ( $value === null ) {
1753  return;
1754  }
1755 
1756  WatchAction::doWatchOrUnwatch( $value, $titleObj, $this->getUser() );
1757  }
1758 
1765  public function getWatchlistUser( $params ) {
1766  if ( !is_null( $params['owner'] ) && !is_null( $params['token'] ) ) {
1767  $user = User::newFromName( $params['owner'], false );
1768  if ( !( $user && $user->getId() ) ) {
1769  $this->dieWithError(
1770  [ 'nosuchusershort', wfEscapeWikiText( $params['owner'] ) ], 'bad_wlowner'
1771  );
1772  }
1773  $token = $user->getOption( 'watchlisttoken' );
1774  if ( $token == '' || !hash_equals( $token, $params['token'] ) ) {
1775  $this->dieWithError( 'apierror-bad-watchlist-token', 'bad_wltoken' );
1776  }
1777  } else {
1778  if ( !$this->getUser()->isLoggedIn() ) {
1779  $this->dieWithError( 'watchlistanontext', 'notloggedin' );
1780  }
1781  $this->checkUserRightsAny( 'viewmywatchlist' );
1782  $user = $this->getUser();
1783  }
1784 
1785  return $user;
1786  }
1787 
1800  public static function makeMessage( $msg, IContextSource $context, array $params = null ) {
1801  if ( is_string( $msg ) ) {
1802  $msg = wfMessage( $msg );
1803  } elseif ( is_array( $msg ) ) {
1804  $msg = wfMessage( ...$msg );
1805  }
1806  if ( !$msg instanceof Message ) {
1807  return null;
1808  }
1809 
1810  $msg->setContext( $context );
1811  if ( $params ) {
1812  $msg->params( $params );
1813  }
1814 
1815  return $msg;
1816  }
1817 
1825  public function errorArrayToStatus( array $errors, User $user = null ) {
1826  if ( $user === null ) {
1827  $user = $this->getUser();
1828  }
1829 
1831  foreach ( $errors as $error ) {
1832  if ( !is_array( $error ) ) {
1833  $error = [ $error ];
1834  }
1835  if ( is_string( $error[0] ) && isset( self::$blockMsgMap[$error[0]] ) && $user->getBlock() ) {
1836  list( $msg, $code ) = self::$blockMsgMap[$error[0]];
1837  $status->fatal( ApiMessage::create( $msg, $code,
1838  [ 'blockinfo' => $this->getBlockDetails( $user->getBlock() ) ]
1839  ) );
1840  } else {
1841  $status->fatal( ...$error );
1842  }
1843  }
1844  return $status;
1845  }
1846 
1853  public function addBlockInfoToStatus( StatusValue $status, User $user = null ) {
1854  if ( $user === null ) {
1855  $user = $this->getUser();
1856  }
1857 
1858  foreach ( self::$blockMsgMap as $msg => list( $apiMsg, $code ) ) {
1859  if ( $status->hasMessage( $msg ) && $user->getBlock() ) {
1860  $status->replaceMessage( $msg, ApiMessage::create( $apiMsg, $code,
1861  [ 'blockinfo' => $this->getBlockDetails( $user->getBlock() ) ]
1862  ) );
1863  }
1864  }
1865  }
1866 
1871  protected function useTransactionalTimeLimit() {
1872  if ( $this->getRequest()->wasPosted() ) {
1874  }
1875  }
1876 
1885  protected function filterIDs( $fields, array $ids ) {
1886  $min = INF;
1887  $max = 0;
1888  foreach ( $fields as list( $table, $field ) ) {
1889  if ( isset( self::$filterIDsCache[$table][$field] ) ) {
1890  $row = self::$filterIDsCache[$table][$field];
1891  } else {
1892  $row = $this->getDB()->selectRow(
1893  $table,
1894  [
1895  'min_id' => "MIN($field)",
1896  'max_id' => "MAX($field)",
1897  ],
1898  '',
1899  __METHOD__
1900  );
1901  self::$filterIDsCache[$table][$field] = $row;
1902  }
1903  $min = min( $min, $row->min_id );
1904  $max = max( $max, $row->max_id );
1905  }
1906  return array_filter( $ids, function ( $id ) use ( $min, $max ) {
1907  return ( is_int( $id ) && $id >= 0 || ctype_digit( $id ) )
1908  && $id >= $min && $id <= $max;
1909  } );
1910  }
1911 
1914  /************************************************************************/
1933  public function addWarning( $msg, $code = null, $data = null ) {
1934  $this->getErrorFormatter()->addWarning( $this->getModulePath(), $msg, $code, $data );
1935  }
1936 
1947  public function addDeprecation( $msg, $feature, $data = [] ) {
1948  $data = (array)$data;
1949  if ( $feature !== null ) {
1950  $data['feature'] = $feature;
1951  $this->logFeatureUsage( $feature );
1952  }
1953  $this->addWarning( $msg, 'deprecation', $data );
1954 
1955  // No real need to deduplicate here, ApiErrorFormatter does that for
1956  // us (assuming the hook is deterministic).
1957  $msgs = [ $this->msg( 'api-usage-mailinglist-ref' ) ];
1958  Hooks::run( 'ApiDeprecationHelp', [ &$msgs ] );
1959  if ( count( $msgs ) > 1 ) {
1960  $key = '$' . implode( ' $', range( 1, count( $msgs ) ) );
1961  $msg = ( new RawMessage( $key ) )->params( $msgs );
1962  } else {
1963  $msg = reset( $msgs );
1964  }
1965  $this->getMain()->addWarning( $msg, 'deprecation-help' );
1966  }
1967 
1980  public function addError( $msg, $code = null, $data = null ) {
1981  $this->getErrorFormatter()->addError( $this->getModulePath(), $msg, $code, $data );
1982  }
1983 
1993  public function addMessagesFromStatus(
1994  StatusValue $status, $types = [ 'warning', 'error' ], array $filter = []
1995  ) {
1996  $this->getErrorFormatter()->addMessagesFromStatus(
1997  $this->getModulePath(), $status, $types, $filter
1998  );
1999  }
2000 
2014  public function dieWithError( $msg, $code = null, $data = null, $httpCode = null ) {
2015  throw ApiUsageException::newWithMessage( $this, $msg, $code, $data, $httpCode );
2016  }
2017 
2026  public function dieWithException( $exception, array $options = [] ) {
2027  $this->dieWithError(
2028  // @phan-suppress-next-line PhanTypeMismatchArgument
2029  $this->getErrorFormatter()->getMessageFromException( $exception, $options )
2030  );
2031  }
2032 
2039  private function warnOrDie( ApiMessage $msg, $enforceLimits = false ) {
2040  if ( $enforceLimits ) {
2041  $this->dieWithError( $msg );
2042  } else {
2043  $this->addWarning( $msg );
2044  }
2045  }
2046 
2055  public function dieBlocked( AbstractBlock $block ) {
2056  // Die using the appropriate message depending on block type
2057  if ( $block->getType() == DatabaseBlock::TYPE_AUTO ) {
2058  $this->dieWithError(
2059  'apierror-autoblocked',
2060  'autoblocked',
2061  [ 'blockinfo' => $this->getBlockDetails( $block ) ]
2062  );
2063  } elseif ( !$block->isSitewide() ) {
2064  $this->dieWithError(
2065  'apierror-blocked-partial',
2066  'blocked',
2067  [ 'blockinfo' => $this->getBlockDetails( $block ) ]
2068  );
2069  } else {
2070  $this->dieWithError(
2071  'apierror-blocked',
2072  'blocked',
2073  [ 'blockinfo' => $this->getBlockDetails( $block ) ]
2074  );
2075  }
2076  }
2077 
2086  public function dieStatus( StatusValue $status ) {
2087  if ( $status->isGood() ) {
2088  throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
2089  }
2090 
2091  // ApiUsageException needs a fatal status, but this method has
2092  // historically accepted any non-good status. Convert it if necessary.
2093  $status->setOK( false );
2094  if ( !$status->getErrorsByType( 'error' ) ) {
2095  $newStatus = Status::newGood();
2096  foreach ( $status->getErrorsByType( 'warning' ) as $err ) {
2097  $newStatus->fatal( $err['message'], ...$err['params'] );
2098  }
2099  if ( !$newStatus->getErrorsByType( 'error' ) ) {
2100  $newStatus->fatal( 'unknownerror-nocode' );
2101  }
2102  $status = $newStatus;
2103  }
2104 
2105  $this->addBlockInfoToStatus( $status );
2106  throw new ApiUsageException( $this, $status );
2107  }
2108 
2114  public function dieReadOnly() {
2115  $this->dieWithError(
2116  'apierror-readonly',
2117  'readonly',
2118  [ 'readonlyreason' => wfReadOnlyReason() ]
2119  );
2120  }
2121 
2130  public function checkUserRightsAny( $rights, $user = null ) {
2131  if ( !$user ) {
2132  $user = $this->getUser();
2133  }
2134  $rights = (array)$rights;
2135  if ( !$this->getPermissionManager()
2136  ->userHasAnyRight( $user, ...$rights )
2137  ) {
2138  $this->dieWithError( [ 'apierror-permissiondenied', $this->msg( "action-{$rights[0]}" ) ] );
2139  }
2140  }
2141 
2156  public function checkTitleUserPermissions( LinkTarget $linkTarget, $actions, $options = [] ) {
2157  if ( !is_array( $options ) ) {
2158  wfDeprecated( '$user as the third parameter to ' . __METHOD__, '1.33' );
2159  $options = [ 'user' => $options ];
2160  }
2161  $user = $options['user'] ?? $this->getUser();
2162 
2163  $errors = [];
2164  foreach ( (array)$actions as $action ) {
2165  $errors = array_merge(
2166  $errors,
2167  $this->getPermissionManager()->getPermissionErrors( $action, $user, $linkTarget )
2168  );
2169  }
2170 
2171  if ( $errors ) {
2172  if ( !empty( $options['autoblock'] ) ) {
2173  $user->spreadAnyEditBlock();
2174  }
2175 
2176  $this->dieStatus( $this->errorArrayToStatus( $errors, $user ) );
2177  }
2178  }
2179 
2191  public function dieWithErrorOrDebug( $msg, $code = null, $data = null, $httpCode = null ) {
2192  if ( $this->getConfig()->get( 'DebugAPI' ) !== true ) {
2193  $this->dieWithError( $msg, $code, $data, $httpCode );
2194  } else {
2195  $this->addWarning( $msg, $code, $data );
2196  }
2197  }
2198 
2208  protected function dieContinueUsageIf( $condition ) {
2209  if ( $condition ) {
2210  $this->dieWithError( 'apierror-badcontinue' );
2211  }
2212  }
2213 
2220  protected static function dieDebug( $method, $message ) {
2221  throw new MWException( "Internal error in $method: $message" );
2222  }
2223 
2230  public function logFeatureUsage( $feature ) {
2231  static $loggedFeatures = [];
2232 
2233  // Only log each feature once per request. We can get multiple calls from calls to
2234  // extractRequestParams() with different values for 'parseLimit', for example.
2235  if ( isset( $loggedFeatures[$feature] ) ) {
2236  return;
2237  }
2238  $loggedFeatures[$feature] = true;
2239 
2240  $request = $this->getRequest();
2241  $ctx = [
2242  'feature' => $feature,
2243  // Spaces to underscores in 'username' for historical reasons.
2244  'username' => str_replace( ' ', '_', $this->getUser()->getName() ),
2245  'ip' => $request->getIP(),
2246  'referer' => (string)$request->getHeader( 'Referer' ),
2247  'agent' => $this->getMain()->getUserAgent(),
2248  ];
2249 
2250  // Text string is deprecated. Remove (or replace with just $feature) in MW 1.34.
2251  $s = '"' . addslashes( $ctx['feature'] ) . '"' .
2252  ' "' . wfUrlencode( $ctx['username'] ) . '"' .
2253  ' "' . $ctx['ip'] . '"' .
2254  ' "' . addslashes( $ctx['referer'] ) . '"' .
2255  ' "' . addslashes( $ctx['agent'] ) . '"';
2256 
2257  wfDebugLog( 'api-feature-usage', $s, 'private', $ctx );
2258  }
2259 
2262  /************************************************************************/
2276  protected function getSummaryMessage() {
2277  return "apihelp-{$this->getModulePath()}-summary";
2278  }
2279 
2289  protected function getExtendedDescription() {
2290  return [ [
2291  "apihelp-{$this->getModulePath()}-extended-description",
2292  'api-help-no-extended-description',
2293  ] ];
2294  }
2295 
2302  public function getFinalSummary() {
2303  $msg = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
2304  $this->getModulePrefix(),
2305  $this->getModuleName(),
2306  $this->getModulePath(),
2307  ] );
2308  return $msg;
2309  }
2310 
2318  public function getFinalDescription() {
2319  $summary = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
2320  $this->getModulePrefix(),
2321  $this->getModuleName(),
2322  $this->getModulePath(),
2323  ] );
2324  $extendedDescription = self::makeMessage(
2325  $this->getExtendedDescription(), $this->getContext(), [
2326  $this->getModulePrefix(),
2327  $this->getModuleName(),
2328  $this->getModulePath(),
2329  ]
2330  );
2331 
2332  $msgs = [ $summary, $extendedDescription ];
2333 
2334  Hooks::run( 'APIGetDescriptionMessages', [ $this, &$msgs ] );
2335 
2336  return $msgs;
2337  }
2338 
2347  public function getFinalParams( $flags = 0 ) {
2348  $params = $this->getAllowedParams( $flags );
2349  if ( !$params ) {
2350  $params = [];
2351  }
2352 
2353  if ( $this->needsToken() ) {
2354  $params['token'] = [
2355  self::PARAM_TYPE => 'string',
2356  self::PARAM_REQUIRED => true,
2357  self::PARAM_SENSITIVE => true,
2358  self::PARAM_HELP_MSG => [
2359  'api-help-param-token',
2360  $this->needsToken(),
2361  ],
2362  ] + ( $params['token'] ?? [] );
2363  }
2364 
2365  // Avoid PHP 7.1 warning of passing $this by reference
2366  $apiModule = $this;
2367  Hooks::run( 'APIGetAllowedParams', [ &$apiModule, &$params, $flags ] );
2368 
2369  return $params;
2370  }
2371 
2379  public function getFinalParamDescription() {
2380  $prefix = $this->getModulePrefix();
2381  $name = $this->getModuleName();
2382  $path = $this->getModulePath();
2383 
2384  $params = $this->getFinalParams( self::GET_VALUES_FOR_HELP );
2385  $msgs = [];
2386  foreach ( $params as $param => $settings ) {
2387  if ( !is_array( $settings ) ) {
2388  $settings = [];
2389  }
2390 
2391  if ( isset( $settings[self::PARAM_HELP_MSG] ) ) {
2392  $msg = $settings[self::PARAM_HELP_MSG];
2393  } else {
2394  $msg = $this->msg( "apihelp-{$path}-param-{$param}" );
2395  }
2396  $msg = self::makeMessage( $msg, $this->getContext(),
2397  [ $prefix, $param, $name, $path ] );
2398  if ( !$msg ) {
2399  self::dieDebug( __METHOD__,
2400  'Value in ApiBase::PARAM_HELP_MSG is not valid' );
2401  }
2402  $msgs[$param] = [ $msg ];
2403 
2404  if ( isset( $settings[self::PARAM_TYPE] ) &&
2405  $settings[self::PARAM_TYPE] === 'submodule'
2406  ) {
2407  if ( isset( $settings[self::PARAM_SUBMODULE_MAP] ) ) {
2408  $map = $settings[self::PARAM_SUBMODULE_MAP];
2409  } else {
2410  $prefix = $this->isMain() ? '' : ( $this->getModulePath() . '+' );
2411  $map = [];
2412  foreach ( $this->getModuleManager()->getNames( $param ) as $submoduleName ) {
2413  $map[$submoduleName] = $prefix . $submoduleName;
2414  }
2415  }
2416  ksort( $map );
2417  $submodules = [];
2418  $deprecatedSubmodules = [];
2419  foreach ( $map as $v => $m ) {
2420  $arr = &$submodules;
2421  $isDeprecated = false;
2422  $summary = null;
2423  try {
2424  $submod = $this->getModuleFromPath( $m );
2425  if ( $submod ) {
2426  $summary = $submod->getFinalSummary();
2427  $isDeprecated = $submod->isDeprecated();
2428  if ( $isDeprecated ) {
2429  $arr = &$deprecatedSubmodules;
2430  }
2431  }
2432  } catch ( ApiUsageException $ex ) {
2433  // Ignore
2434  }
2435  if ( $summary ) {
2436  $key = $summary->getKey();
2437  $params = $summary->getParams();
2438  } else {
2439  $key = 'api-help-undocumented-module';
2440  $params = [ $m ];
2441  }
2442  $m = new ApiHelpParamValueMessage( "[[Special:ApiHelp/$m|$v]]", $key, $params, $isDeprecated );
2443  $arr[] = $m->setContext( $this->getContext() );
2444  }
2445  $msgs[$param] = array_merge( $msgs[$param], $submodules, $deprecatedSubmodules );
2446  } elseif ( isset( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
2447  if ( !is_array( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
2448  self::dieDebug( __METHOD__,
2449  'ApiBase::PARAM_HELP_MSG_PER_VALUE is not valid' );
2450  }
2451  if ( !is_array( $settings[self::PARAM_TYPE] ) ) {
2452  self::dieDebug( __METHOD__,
2453  'ApiBase::PARAM_HELP_MSG_PER_VALUE may only be used when ' .
2454  'ApiBase::PARAM_TYPE is an array' );
2455  }
2456 
2457  $valueMsgs = $settings[self::PARAM_HELP_MSG_PER_VALUE];
2458  $deprecatedValues = $settings[self::PARAM_DEPRECATED_VALUES] ?? [];
2459 
2460  foreach ( $settings[self::PARAM_TYPE] as $value ) {
2461  if ( isset( $valueMsgs[$value] ) ) {
2462  $msg = $valueMsgs[$value];
2463  } else {
2464  $msg = "apihelp-{$path}-paramvalue-{$param}-{$value}";
2465  }
2466  $m = self::makeMessage( $msg, $this->getContext(),
2467  [ $prefix, $param, $name, $path, $value ] );
2468  if ( $m ) {
2469  $m = new ApiHelpParamValueMessage(
2470  $value,
2471  // @phan-suppress-next-line PhanTypeMismatchArgument
2472  [ $m->getKey(), 'api-help-param-no-description' ],
2473  $m->getParams(),
2474  isset( $deprecatedValues[$value] )
2475  );
2476  $msgs[$param][] = $m->setContext( $this->getContext() );
2477  } else {
2478  self::dieDebug( __METHOD__,
2479  "Value in ApiBase::PARAM_HELP_MSG_PER_VALUE for $value is not valid" );
2480  }
2481  }
2482  }
2483 
2484  if ( isset( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
2485  if ( !is_array( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
2486  self::dieDebug( __METHOD__,
2487  'Value for ApiBase::PARAM_HELP_MSG_APPEND is not an array' );
2488  }
2489  foreach ( $settings[self::PARAM_HELP_MSG_APPEND] as $m ) {
2490  $m = self::makeMessage( $m, $this->getContext(),
2491  [ $prefix, $param, $name, $path ] );
2492  if ( $m ) {
2493  $msgs[$param][] = $m;
2494  } else {
2495  self::dieDebug( __METHOD__,
2496  'Value in ApiBase::PARAM_HELP_MSG_APPEND is not valid' );
2497  }
2498  }
2499  }
2500  }
2501 
2502  Hooks::run( 'APIGetParamDescriptionMessages', [ $this, &$msgs ] );
2503 
2504  return $msgs;
2505  }
2506 
2516  protected function getHelpFlags() {
2517  $flags = [];
2518 
2519  if ( $this->isDeprecated() ) {
2520  $flags[] = 'deprecated';
2521  }
2522  if ( $this->isInternal() ) {
2523  $flags[] = 'internal';
2524  }
2525  if ( $this->isReadMode() ) {
2526  $flags[] = 'readrights';
2527  }
2528  if ( $this->isWriteMode() ) {
2529  $flags[] = 'writerights';
2530  }
2531  if ( $this->mustBePosted() ) {
2532  $flags[] = 'mustbeposted';
2533  }
2534 
2535  return $flags;
2536  }
2537 
2549  protected function getModuleSourceInfo() {
2550  global $IP;
2551 
2552  if ( $this->mModuleSource !== false ) {
2553  return $this->mModuleSource;
2554  }
2555 
2556  // First, try to find where the module comes from...
2557  $rClass = new ReflectionClass( $this );
2558  $path = $rClass->getFileName();
2559  if ( !$path ) {
2560  // No path known?
2561  $this->mModuleSource = null;
2562  return null;
2563  }
2564  $path = realpath( $path ) ?: $path;
2565 
2566  // Build map of extension directories to extension info
2567  if ( self::$extensionInfo === null ) {
2568  $extDir = $this->getConfig()->get( 'ExtensionDirectory' );
2569  self::$extensionInfo = [
2570  realpath( __DIR__ ) ?: __DIR__ => [
2571  'path' => $IP,
2572  'name' => 'MediaWiki',
2573  'license-name' => 'GPL-2.0-or-later',
2574  ],
2575  realpath( "$IP/extensions" ) ?: "$IP/extensions" => null,
2576  realpath( $extDir ) ?: $extDir => null,
2577  ];
2578  $keep = [
2579  'path' => null,
2580  'name' => null,
2581  'namemsg' => null,
2582  'license-name' => null,
2583  ];
2584  foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $group ) {
2585  foreach ( $group as $ext ) {
2586  if ( !isset( $ext['path'] ) || !isset( $ext['name'] ) ) {
2587  // This shouldn't happen, but does anyway.
2588  continue;
2589  }
2590 
2591  $extpath = $ext['path'];
2592  if ( !is_dir( $extpath ) ) {
2593  $extpath = dirname( $extpath );
2594  }
2595  self::$extensionInfo[realpath( $extpath ) ?: $extpath] =
2596  array_intersect_key( $ext, $keep );
2597  }
2598  }
2599  foreach ( ExtensionRegistry::getInstance()->getAllThings() as $ext ) {
2600  $extpath = $ext['path'];
2601  if ( !is_dir( $extpath ) ) {
2602  $extpath = dirname( $extpath );
2603  }
2604  self::$extensionInfo[realpath( $extpath ) ?: $extpath] =
2605  array_intersect_key( $ext, $keep );
2606  }
2607  }
2608 
2609  // Now traverse parent directories until we find a match or run out of
2610  // parents.
2611  do {
2612  if ( array_key_exists( $path, self::$extensionInfo ) ) {
2613  // Found it!
2614  $this->mModuleSource = self::$extensionInfo[$path];
2615  return $this->mModuleSource;
2616  }
2617 
2618  $oldpath = $path;
2619  $path = dirname( $path );
2620  } while ( $path !== $oldpath );
2621 
2622  // No idea what extension this might be.
2623  $this->mModuleSource = null;
2624  return null;
2625  }
2626 
2638  public function modifyHelp( array &$help, array $options, array &$tocData ) {
2639  }
2640 
2642 }
2643 
$filter
$filter
Definition: profileinfo.php:344
ApiMain
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:41
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:33
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:63
ApiBase\PARAM_SUBMODULE_MAP
const PARAM_SUBMODULE_MAP
(string[]) When PARAM_TYPE is 'submodule', map parameter values to submodule paths.
Definition: ApiBase.php:172
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:316
ApiUsageException
Exception used to abort API execution with an error.
Definition: ApiUsageException.php:28
ContextSource\getContext
getContext()
Get the base IContextSource object.
Definition: ContextSource.php:40
ApiBase\$mMainModule
ApiMain $mMainModule
Definition: ApiBase.php:290
StatusValue
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: StatusValue.php:42
ApiBase\getParent
getParent()
Get the parent of this module.
Definition: ApiBase.php:554
ApiBase\addWarning
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition: ApiBase.php:1933
ApiBase\getSummaryMessage
getSummaryMessage()
Return the summary message.
Definition: ApiBase.php:2276
ApiBase\getFinalParams
getFinalParams( $flags=0)
Get final list of parameters, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:2347
ApiBase\PARAM_REQUIRED
const PARAM_REQUIRED
(boolean) Is the parameter required?
Definition: ApiBase.php:118
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:117
ApiBase\parameterNotEmpty
parameterNotEmpty( $x)
Callback function used in requireOnlyOneParameter to check whether required parameters are set.
Definition: ApiBase.php:1019
ApiBase\$mModuleSource
array null bool $mModuleSource
Definition: ApiBase.php:296
ApiBase\validateToken
validateToken( $token, array $params)
Validate the supplied token.
Definition: ApiBase.php:1671
ApiContinuationManager
This manages continuation state.
Definition: ApiContinuationManager.php:26
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:2014
ApiBase\PARAM_HELP_MSG
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:131
ApiBase\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiBase.php:369
ApiBase\PARAM_ALL
const PARAM_ALL
(boolean|string) When PARAM_TYPE has a defined set of values and PARAM_ISMULTI is true,...
Definition: ApiBase.php:187
ApiBase\validateTimestamp
validateTimestamp( $value, $encParamName)
Validate and normalize parameters of type 'timestamp'.
Definition: ApiBase.php:1634
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1869
ApiBase\$extensionInfo
static array $extensionInfo
Maps extension paths to info arrays.
Definition: ApiBase.php:275
ApiBase\getTitleOrPageId
getTitleOrPageId( $params, $load=false)
Get a WikiPage object from a title or pageid param, if possible.
Definition: ApiBase.php:1034
ApiBase\PARAM_TYPE
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition: ApiBase.php:94
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:640
ApiBase\__construct
__construct(ApiMain $mainModule, $moduleName, $modulePrefix='')
Definition: ApiBase.php:303
ApiBase\$blockMsgMap
static $blockMsgMap
$var array Map of web UI block messages to corresponding API messages and codes
Definition: ApiBase.php:281
wfUrlencode
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
Definition: GlobalFunctions.php:309
ApiBase\mustBePosted
mustBePosted()
Indicates whether this module must be called with a POST request.
Definition: ApiBase.php:435
ApiBase\logFeatureUsage
logFeatureUsage( $feature)
Write logging information for API features to a debug log, for usage analysis.
Definition: ApiBase.php:2230
ApiBase\checkUserRightsAny
checkUserRightsAny( $rights, $user=null)
Helper function for permission-denied errors.
Definition: ApiBase.php:2130
ApiBase\shouldCheckMaxlag
shouldCheckMaxlag()
Indicates if this module needs maxlag to be checked.
Definition: ApiBase.php:404
ApiBase\dieWithErrorOrDebug
dieWithErrorOrDebug( $msg, $code=null, $data=null, $httpCode=null)
Will only set a warning instead of failing if the global $wgDebugAPI is set to true.
Definition: ApiBase.php:2191
ApiBase\setContinuationManager
setContinuationManager(ApiContinuationManager $manager=null)
Set the continuation manager.
Definition: ApiBase.php:694
ApiBase\getExtendedDescription
getExtendedDescription()
Return the extended help text message.
Definition: ApiBase.php:2289
ApiBase\PARAM_ISMULTI_LIMIT1
const PARAM_ISMULTI_LIMIT1
(integer) Maximum number of values, for normal users.
Definition: ApiBase.php:215
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:515
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1264
ApiBase\addBlockInfoToStatus
addBlockInfoToStatus(StatusValue $status, User $user=null)
Add block info to block messages in a Status.
Definition: ApiBase.php:1853
$s
$s
Definition: mergeMessageFileList.php:185
ApiBase\dieBlocked
dieBlocked(AbstractBlock $block)
Throw an ApiUsageException, which will (if uncaught) call the main module's error handler and die wit...
Definition: ApiBase.php:2055
ApiBase\getDB
getDB()
Gets a default replica DB connection object.
Definition: ApiBase.php:668
ApiBase\addMessagesFromStatus
addMessagesFromStatus(StatusValue $status, $types=[ 'warning', 'error'], array $filter=[])
Add warnings and/or errors from a Status.
Definition: ApiBase.php:1993
ApiBase\makeMessage
static makeMessage( $msg, IContextSource $context, array $params=null)
Create a Message from a string or array.
Definition: ApiBase.php:1800
ContextSource\getRequest
getRequest()
Definition: ContextSource.php:71
ApiBase\checkTitleUserPermissions
checkTitleUserPermissions(LinkTarget $linkTarget, $actions, $options=[])
Helper function for permission-denied errors.
Definition: ApiBase.php:2156
ApiBase\dynamicParameterDocumentation
dynamicParameterDocumentation()
Indicate if the module supports dynamically-determined parameters that cannot be included in self::ge...
Definition: ApiBase.php:728
ApiBase\modifyHelp
modifyHelp(array &$help, array $options, array &$tocData)
Called from ApiHelp before the pieces are joined together and returned.
Definition: ApiBase.php:2638
ApiBase\PARAM_ALLOW_DUPLICATES
const PARAM_ALLOW_DUPLICATES
(boolean) Allow the same value to be set more than once when PARAM_ISMULTI is true?
Definition: ApiBase.php:109
Message
ContextSource\getUser
getUser()
Definition: ContextSource.php:120
ApiBase\PARAM_DEPRECATED_VALUES
const PARAM_DEPRECATED_VALUES
(array) When PARAM_TYPE is an array, this indicates which of the values are deprecated.
Definition: ApiBase.php:209
ApiBase\lacksSameOriginSecurity
lacksSameOriginSecurity()
Returns true if the current request breaks the same-origin policy.
Definition: ApiBase.php:568
ApiBase\PARAM_HELP_MSG_APPEND
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition: ApiBase.php:138
ApiUsageException\newWithMessage
static newWithMessage(ApiBase $module=null, $msg, $code=null, $data=null, $httpCode=0)
Definition: ApiUsageException.php:63
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1007
ApiBase\getWebUITokenSalt
getWebUITokenSalt(array $params)
Fetch the salt used in the Web UI corresponding to this module.
Definition: ApiBase.php:489
ApiBase\isMain
isMain()
Returns true if this module is the main module ($this === $this->mMainModule), false otherwise.
Definition: ApiBase.php:545
ApiBase\isReadMode
isReadMode()
Indicates whether this module requires read rights.
Definition: ApiBase.php:412
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:42
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
ApiBase\getModuleFromPath
getModuleFromPath( $path)
Get a module from its module path.
Definition: ApiBase.php:602
ApiBase\getFinalParamDescription
getFinalParamDescription()
Get final parameter descriptions, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:2379
ApiMessage
Extension of Message implementing IApiMessage.
Definition: ApiMessage.php:26
ApiBase\PARAM_SENSITIVE
const PARAM_SENSITIVE
(boolean) Is the parameter sensitive? Note 'password'-type fields are always sensitive regardless of ...
Definition: ApiBase.php:200
ApiBase\PARAM_ISMULTI_LIMIT2
const PARAM_ISMULTI_LIMIT2
(integer) Maximum number of values, for users with the apihighimits right.
Definition: ApiBase.php:222
ExtensionRegistry\getInstance
static getInstance()
Definition: ExtensionRegistry.php:106
ApiBase\getFinalSummary
getFinalSummary()
Get final module summary.
Definition: ApiBase.php:2302
ApiBase\PARAM_DEPRECATED
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
Definition: ApiBase.php:112
ApiBase\explodeMultiValue
explodeMultiValue( $value, $limit)
Split a multi-valued parameter string, like explode()
Definition: ApiBase.php:1469
MediaWiki\Block\DatabaseBlock
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Definition: DatabaseBlock.php:54
ApiBase\PARAM_MIN
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition: ApiBase.php:106
MWException
MediaWiki exception.
Definition: MWException.php:26
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:142
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1044
ApiBase\warnOrDie
warnOrDie(ApiMessage $msg, $enforceLimits=false)
Adds a warning to the output, else dies.
Definition: ApiBase.php:2039
ApiBase\getCustomPrinter
getCustomPrinter()
If the module may only be used with a certain format module, it should override this method to return...
Definition: ApiBase.php:354
ApiBase\getFinalDescription
getFinalDescription()
Get final module description, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:2318
wfTransactionalTimeLimit
wfTransactionalTimeLimit()
Set PHP's time limit to the larger of php.ini or $wgTransactionalTimeLimit.
Definition: GlobalFunctions.php:2796
ApiBase\getModulePath
getModulePath()
Get the path to this module.
Definition: ApiBase.php:584
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2575
ApiBase\LIMIT_BIG1
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:259
ContextSource
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
Definition: ContextSource.php:29
ApiQueryTokens\getTokenTypeSalts
static getTokenTypeSalts()
Get the salts for known token types.
Definition: ApiQueryTokens.php:63
$IP
$IP
Definition: update.php:3
ApiBase\PARAM_MAX
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition: ApiBase.php:97
ApiBase\dieWithException
dieWithException( $exception, array $options=[])
Abort execution with an error derived from an exception.
Definition: ApiBase.php:2026
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:761
WatchAction\doWatchOrUnwatch
static doWatchOrUnwatch( $watch, Title $title, User $user)
Watch or unwatch a page.
Definition: WatchAction.php:91
ApiBase\handleParamNormalization
handleParamNormalization( $paramName, $value, $rawValue)
Handle when a parameter was Unicode-normalized.
Definition: ApiBase.php:1457
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
ApiBase\isDeprecated
isDeprecated()
Indicates whether this module is deprecated.
Definition: ApiBase.php:444
ApiBase\$mReplicaDB
$mReplicaDB
Definition: ApiBase.php:293
MediaWiki\Block\AbstractBlock\getType
getType()
Get the type of target for this particular block.
Definition: AbstractBlock.php:419
MediaWiki\Block\AbstractBlock\isSitewide
isSitewide( $x=null)
Indicates that the block is a sitewide block.
Definition: AbstractBlock.php:203
ApiMessage\create
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:40
ApiBase\PARAM_EXTRA_NAMESPACES
const PARAM_EXTRA_NAMESPACES
(int[]) When PARAM_TYPE is 'namespace', include these as additional possible values.
Definition: ApiBase.php:193
ApiBase\ALL_DEFAULT_STRING
const ALL_DEFAULT_STRING
Definition: ApiBase.php:256
ContextSource\setContext
setContext(IContextSource $context)
Definition: ContextSource.php:55
ApiBase\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiBase.php:378
ApiBase\validateUser
validateUser( $value, $encParamName)
Validate and normalize parameters of type 'user'.
Definition: ApiBase.php:1706
ApiBase\getWatchlistValue
getWatchlistValue( $watchlist, $titleObj, $userOption=null)
Return true if we're to watch the page, false if not, null if no change.
Definition: ApiBase.php:1099
ApiBase\needsToken
needsToken()
Returns the token type this module requires in order to execute.
Definition: ApiBase.php:476
ApiBase\getModulePrefix
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition: ApiBase.php:528
ApiBase\$mModuleName
string $mModuleName
Definition: ApiBase.php:292
ApiBase\setWatch
setWatch( $watch, $titleObj, $userOption=null)
Set a watch (or unwatch) based the based on a watchlist parameter.
Definition: ApiBase.php:1750
ApiBase\getContinuationManager
getContinuationManager()
Get the continuation manager.
Definition: ApiBase.php:680
ContextSource\msg
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:168
ApiBase\addError
addError( $msg, $code=null, $data=null)
Add an error for this module without aborting.
Definition: ApiBase.php:1980
MediaWiki\Permissions\PermissionManager
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Definition: PermissionManager.php:47
ApiBase\getWatchlistUser
getWatchlistUser( $params)
Gets the user for whom to get the watchlist.
Definition: ApiBase.php:1765
RE_IP_BYTE
const RE_IP_BYTE
Definition: IP.php:29
ApiBase\dieContinueUsageIf
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition: ApiBase.php:2208
ApiBase\addDeprecation
addDeprecation( $msg, $feature, $data=[])
Add a deprecation warning for this module.
Definition: ApiBase.php:1947
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
ApiBase\LIMIT_SML2
const LIMIT_SML2
Slow query, apihighlimits limit.
Definition: ApiBase.php:265
ApiBase\encodeParamName
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition: ApiBase.php:739
ApiBase\dieReadOnly
dieReadOnly()
Helper function for readonly errors.
Definition: ApiBase.php:2114
ApiBase\GET_VALUES_FOR_HELP
const GET_VALUES_FOR_HELP
getAllowedParams() flag: When set, the result could take longer to generate, but should be more thoro...
Definition: ApiBase.php:272
WikiPage\newFromID
static newFromID( $id, $from='fromdb')
Constructor from a page id.
Definition: WikiPage.php:180
ApiBase\getPermissionManager
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition: ApiBase.php:710
ApiBase\useTransactionalTimeLimit
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition: ApiBase.php:1871
ApiBase\getConditionalRequestData
getConditionalRequestData( $condition)
Returns data for HTTP conditional request mechanisms.
Definition: ApiBase.php:505
ApiBase\isWriteMode
isWriteMode()
Indicates whether this module requires write mode.
Definition: ApiBase.php:427
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1551
ApiBase\requireMaxOneParameter
requireMaxOneParameter( $params, $required)
Die if more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:931
ApiBase\requireOnlyOneParameter
requireOnlyOneParameter( $params, $required)
Die if none or more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:893
ApiBase\PARAM_HELP_MSG_INFO
const PARAM_HELP_MSG_INFO
(array) Specify additional information tags for the parameter.
Definition: ApiBase.php:148
ApiBase\$mParamCache
$mParamCache
Definition: ApiBase.php:294
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
ApiBase\PARAM_RANGE_ENFORCE
const PARAM_RANGE_ENFORCE
(boolean) For PARAM_TYPE 'integer', enforce PARAM_MIN and PARAM_MAX?
Definition: ApiBase.php:124
ApiBase\PARAM_VALUE_LINKS
const PARAM_VALUE_LINKS
(string[]) When PARAM_TYPE is an array, this may be an array mapping those values to page titles whic...
Definition: ApiBase.php:155
ChangeTags\canAddTagsAccompanyingChange
static canAddTagsAccompanyingChange(array $tags, User $user=null)
Is it OK to allow the user to apply all the specified tags at the same time as they edit/make the cha...
Definition: ChangeTags.php:521
ApiBase\requireAtLeastOneParameter
requireAtLeastOneParameter( $params, $required)
Die if none of a certain set of parameters is set and not false.
Definition: ApiBase.php:959
IP\sanitizeIP
static sanitizeIP( $ip)
Convert an IP into a verbose, uppercase, normalized form.
Definition: IP.php:139
$status
return $status
Definition: SyntaxHighlight.php:347
ApiBase\PARAM_TEMPLATE_VARS
const PARAM_TEMPLATE_VARS
(array) Indicate that this is a templated parameter, and specify replacements.
Definition: ApiBase.php:252
ApiBase\filterIDs
filterIDs( $fields, array $ids)
Filter out-of-range values from a list of positive integer IDs.
Definition: ApiBase.php:1885
ApiBase\LIMIT_BIG2
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:261
wfReadOnlyReason
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
Definition: GlobalFunctions.php:1184
ApiQueryTokens\getToken
static getToken(User $user, MediaWiki\Session\Session $session, $salt)
Get a token from a salt.
Definition: ApiQueryTokens.php:94
ApiBase\getModuleManager
getModuleManager()
Get the module manager, or null if this module has no sub-modules.
Definition: ApiBase.php:341
ApiBase\isInternal
isInternal()
Indicates whether this module is "internal" Internal API modules are not (yet) intended for 3rd party...
Definition: ApiBase.php:454
ApiBase\getModuleSourceInfo
getModuleSourceInfo()
Returns information about the source of this module, if known.
Definition: ApiBase.php:2549
User\getCanonicalName
static getCanonicalName( $name, $validate='valid')
Given unvalidated user input, return a canonical username, or false if the username is invalid.
Definition: User.php:1139
ApiBase\validateLimit
validateLimit( $paramName, &$value, $min, $max, $botMax=null, $enforceLimits=false)
Validate the value against the minimum and user/bot maximum limits.
Definition: ApiBase.php:1578
$path
$path
Definition: NoLocalSettings.php:25
ApiBase\PARAM_DFLT
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:55
ApiBase\getParameter
getParameter( $paramName, $parseLimit=true)
Get a value for the given parameter.
Definition: ApiBase.php:876
ApiBase\dieStatus
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition: ApiBase.php:2086
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:520
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:58
ApiBase\PARAM_MAX2
const PARAM_MAX2
(integer) Max value allowed for the parameter for users with the apihighlimits right,...
Definition: ApiBase.php:103
ApiBase\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiBase.php:394
ApiBase\parseMultiValue
parseMultiValue( $valueName, $value, $allowMultiple, $allowedValues, $allSpecifier=null, $limit1=null, $limit2=null)
Return an array of values that were given in a 'a|b|c' notation, after it optionally validates them a...
Definition: ApiBase.php:1497
$help
$help
Definition: mcc.php:32
ApiBase\getMain
getMain()
Get the main module.
Definition: ApiBase.php:536
ApiBase\getParameterFromSettings
getParameterFromSettings( $paramName, $paramSettings, $parseLimit)
Using the settings determine the value for the given parameter.
Definition: ApiBase.php:1140
$ext
if(!is_readable( $file)) $ext
Definition: router.php:48
ApiBase\PARAM_MAX_CHARS
const PARAM_MAX_CHARS
(integer) Maximum length of a string in characters (unicode codepoints).
Definition: ApiBase.php:234
MediaWiki\Block\AbstractBlock
Definition: AbstractBlock.php:34
ApiBase\getTitleFromTitleOrPageId
getTitleFromTitleOrPageId( $params)
Get a Title object from a title or pageid param, if possible.
Definition: ApiBase.php:1071
User\IGNORE_USER_RIGHTS
const IGNORE_USER_RIGHTS
Definition: User.php:83
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
ApiBase\$mModulePrefix
string $mModulePrefix
Definition: ApiBase.php:292
RawMessage
Variant of the Message class.
Definition: RawMessage.php:34
ApiBase\PARAM_HELP_MSG_PER_VALUE
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition: ApiBase.php:164
ApiBase\PARAM_SUBMODULE_PARAM_PREFIX
const PARAM_SUBMODULE_PARAM_PREFIX
(string) When PARAM_TYPE is 'submodule', used to indicate the 'g' prefix added by ApiQueryGeneratorBa...
Definition: ApiBase.php:179
ApiBase\PARAM_MAX_BYTES
const PARAM_MAX_BYTES
(integer) Maximum length of a string in bytes (in UTF-8 encoding).
Definition: ApiBase.php:228
ApiHelpParamValueMessage
Message subclass that prepends wikitext for API help.
Definition: ApiHelpParamValueMessage.php:33
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:51
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:467
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
ApiBase\dieDebug
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:2220
ExternalUserNames\isExternal
static isExternal( $username)
Tells whether the username is external or not.
Definition: ExternalUserNames.php:137
ApiBase\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
ApiBase\errorArrayToStatus
errorArrayToStatus(array $errors, User $user=null)
Turn an array of message keys or key+param arrays into a Status.
Definition: ApiBase.php:1825
ApiBase\getErrorFormatter
getErrorFormatter()
Get the error formatter.
Definition: ApiBase.php:654
ApiBase\requirePostedParameters
requirePostedParameters( $params, $prefix='prefix')
Die if any of the specified parameters were found in the query part of the URL rather than the post b...
Definition: ApiBase.php:989
IP\isIPAddress
static isIPAddress( $ip)
Determine if a string is as valid IP address or network (CIDR prefix).
Definition: IP.php:77
ApiBase\LIMIT_SML1
const LIMIT_SML1
Slow query, standard limit.
Definition: ApiBase.php:263
ApiBase\getHelpFlags
getHelpFlags()
Generates the list of flags for the help screen and for action=paraminfo.
Definition: ApiBase.php:2516
$type
$type
Definition: testCompression.php:48
ApiBase\$filterIDsCache
static stdClass[][] $filterIDsCache
Cache for self::filterIDs()
Definition: ApiBase.php:278