# Building & testing
npm-debug.log
node_modules/
+/resources/lib/.foreign
/tests/phpunit/phpunit.phar
/tests/selenium/log
.eslintcache
--- /dev/null
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+$cfg = require __DIR__ . '/../vendor/mediawiki/mediawiki-phan-config/src/config.php';
+
+$cfg['file_list'] = array_merge(
+ $cfg['file_list'],
+ function_exists( 'register_postsend_function' ) ? [] : [ '.phan/stubs/hhvm.php' ],
+ function_exists( 'wikidiff2_do_diff' ) ? [] : [ '.phan/stubs/wikidiff.php' ],
+ class_exists( PEAR::class ) ? [] : [ '.phan/stubs/mail.php' ],
+ defined( 'PASSWORD_ARGON2I' ) ? [] : [ '.phan/stubs/password.php' ],
+ // Per composer.json, PHPUnit 6 is used for PHP 7.0+, PHPUnit 4 otherwise.
+ // Load the interface for the version of PHPUnit that isn't installed.
+ // Phan only supports PHP 7.0+ (and not HHVM), so we only need to stub PHPUnit 4.
+ class_exists( PHPUnit_TextUI_Command::class ) ? [] : [ '.phan/stubs/phpunit4.php' ],
+ class_exists( ProfilerExcimer::class ) ? [] : [ '.phan/stubs/excimer.php' ],
+ [
+ 'maintenance/7zip.inc',
+ 'maintenance/cleanupTable.inc',
+ 'maintenance/CodeCleanerGlobalsPass.inc',
+ 'maintenance/commandLine.inc',
+ 'maintenance/sqlite.inc',
+ 'maintenance/userDupes.inc',
+ 'maintenance/language/checkLanguage.inc',
+ 'maintenance/language/languages.inc',
+ ]
+);
+
+$cfg['autoload_internal_extension_signatures'] = [
+ 'memcached' => '.phan/internal_stubs/memcached.phan_php',
+ 'oci8' => '.phan/internal_stubs/oci8.phan_php',
+ 'sqlsrv' => '.phan/internal_stubs/sqlsrv.phan_php',
+ 'tideways' => '.phan/internal_stubs/tideways.phan_php',
+];
+
+$cfg['directory_list'] = [
+ 'includes/',
+ 'languages/',
+ 'maintenance/',
+ 'mw-config/',
+ 'resources/',
+ 'vendor/',
+ '.phan/stubs/',
+];
+
+$cfg['exclude_analysis_directory_list'] = [
+ 'vendor/',
+ '.phan/stubs/',
+ // The referenced classes are not available in vendor, only when
+ // included from composer.
+ 'includes/composer/',
+ // Directly references classes that only exist in Translate extension
+ 'maintenance/language/',
+ // External class
+ 'includes/libs/jsminplus.php',
+];
+
+$cfg['suppress_issue_types'] = array_merge( $cfg['suppress_issue_types'], [
+ // approximate error count: 18
+ "PhanAccessMethodInternal",
+ // approximate error count: 17
+ "PhanCommentParamOnEmptyParamList",
+ // approximate error count: 30
+ "PhanCommentParamWithoutRealParam",
+ // approximate error count: 2
+ "PhanCompatibleNegativeStringOffset",
+ // approximate error count: 1
+ "PhanEmptyFQSENInCallable",
+ // approximate error count: 1
+ "PhanInvalidCommentForDeclarationType",
+ // approximate error count: 6
+ "PhanNonClassMethodCall",
+ // approximate error count: 21
+ "PhanParamReqAfterOpt",
+ // approximate error count: 27
+ "PhanParamSignatureMismatch",
+ // approximate error count: 4
+ "PhanParamSignatureMismatchInternal",
+ // approximate error count: 1
+ "PhanParamSignatureRealMismatchTooFewParameters",
+ // approximate error count: 1
+ "PhanParamSuspiciousOrder",
+ // approximate error count: 127
+ "PhanParamTooMany",
+ // approximate error count: 2
+ "PhanParamTooManyCallable",
+ // approximate error count: 1
+ "PhanParamTooManyInternal",
+ // approximate error count: 2
+ "PhanPluginDuplicateExpressionBinaryOp",
+ // approximate error count: 2
+ "PhanTraitParentReference",
+ // approximate error count: 27
+ "PhanTypeArraySuspicious",
+ // approximate error count: 33
+ "PhanTypeArraySuspiciousNullable",
+ // approximate error count: 26
+ "PhanTypeComparisonFromArray",
+ // approximate error count: 2
+ "PhanTypeComparisonToArray",
+ // approximate error count: 1
+ "PhanTypeConversionFromArray",
+ // approximate error count: 2
+ "PhanTypeExpectedObjectOrClassName",
+ // approximate error count: 7
+ "PhanTypeExpectedObjectPropAccess",
+ // approximate error count: 3
+ "PhanTypeInstantiateAbstract",
+ // approximate error count: 1
+ "PhanTypeInvalidCallableArraySize",
+ // approximate error count: 62
+ "PhanTypeInvalidDimOffset",
+ // approximate error count: 10
+ "PhanTypeInvalidExpressionArrayDestructuring",
+ // approximate error count: 1
+ "PhanTypeInvalidLeftOperand",
+ // approximate error count: 7
+ "PhanTypeInvalidLeftOperandOfIntegerOp",
+ // approximate error count: 2
+ "PhanTypeInvalidRightOperand",
+ // approximate error count: 2
+ "PhanTypeInvalidRightOperandOfIntegerOp",
+ // approximate error count: 1
+ "PhanTypeMagicVoidWithReturn",
+ // approximate error count: 152
+ "PhanTypeMismatchArgument",
+ // approximate error count: 28
+ "PhanTypeMismatchArgumentInternal",
+ // approximate error count: 1
+ "PhanTypeMismatchBitwiseBinaryOperands",
+ // approximate error count: 1
+ "PhanTypeMismatchDeclaredParam",
+ // approximate error count: 2
+ "PhanTypeMismatchDimEmpty",
+ // approximate error count: 29
+ "PhanTypeMismatchDimFetch",
+ // approximate error count: 10
+ "PhanTypeMismatchForeach",
+ // approximate error count: 77
+ "PhanTypeMismatchProperty",
+ // approximate error count: 88
+ "PhanTypeMismatchReturn",
+ // approximate error count: 43
+ "PhanTypeMissingReturn",
+ // approximate error count: 1
+ "PhanTypeNoAccessiblePropertiesForeach",
+ // approximate error count: 4
+ "PhanTypeNonVarPassByRef",
+ // approximate error count: 12
+ "PhanTypeObjectUnsetDeclaredProperty",
+ // approximate error count: 9
+ "PhanTypeSuspiciousNonTraversableForeach",
+ // approximate error count: 3
+ "PhanTypeSuspiciousStringExpression",
+ // approximate error count: 22
+ "PhanUndeclaredConstant",
+ // approximate error count: 3
+ "PhanUndeclaredInvokeInCallable",
+ // approximate error count: 242
+ "PhanUndeclaredMethod",
+ // approximate error count: 847
+ "PhanUndeclaredProperty",
+ // approximate error count: 1
+ "PhanUndeclaredTypeReturnType",
+ // approximate error count: 3
+ "PhanUndeclaredTypeThrowsType",
+ // approximate error count: 2
+ "PhanUndeclaredVariableAssignOp",
+ // approximate error count: 55
+ "PhanUndeclaredVariableDim",
+ // approximate error count: 4
+ "PhanUnextractableAnnotationElementName",
+ // approximate error count: 4
+ "PhanUnextractableAnnotationSuffix",
+] );
+
+$cfg['ignore_undeclared_variables_in_global_scope'] = true;
+$cfg['globals_type_map']['IP'] = 'string';
+
+return $cfg;
--- /dev/null
+<?php
+// These stubs were generated by the phan stub generator.
+// @phan-stub-for-extension memcached@3.0.1
+
+namespace {
+class Memcached {
+
+ // constants
+ const LIBMEMCACHED_VERSION_HEX = 16777240;
+ const OPT_COMPRESSION = -1001;
+ const OPT_COMPRESSION_TYPE = -1004;
+ const OPT_PREFIX_KEY = -1002;
+ const OPT_SERIALIZER = -1003;
+ const OPT_USER_FLAGS = -1006;
+ const OPT_STORE_RETRY_COUNT = -1005;
+ const HAVE_IGBINARY = true;
+ const HAVE_JSON = true;
+ const HAVE_MSGPACK = true;
+ const HAVE_SESSION = true;
+ const HAVE_SASL = true;
+ const OPT_HASH = 2;
+ const HASH_DEFAULT = 0;
+ const HASH_MD5 = 1;
+ const HASH_CRC = 2;
+ const HASH_FNV1_64 = 3;
+ const HASH_FNV1A_64 = 4;
+ const HASH_FNV1_32 = 5;
+ const HASH_FNV1A_32 = 6;
+ const HASH_HSIEH = 7;
+ const HASH_MURMUR = 8;
+ const OPT_DISTRIBUTION = 9;
+ const DISTRIBUTION_MODULA = 0;
+ const DISTRIBUTION_CONSISTENT = 1;
+ const DISTRIBUTION_VIRTUAL_BUCKET = 6;
+ const OPT_LIBKETAMA_COMPATIBLE = 16;
+ const OPT_LIBKETAMA_HASH = 17;
+ const OPT_TCP_KEEPALIVE = 32;
+ const OPT_BUFFER_WRITES = 10;
+ const OPT_BINARY_PROTOCOL = 18;
+ const OPT_NO_BLOCK = 0;
+ const OPT_TCP_NODELAY = 1;
+ const OPT_SOCKET_SEND_SIZE = 4;
+ const OPT_SOCKET_RECV_SIZE = 5;
+ const OPT_CONNECT_TIMEOUT = 14;
+ const OPT_RETRY_TIMEOUT = 15;
+ const OPT_DEAD_TIMEOUT = 36;
+ const OPT_SEND_TIMEOUT = 19;
+ const OPT_RECV_TIMEOUT = 20;
+ const OPT_POLL_TIMEOUT = 8;
+ const OPT_CACHE_LOOKUPS = 6;
+ const OPT_SERVER_FAILURE_LIMIT = 21;
+ const OPT_AUTO_EJECT_HOSTS = 28;
+ const OPT_HASH_WITH_PREFIX_KEY = 25;
+ const OPT_NOREPLY = 26;
+ const OPT_SORT_HOSTS = 12;
+ const OPT_VERIFY_KEY = 13;
+ const OPT_USE_UDP = 27;
+ const OPT_NUMBER_OF_REPLICAS = 29;
+ const OPT_RANDOMIZE_REPLICA_READ = 30;
+ const OPT_REMOVE_FAILED_SERVERS = 35;
+ const OPT_SERVER_TIMEOUT_LIMIT = 37;
+ const RES_SUCCESS = 0;
+ const RES_FAILURE = 1;
+ const RES_HOST_LOOKUP_FAILURE = 2;
+ const RES_UNKNOWN_READ_FAILURE = 7;
+ const RES_PROTOCOL_ERROR = 8;
+ const RES_CLIENT_ERROR = 9;
+ const RES_SERVER_ERROR = 10;
+ const RES_WRITE_FAILURE = 5;
+ const RES_DATA_EXISTS = 12;
+ const RES_NOTSTORED = 14;
+ const RES_NOTFOUND = 16;
+ const RES_PARTIAL_READ = 18;
+ const RES_SOME_ERRORS = 19;
+ const RES_NO_SERVERS = 20;
+ const RES_END = 21;
+ const RES_ERRNO = 26;
+ const RES_BUFFERED = 32;
+ const RES_TIMEOUT = 31;
+ const RES_BAD_KEY_PROVIDED = 33;
+ const RES_STORED = 15;
+ const RES_DELETED = 22;
+ const RES_STAT = 24;
+ const RES_ITEM = 25;
+ const RES_NOT_SUPPORTED = 28;
+ const RES_FETCH_NOTFINISHED = 30;
+ const RES_SERVER_MARKED_DEAD = 35;
+ const RES_UNKNOWN_STAT_KEY = 36;
+ const RES_INVALID_HOST_PROTOCOL = 34;
+ const RES_MEMORY_ALLOCATION_FAILURE = 17;
+ const RES_CONNECTION_SOCKET_CREATE_FAILURE = 11;
+ const RES_E2BIG = 37;
+ const RES_KEY_TOO_BIG = 39;
+ const RES_SERVER_TEMPORARILY_DISABLED = 47;
+ const RES_SERVER_MEMORY_ALLOCATION_FAILURE = 48;
+ const RES_AUTH_PROBLEM = 40;
+ const RES_AUTH_FAILURE = 41;
+ const RES_AUTH_CONTINUE = 42;
+ const RES_PAYLOAD_FAILURE = -1001;
+ const SERIALIZER_PHP = 1;
+ const SERIALIZER_IGBINARY = 2;
+ const SERIALIZER_JSON = 3;
+ const SERIALIZER_JSON_ARRAY = 4;
+ const SERIALIZER_MSGPACK = 5;
+ const COMPRESSION_FASTLZ = 2;
+ const COMPRESSION_ZLIB = 1;
+ const GET_PRESERVE_ORDER = 1;
+ const GET_EXTENDED = 2;
+ const GET_ERROR_RETURN_VALUE = false;
+
+ // methods
+ public function __construct($persistent_id = null, $callback = null) {}
+ public function getResultCode() {}
+ public function getResultMessage() {}
+ public function get($key, $cache_cb = null, $get_flags = null) {}
+ public function getByKey($server_key, $key, $cache_cb = null, $get_flags = null) {}
+ public function getMulti(array $keys, $get_flags = null) {}
+ public function getMultiByKey($server_key, array $keys, $get_flags = null) {}
+ public function getDelayed(array $keys, $with_cas = null, $value_cb = null) {}
+ public function getDelayedByKey($server_key, array $keys, $with_cas = null, $value_cb = null) {}
+ public function fetch() {}
+ public function fetchAll() {}
+ public function set($key, $value, $expiration = null) {}
+ public function setByKey($server_key, $key, $value, $expiration = null) {}
+ public function touch($key, $expiration) {}
+ public function touchByKey($server_key, $key, $expiration) {}
+ public function setMulti(array $items, $expiration = null) {}
+ public function setMultiByKey($server_key, array $items, $expiration = null) {}
+ public function cas($cas_token, $key, $value, $expiration = null) {}
+ public function casByKey($cas_token, $server_key, $key, $value, $expiration = null) {}
+ public function add($key, $value, $expiration = null) {}
+ public function addByKey($server_key, $key, $value, $expiration = null) {}
+ public function append($key, $value, $expiration = null) {}
+ public function appendByKey($server_key, $key, $value, $expiration = null) {}
+ public function prepend($key, $value, $expiration = null) {}
+ public function prependByKey($server_key, $key, $value, $expiration = null) {}
+ public function replace($key, $value, $expiration = null) {}
+ public function replaceByKey($server_key, $key, $value, $expiration = null) {}
+ public function delete($key, $time = null) {}
+ public function deleteMulti($keys, $time = null) {}
+ public function deleteByKey($server_key, $key, $time = null) {}
+ public function deleteMultiByKey($server_key, $keys, $time = null) {}
+ public function increment($key, $offset = null, $initial_value = null, $expiry = null) {}
+ public function decrement($key, $offset = null, $initial_value = null, $expiry = null) {}
+ public function incrementByKey($server_key, $key, $offset = null, $initial_value = null, $expiry = null) {}
+ public function decrementByKey($server_key, $key, $offset = null, $initial_value = null, $expiry = null) {}
+ public function addServer($host, $port, $weight = null) {}
+ public function addServers(array $servers) {}
+ public function getServerList() {}
+ public function getServerByKey($server_key) {}
+ public function resetServerList() {}
+ public function quit() {}
+ public function flushBuffers() {}
+ public function getLastErrorMessage() {}
+ public function getLastErrorCode() {}
+ public function getLastErrorErrno() {}
+ public function getLastDisconnectedServer() {}
+ public function getStats($args) {}
+ public function getVersion() {}
+ public function getAllKeys() {}
+ public function flush($delay = null) {}
+ public function getOption($option) {}
+ public function setOption($option, $value) {}
+ public function setOptions($options) {}
+ public function setBucket($host_map, $forward_map, $replicas) {}
+ public function setSaslAuthData($username, $password) {}
+ public function isPersistent() {}
+ public function isPristine() {}
+}
+
+class MemcachedException extends \RuntimeException {
+
+ // properties
+ protected $message;
+ protected $code;
+ protected $file;
+ protected $line;
+}
+
+}
--- /dev/null
+<?php
+
+// @phan-stub-for-extension oci8@2.0.7
+
+
+class OCI_Lob {
+
+
+ public function load () {}
+
+
+ public function tell () {}
+
+
+ public function truncate ($length = 0) {}
+
+
+ public function erase ($offset = null, $length = null) {}
+
+
+ public function flush ($flag = null) {}
+
+
+ public function setbuffering ($on_off) {}
+
+
+ public function getbuffering () {}
+
+
+ public function rewind () {}
+
+
+ public function read ($length) {}
+
+
+ public function eof () {}
+
+
+ public function seek ($offset, $whence = OCI_SEEK_SET) {}
+
+
+ public function write ($data, $length = null) {}
+
+
+ public function append (OCI_Lob $lob_from) {}
+
+
+ public function size () {}
+
+
+ public function writetofile ($filename, $start, $length) {}
+
+
+ public function export ($filename, $start = null, $length = null) {}
+
+
+ public function import ($filename) {}
+
+
+ public function writeTemporary ($data, $lob_type = OCI_TEMP_CLOB) {}
+
+
+ public function close () {}
+
+
+ public function save ($data, $offset = null) {}
+
+
+ public function savefile ($filename) {}
+
+
+ public function free () {}
+
+}
+
+
+class OCI_Collection {
+
+
+ public function append ($value) {}
+
+
+ public function getelem ($index) {}
+
+
+ public function assignelem ($index, $value) {}
+
+
+ public function assign (OCI_Collection $from) {}
+
+
+ public function size () {}
+
+
+ public function max () {}
+
+
+ public function trim ($num) {}
+
+
+ public function free () {}
+
+}
+
+
+function oci_define_by_name ($statement, $column_name, &$variable, $type = SQLT_CHR) {}
+
+
+function oci_bind_by_name ($statement, $bv_name, &$variable, $maxlength = -1, $type = SQLT_CHR) {}
+
+
+function oci_bind_array_by_name ($statement, $name, array &$var_array, $max_table_length, $max_item_length = -1, $type = SQLT_AFC) {}
+
+
+function oci_field_is_null ($statement, $field) {}
+
+
+function oci_field_name ($statement, $field) {}
+
+
+function oci_field_size ($statement, $field) {}
+
+
+function oci_field_scale ($statement, $field) {}
+
+
+function oci_field_precision ($statement, $field) {}
+
+
+function oci_field_type ($statement, $field) {}
+
+
+function oci_field_type_raw ($statement, $field) {}
+
+
+function oci_execute ($statement, $mode = OCI_COMMIT_ON_SUCCESS) {}
+
+
+function oci_cancel ($statement) {}
+
+
+function oci_fetch ($statement) {}
+
+
+function oci_fetch_object ($statement) {}
+
+
+function oci_fetch_row ($statement) {}
+
+
+function oci_fetch_assoc ($statement) {}
+
+
+function oci_fetch_array ($statement, $mode = null) {}
+
+
+function ocifetchinto ($statement_resource, &$result, $mode = null) {}
+
+
+function oci_fetch_all ($statement, array &$output, $skip = 0, $maxrows = -1, $flags = OCI_FETCHSTATEMENT_BY_COLUMN | OCI_ASSOC) {}
+
+
+function oci_free_statement ($statement) {}
+
+
+function oci_internal_debug ($onoff) {}
+
+
+function oci_num_fields ($statement) {}
+
+
+function oci_parse ($connection, $sql_text) {}
+
+
+function oci_get_implicit_resultset ($statement) {}
+
+
+function oci_new_cursor ($connection) {}
+
+
+function oci_result ($statement, $field) {}
+
+
+function oci_client_version () {}
+
+
+function oci_server_version ($connection) {}
+
+
+function oci_statement_type ($statement) {}
+
+
+function oci_num_rows ($statement) {}
+
+
+function oci_close ($connection) {}
+
+
+function oci_connect ($username, $password, $connection_string = null, $character_set = null, $session_mode = null) {}
+
+
+function oci_new_connect ($username, $password, $connection_string = null, $character_set = null, $session_mode = null) {}
+
+
+function oci_pconnect ($username, $password, $connection_string = null, $character_set = null, $session_mode = null) {}
+
+
+function oci_error ($resource = null) {}
+
+
+function oci_free_descriptor ($descriptor) {}
+
+
+function oci_lob_is_equal (OCI_Lob $lob1, OCI_Lob $lob2) {}
+
+
+function oci_lob_copy (OCI_Lob $lob_to, OCI_Lob $lob_from, $length = 0) {}
+
+
+function oci_commit ($connection) {}
+
+
+function oci_rollback ($connection) {}
+
+
+function oci_new_descriptor ($connection, $type = OCI_DTYPE_LOB) {}
+
+
+function oci_set_prefetch ($statement, $rows) {}
+
+
+function oci_set_client_identifier ($connection, $client_identifier) {}
+
+
+function oci_set_edition ($edition) {}
+
+
+function oci_set_module_name ($connection, $module_name) {}
+
+
+function oci_set_action ($connection, $action_name) {}
+
+
+function oci_set_client_info ($connection, $client_info) {}
+
+
+function oci_password_change ($connection, $username, $old_password, $new_password) {}
+
+
+function oci_new_collection ($connection, $tdo, $schema = null) {}
+
+
+function oci_free_cursor ($statement_resource) {}
+
+
+function ocifreecursor ($statement_resource) {}
+
+
+function ocibindbyname ($statement, $column_name, &$variable, $maximum_length = -1, $type = SQLT_CHR) {}
+
+
+function ocidefinebyname ($statement, $column_name, &$variable, $type = SQLT_CHR) {}
+
+
+function ocicolumnisnull ($statement, $column_number_or_name) {}
+
+
+function ocicolumnname ($statement, $column_number) {}
+
+
+function ocicolumnsize ($statement, $column_number_or_name) {}
+
+
+function ocicolumnscale ($statement_resource, $column_number) {}
+
+
+function ocicolumnprecision ($statement_resource, $column_number) {}
+
+
+function ocicolumntype ($statement_resource, $column_number) {}
+
+
+function ocicolumntyperaw ($statement_resource, $column_number) {}
+
+
+function ociexecute ($statement_resource, $mode = OCI_COMMIT_ON_SUCCESS) {}
+
+
+function ocicancel ($statement_resource) {}
+
+
+function ocifetch ($statement_resource) {}
+
+
+function ocifetchstatement ($statement_resource, &$output, $skip, $maximum_rows, $flags) {}
+
+
+function ocifreestatement ($statement_resource) {}
+
+
+function ociinternaldebug ($mode) {}
+
+
+function ocinumcols ($statement_resource) {}
+
+
+function ociparse ($connection_resource, $sql_text) {}
+
+
+function ocinewcursor ($connection_resource) {}
+
+
+function ociresult ($statement_resource, $column_number_or_name) {}
+
+
+function ociserverversion ($connection_resource) {}
+
+
+function ocistatementtype ($statement_resource) {}
+
+
+function ocirowcount ($statement_resource) {}
+
+
+function ocilogoff ($connection_resource) {}
+
+
+function ocilogon ($username, $password, $connection_string, $character_set, $session_mode) {}
+
+
+function ocinlogon ($username, $password, $connection_string, $character_set, $session_mode) {}
+
+
+function ociplogon ($username, $password, $connection_string, $character_set, $session_mode) {}
+
+
+function ocierror ($connection_or_statement_resource) {}
+
+
+function ocifreedesc ($lob_descriptor) {}
+
+
+function ocisavelob ($lob_descriptor, $data, $offset) {}
+
+
+function ocisavelobfile ($lob_descriptor, $filename) {}
+
+
+function ociwritelobtofile ($lob_descriptor, $filename, $start, $length) {}
+
+
+function ociloadlob ($lob_descriptor) {}
+
+
+function ocicommit ($connection_resource) {}
+
+
+function ocirollback ($connection_resource) {}
+
+
+function ocinewdescriptor ($connection_resource, $type = OCI_DTYPE_LOB) {}
+
+
+function ocisetprefetch ($statement_resource, $number_of_rows) {}
+
+
+function ocipasswordchange ($connection_resource_or_connection_string_or_dbname, $username, $old_password, $new_password) {}
+
+
+function ocifreecollection ($collection) {}
+
+
+function ocinewcollection ($connection_resource, $tdo, $schema = null) {}
+
+
+function ocicollappend ($collection, $value) {}
+
+
+function ocicollgetelem ($collection, $index) {}
+
+
+function ocicollassignelem ($collection, $index, $value) {}
+
+
+function ocicollsize ($collection) {}
+
+
+function ocicollmax ($collection) {}
+
+
+function ocicolltrim ($collection, $number) {}
+
+
+
+function ociwritetemporarylob($lob_descriptor, $data, $lob_type = OCI_TEMP_CLOB ) {}
+
+
+function ocicloselob($lob_descriptor){}
+
+
+function ocicollassign($to, $from ) {}
+
+define ('OCI_DEFAULT', 0);
+
+
+define ('OCI_SYSOPER', 4);
+
+
+define ('OCI_SYSDBA', 2);
+
+
+define ('OCI_CRED_EXT', -2147483648);
+
+
+define ('OCI_DESCRIBE_ONLY', 16);
+
+
+define ('OCI_COMMIT_ON_SUCCESS', 32);
+
+
+define ('OCI_NO_AUTO_COMMIT', 0);
+
+
+define ('OCI_EXACT_FETCH', 2);
+
+
+define ('OCI_SEEK_SET', 0);
+
+
+define ('OCI_SEEK_CUR', 1);
+
+
+define ('OCI_SEEK_END', 2);
+
+
+define ('OCI_LOB_BUFFER_FREE', 1);
+
+
+define ('SQLT_BFILEE', 114);
+
+
+define ('SQLT_CFILEE', 115);
+
+
+define ('SQLT_CLOB', 112);
+
+
+define ('SQLT_BLOB', 113);
+
+
+define ('SQLT_RDD', 104);
+
+
+define ('SQLT_INT', 3);
+
+
+define ('SQLT_NUM', 2);
+
+
+define ('SQLT_RSET', 116);
+
+
+define ('SQLT_AFC', 96);
+
+
+define ('SQLT_CHR', 1);
+
+
+define ('SQLT_VCS', 9);
+
+
+define ('SQLT_AVC', 97);
+
+
+define ('SQLT_STR', 5);
+
+
+define ('SQLT_LVC', 94);
+
+
+define ('SQLT_FLT', 4);
+
+
+define ('SQLT_UIN', 68);
+
+
+define ('SQLT_LNG', 8);
+
+
+define ('SQLT_LBI', 24);
+
+
+define ('SQLT_BIN', 23);
+
+
+define ('SQLT_ODT', 156);
+
+
+define ('SQLT_BDOUBLE', 22);
+
+
+define ('SQLT_BFLOAT', 21);
+
+
+define ('OCI_B_NTY', 108);
+
+
+define ('SQLT_NTY', 108);
+
+
+define ('OCI_SYSDATE', "SYSDATE");
+
+
+define ('OCI_B_BFILE', 114);
+
+
+define ('OCI_B_CFILEE', 115);
+
+
+define ('OCI_B_CLOB', 112);
+
+
+define ('OCI_B_BLOB', 113);
+
+
+define ('OCI_B_ROWID', 104);
+
+
+define ('OCI_B_CURSOR', 116);
+
+
+define ('OCI_B_BIN', 23);
+
+
+define ('OCI_B_INT', 3);
+
+
+define ('OCI_B_NUM', 2);
+
+
+define ('OCI_FETCHSTATEMENT_BY_COLUMN', 16);
+
+
+define ('OCI_FETCHSTATEMENT_BY_ROW', 32);
+
+
+define ('OCI_ASSOC', 1);
+
+
+define ('OCI_NUM', 2);
+
+
+define ('OCI_BOTH', 3);
+
+
+define ('OCI_RETURN_NULLS', 4);
+
+
+define ('OCI_RETURN_LOBS', 8);
+
+
+define ('OCI_DTYPE_FILE', 56);
+
+
+define ('OCI_DTYPE_LOB', 50);
+
+
+define ('OCI_DTYPE_ROWID', 54);
+
+
+define ('OCI_D_FILE', 56);
+
+
+define ('OCI_D_LOB', 50);
+
+
+define ('OCI_D_ROWID', 54);
+
+
+define ('OCI_TEMP_CLOB', 2);
+
+
+define ('OCI_TEMP_BLOB', 1);
+
+
+define ('SQLT_BOL', 252);
+
+
+define ('OCI_B_BOL', 252);
--- /dev/null
+<?php
+// @phan-stub-for-extension sqlsrv@3.0.1
+
+define('SQLSRV_ERR_ERRORS', 0);
+
+
+define('SQLSRV_ERR_WARNINGS', 1);
+
+
+define('SQLSRV_ERR_ALL', 2);
+
+
+define('SQLSRV_LOG_SYSTEM_ALL',-1);
+
+
+define('SQLSRV_LOG_SYSTEM_OFF', 0);
+
+
+define('SQLSRV_LOG_SYSTEM_INIT', 1);
+
+
+define('SQLSRV_LOG_SYSTEM_CONN', 2);
+
+
+define('SQLSRV_LOG_SYSTEM_STMT', 4);
+
+
+define('SQLSRV_LOG_SYSTEM_UTIL', 8);
+
+
+define('SQLSRV_LOG_SEVERITY_ALL', -1);
+
+
+define('SQLSRV_LOG_SEVERITY_ERROR', 1);
+
+
+define('SQLSRV_LOG_SEVERITY_NOTICE', 4);
+
+
+define('SQLSRV_LOG_SEVERITY_WARNING', 2);
+
+
+define('SQLSRV_FETCH_NUMERIC', 1);
+
+
+define('SQLSRV_FETCH_ASSOC', 2);
+
+
+define('SQLSRV_FETCH_BOTH', 3);
+
+
+define('SQLSRV_PHPTYPE_NULL', 1);
+
+
+define('SQLSRV_PHPTYPE_INT', 2);
+
+
+define('SQLSRV_PHPTYPE_FLOAT', 3);
+
+
+define('SQLSRV_PHPTYPE_DATETIME', 4);
+
+
+define('SQLSRV_ENC_BINARY', 'binary');
+
+
+define('SQLSRV_ENC_CHAR','char');
+
+
+define('SQLSRV_NULLABLE_NO', 0);
+
+
+define('SQLSRV_NULLABLE_YES', 1);
+
+
+define('SQLSRV_NULLABLE_UNKNOWN', 2);
+
+
+define('SQLSRV_SQLTYPE_BIGINT', -5);
+
+define('SQLSRV_SQLTYPE_BIT', -7);
+
+define('SQLSRV_SQLTYPE_DATETIME', 25177693);
+
+define('SQLSRV_SQLTYPE_FLOAT', 6);
+
+define('SQLSRV_SQLTYPE_IMAGE', -4);
+
+define('SQLSRV_SQLTYPE_INT', 4);
+
+define('SQLSRV_SQLTYPE_MONEY', 33564163);
+
+define('SQLSRV_SQLTYPE_NTEXT', -10);
+
+define('SQLSRV_SQLTYPE_TEXT', -1);
+
+define('SQLSRV_SQLTYPE_REAL', 7);
+
+define('SQLSRV_SQLTYPE_SMALLDATETIME', 8285);
+
+define('SQLSRV_SQLTYPE_SMALLINT', 5);
+
+define('SQLSRV_SQLTYPE_SMALLMONEY', 33559555);
+
+define('SQLSRV_SQLTYPE_TIMESTAMP', 4606);
+
+define('SQLSRV_SQLTYPE_TINYINT', -6);
+
+define('SQLSRV_SQLTYPE_UDT', -151);
+
+define('SQLSRV_SQLTYPE_UNIQUEIDENTIFIER', -11);
+
+define('SQLSRV_SQLTYPE_XML', -152);
+
+define('SQLSRV_SQLTYPE_DATE', 5211);
+
+define('SQLSRV_SQLTYPE_TIME', 58728806);
+
+define('SQLSRV_SQLTYPE_DATETIMEOFFSET', 58738021);
+
+define('SQLSRV_SQLTYPE_DATETIME2', 58734173);
+
+
+define('SQLSRV_PARAM_IN', 1);
+
+
+define('SQLSRV_PARAM_INOUT', 2);
+
+
+define('SQLSRV_PARAM_OUT', 4);
+
+
+define('SQLSRV_TXN_READ_UNCOMMITTED', 1);
+
+define('SQLSRV_TXN_READ_COMMITTED', 2);
+
+define('SQLSRV_TXN_REPEATABLE_READ', 4);
+
+define('SQLSRV_TXN_SERIALIZABLE', 8);
+
+define('SQLSRV_TXN_SNAPSHOT', 32);
+
+
+define('SQLSRV_SCROLL_NEXT', 1);
+
+define('SQLSRV_SCROLL_PRIOR', 4);
+
+define('SQLSRV_SCROLL_FIRST', 2);
+
+define('SQLSRV_SCROLL_LAST', 3);
+
+define('SQLSRV_SCROLL_ABSOLUTE', 5);
+
+define('SQLSRV_SCROLL_RELATIVE', 6);
+
+
+define('SQLSRV_CURSOR_FORWARD', 'forward');
+
+define('SQLSRV_CURSOR_STATIC', 'static');
+
+define('SQLSRV_CURSOR_DYNAMIC', 'dynamic');
+
+define('SQLSRV_CURSOR_KEYSET', 'keyset');
+
+define('SQLSRV_CURSOR_CLIENT_BUFFERED', 'buffered');
+
+
+
+function sqlsrv_connect($server_name, $connection_info = array()){}
+
+
+function sqlsrv_close($conn){}
+
+
+function sqlsrv_commit($conn){}
+
+
+function sqlsrv_begin_transaction($conn){}
+
+
+function sqlsrv_rollback($conn){}
+
+
+function sqlsrv_errors($errorsAndOrWarnings = SQLSRV_ERR_ALL){}
+
+
+function sqlsrv_configure($setting, $value){}
+
+
+function sqlsrv_get_config($setting){}
+
+
+function sqlsrv_prepare($conn, $tsql, $params=array(), $options=array()){}
+
+
+function sqlsrv_execute($stmt){}
+
+
+function sqlsrv_query($conn, $tsql, $params=array(), $options=array()){}
+
+
+function sqlsrv_fetch($stmt, $row=null, $offset=null){}
+
+
+function sqlsrv_get_field($stmt, $field_index, $get_as_type){}
+
+
+function sqlsrv_fetch_array($stmt, $fetch_type = null, $row=null, $offset=null){}
+
+
+function sqlsrv_fetch_object($stmt, $class_name=null, $ctor_params=null, $row=null, $offset=null){}
+
+
+function sqlsrv_has_rows($stmt){}
+
+
+function sqlsrv_num_fields($stmt){}
+
+
+function sqlsrv_next_result($stmt){}
+
+
+function sqlsrv_num_rows($stmt){}
+
+
+function sqlsrv_rows_affected($stmt){}
+
+
+function sqlsrv_client_info($conn){}
+
+
+function sqlsrv_server_info($conn){}
+
+
+function sqlsrv_cancel($stmt){}
+
+
+function sqlsrv_free_stmt($stmt){}
+
+
+function sqlsrv_field_metadata($stmt){}
+
+
+function sqlsrv_send_stream_data($stmt){}
+
+
+function SQLSRV_PHPTYPE_STREAM($encoding){}
+
+
+function SQLSRV_PHPTYPE_STRING($encoding){}
+
+
+function SQLSRV_SQLTYPE_BINARY($byteCount){}
+
+
+function SQLSRV_SQLTYPE_VARBINARY($byteCount){}
+
+
+
+function SQLSRV_SQLTYPE_VARCHAR($charCount) {}
+
+
+function SQLSRV_SQLTYPE_CHAR($charCount){}
+
+
+function SQLSRV_SQLTYPE_NCHAR($charCount){}
+
+
+function SQLSRV_SQLTYPE_NVARCHAR($charCount){}
+
+
+function SQLSRV_SQLTYPE_DECIMAL($precision, $scale){}
+
+
+function SQLSRV_SQLTYPE_NUMERIC($precision, $scale){}
+
--- /dev/null
+<?php
+// These stubs were generated by the phan stub generator.
+// @phan-stub-for-extension tideways@4.0.7
+
+namespace {
+function tideways_disable() {}
+function tideways_enable($flags = null, $options = null) {}
+function tideways_fatal_backtrace() {}
+function tideways_get_spans() {}
+function tideways_last_detected_exception() {}
+function tideways_last_fatal_error() {}
+function tideways_prepend_overwritten() {}
+function tideways_span_annotate($span = null, $annotations = null) {}
+function tideways_span_callback($name = null, $callback = null) {}
+function tideways_span_create($category = null) {}
+function tideways_span_timer_start($span = null) {}
+function tideways_span_timer_stop($span = null) {}
+function tideways_span_watch($name = null, $category = null) {}
+function tideways_sql_minify($sql = null) {}
+function tideways_transaction_name() {}
+const TIDEWAYS_FLAGS_CPU = 2;
+const TIDEWAYS_FLAGS_MEMORY = 4;
+const TIDEWAYS_FLAGS_NO_BUILTINS = 1;
+const TIDEWAYS_FLAGS_NO_COMPILE = 16;
+const TIDEWAYS_FLAGS_NO_HIERACHICAL = 64;
+const TIDEWAYS_FLAGS_NO_SPANS = 32;
+const TIDEWAYS_FLAGS_NO_USERLAND = 8;
+}
--- /dev/null
+These stubs describe how code that is not available at analysis time should be
+used. No implementations are necessary, just define the classes and their
+methods and use phpdoc to describe what arguments are allowed.
--- /dev/null
+<?php
+
+// phpcs:ignoreFile
+
+define( 'EXCIMER_REAL', 0 );
+define( 'EXCIMER_CPU', 1 );
+
+class ExcimerProfiler {
+ public function __construct() {
+ }
+ public function setPeriod( $period ) {
+ }
+ public function setEventType( $event_type ) {
+ }
+ public function setMaxDepth( $maxDepth ) {
+ }
+ public function setFlushCallback( $callback, $max_samples ) {
+ }
+ public function clearFlushCallback() {
+ }
+ public function start() {
+ }
+ public function stop() {
+ }
+ public function getLog() {
+ }
+ public function flush() {
+ }
+}
+
+class ExcimerLog {
+ private final function __construct() {
+ }
+ function formatCollapsed() {
+ }
+ function aggregateByFunction() {
+ }
+ function getEventCount() {
+ }
+ function current() {
+ }
+ function key() {
+ }
+ function next() {
+ }
+ function rewind() {
+ }
+ function valid() {
+ }
+ function count() {
+ }
+ function offsetExists( $offset ) {
+ }
+ function offsetGet( $offset ) {
+ }
+ function offsetSet( $offset, $value ) {
+ }
+ function offsetUnset( $offset ) {
+ }
+
+}
+
+class ExcimerLogEntry {
+ private final function __construct() {
+ }
+ function getTimestamp() {
+ }
+ function getEventCount() {
+ }
+ function getTrace() {
+ }
+}
+
+class ExcimerTimer {
+ function setEventType( $event_type ) {
+ }
+ function setInterval( $interval ) {
+ }
+ function setPeriod( $period ) {
+ }
+ function setCallback( $callback ) {
+ }
+ function start() {
+ }
+ function stop() {
+ }
+ function getTime() {
+ }
+}
--- /dev/null
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+// phpcs:ignoreFile
+
+define( 'HHVM_VERSION', '3.18.6-dev' );
+
+/**
+ * @param callable $callback
+ * @param mixed ...$parameters
+ */
+function register_postsend_function( $callback ) {
+}
--- /dev/null
+<?php
+
+/**
+ * Minimal set of classes necessary for UserMailer to be happy. Types
+ * taken from documentation at pear.php.net.
+ * phpcs:ignoreFile
+ */
+
+class PEAR {
+ /**
+ * @param mixed $data
+ * @return bool
+ */
+ public static function isError( $data ) {
+ }
+}
+
+class PEAR_Error {
+ /**
+ * @return string
+ */
+ public function getMessage() {
+ }
+}
+
+class Mail {
+ /**
+ * @param string $driver
+ * @param array $params
+ * @return self
+ */
+ static public function factory( $driver, array $params = [] ) {
+ }
+
+ /**
+ * @param mixed $recipients
+ * @param array $headers
+ * @param string $body
+ * @return bool|PEAR_Error
+ */
+ public function send( $recipients, array $headers, $body ) {
+ }
+}
+
+class Mail_smtp extends Mail {
+}
+
+class Mail_mime {
+ /**
+ * @param mixed $params
+ */
+ public function __construct( $params = [] ) {
+ }
+
+ /**
+ * @param string $data
+ * @param bool $isfile
+ * @param bool $append
+ * @return bool|PEAR_Error
+ */
+ public function setTXTBody( $data, $isfile = false, $append = false ) {
+ }
+
+ /**
+ * @param string $data
+ * @param bool $isfile
+ * @return bool|PEAR_Error
+ */
+ public function setHTMLBody( $data, $isfile = false ) {
+ }
+
+ /**
+ * @param array|null $parms
+ * @param mixed $filename
+ * @param bool $skip_head
+ * @return string|bool|PEAR_Error
+ */
+ public function get( $params = null, $filename = null, $skip_head = false ) {
+ }
+
+ /**
+ * @param array|null $xtra_headers
+ * @param bool $overwrite
+ * @param bool $skip_content
+ * @return array
+ */
+ public function headers( array $xtra_headers = null, $overwrite = false, $skip_content = false ) {
+ }
+}
--- /dev/null
+<?php
+// phpcs:ignoreFile
+
+// Password constants added in PHP 7.2 & 7.3
+
+const PASSWORD_ARGON2I = 2;
+const PASSWORD_ARGON2ID = 3;
+const PASSWORD_ARGON2_DEFAULT_MEMORY_COST = 1024;
+const PASSWORD_ARGON2_DEFAULT_THREADS = 2;
+const PASSWORD_ARGON2_DEFAULT_TIME_COST = 2;
+
--- /dev/null
+<?php
+
+/**
+ * Some old classes from PHPUnit 4 that MediaWiki (conditionally) references.
+ *
+ * phpcs:ignoreFile
+ */
+
+class PHPUnit_TextUI_Command {
+
+}
--- /dev/null
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+// phpcs:ignoreFile
+
+/**
+ * @param string $text1
+ * @param string $text2
+ * @param int $numContextLines
+ * @param int $movedParagraphDetectionCutoff
+ * @return string
+ */
+function wikidiff2_do_diff( $text1, $text2, $numContextLines, $movedParagraphDetectionCutoff = 0 ) {
+}
+
+/**
+ * @param string $text1
+ * @param string $text2
+ * @param int $numContextLines
+ * @param int $maxMovedLines
+ * @return string
+ */
+function wikidiff2_inline_diff( $text1, $text2, $numContextLines, $maxMovedLines = 25 ) {
+}
any new occurrences.
-->
<exclude-pattern>*/includes/Feed\.php</exclude-pattern>
- <exclude-pattern>*/includes/RevisionList\.php</exclude-pattern>
<exclude-pattern>*/includes/installer/PhpBugTests\.php</exclude-pattern>
<exclude-pattern>*/includes/specials/SpecialMostinterwikis\.php</exclude-pattern>
<exclude-pattern>*/includes/compat/XMPReader\.php</exclude-pattern>
<exclude-pattern>*/includes/parser/Preprocessor_Hash\.php</exclude-pattern>
<exclude-pattern>*/includes/parser/Preprocessor\.php</exclude-pattern>
<exclude-pattern>*/includes/PathRouter\.php</exclude-pattern>
- <exclude-pattern>*/includes/poolcounter/PoolCounter\.php</exclude-pattern>
<exclude-pattern>*/includes/PrefixSearch\.php</exclude-pattern>
<exclude-pattern>*/includes/profiler/SectionProfiler\.php</exclude-pattern>
- <exclude-pattern>*/includes/RevisionList\.php</exclude-pattern>
<exclude-pattern>*/includes/search/SearchEngine\.php</exclude-pattern>
<exclude-pattern>*/includes/specialpage/LoginSignupSpecialPage\.php</exclude-pattern>
<exclude-pattern>*/includes/specials/forms/PreferencesFormLegacy\.php</exclude-pattern>
== MediaWiki 1.18 ==
+== MediaWiki 1.18.6 ==
+2012-11-29
+
+This is a maintenance and security release of the MediaWiki 1.18 branch
+
+=== Changes since 1.18.5 ===
+* ([[bugzilla:40995|bug 40995]]) Prevent session fixation in Special:UserLogin
+(CVE-2012-5391)
+* ([[bugzilla:41400|bug 41400]]) Prevent linker regex from exceeding PCRE
+backtrack limit
+* Localisation updates
+* Increase permitted runtime for testParserTest
+* ([[bugzilla:36179|bug 36179]]) Unquote 'null' for PostgreSQL.
+
== MediaWiki 1.18.5 ==
2012-08-30
the page
* list=exturlusage in "list all links" mode can now filter by protocol
+== MediaWiki 1.12 ==
+== MediaWiki 1.12.4 ==
-== MediaWiki 1.12 ==
+February 7, 2009
+
+A number of cross-site scripting (XSS) security vulnerabilities were discovered
+in the web-based installer (config/index.php). These vulnerabilities all
+require a live installer -- once the installer has been used to install a wiki,
+it is deactivated.
+
+Note that cross-site scripting vulnerabilities can be used to attack any
+website in the same cookie domain. So if you have an uninstalled copy of
+MediaWiki on the same site as an active web service, MediaWiki could be used to
+attack the active service.
+
+If you are hosting an old copy of MediaWiki that you have never installed, you
+are advised to remove it from the web.
+
+== MediaWiki 1.12.3 ==
+
+* Fixed packaging/distribution error. Many files were missing from the
+distributed tarball.
+
+== MediaWiki 1.12.2 ==
+
+David Remahl of Apple's Product Security team has identified a number of
+security issues in previous releases of MediaWiki. Subsequent analysis by the
+MediaWiki development team expanded the scope of these vulnerabilities. The
+issues with a significant impact are as follows:
+
+* A local script injection vulnerability affecting Internet Explorer clients
+for all MediaWiki installations with uploads enabled. [CVE-2008-5250]
+* A local script injection vulnerability affecting clients with SVG scripting
+capability (such as Firefox 1.5+), for all MediaWiki installations with SVG
+uploads enabled. [CVE-2008-5250]
+* A CSRF vulnerability affecting the Special:Import feature, for all MediaWiki
+installations since the feature was introduced in 1.3.0. [CVE-2008-5252]
+
+A local script injection vulnerability allows an attacker with a wiki account
+to steal another user's login session, and to act as that user on the wiki. The
+attacker uploads a malicious script file, and tricks the victim into executing
+it.
+
+CSRF vulnerabilities allow an attacker to act as an authorised user on the
+wiki, but unlike an XSS vulnerability, the attacker can only act as the user in
+a specific and restricted way. The present CSRF vulnerability allows pages to
+be edited, with forged revision histories. Like an XSS vulnerability, the
+authorised user must visit the malicious web page to activate the attack.
+
+These three vulnerabilities are all fixed in this release.
+
+David Remahl also reminded us of some security-related configuration issues:
+
+* By default, MediaWiki stores a backup of deleted images in the images/deleted
+directory. If you do not want these images to be publically accessible, make
+sure this directory is not accessible from the web. MediaWiki takes some steps
+to avoid leaking these images, but these measures are not perfect.
+* Set display_errors=off in your php.ini to avoid path disclosure via PHP fatal
+errors. This is the default on most shared web hosts.
+* Enabling MediaWiki's debugging features, such as $wgShowExceptionDetails, may
+lead to path disclosure.
+
+Other changes in this release:
+
+* Avoid fatal error in profileinfo.php when not configured.
+* Add a .htaccess to deleted images directory for additional protection against
+exposure of deleted files with known SHA-1 hashes on default installations.
+* Avoid streaming uploaded files to the user via index.php. This allows
+security-conscious users to serve uploaded files via a different domain, and
+thus client-side scripts executed from that domain cannot access the login
+cookies. Affects Special:Undelete, img_auth.php and thumb.php.
+* When streaming files via index.php, use the MIME type detected from the file
+extension, not from the data. This reduces the XSS attack surface.
+* Blacklist redirects via Special:Filepath. Such redirects exacerbate any XSS
+vulnerabilities involving uploads of files containing scripts.
+* Internationalisation updates.
+
+== MediaWiki 1.12.1 ==
+
+Changes since 1.12.0:
+* (bug [[bugzilla:13522|13522]]) Fix fatal error in Parser::extractTagsAndParams
+* (bug [[bugzilla:12077|12077]]) Fix HTML nesting for TOC
+* (bug [[bugzilla:13532|13532]]) Use proper timestamp call when reverting images
+* (bug [[bugzilla:13649|13649]], [[bugzilla:14084|14084]]) Bad call to
+wfTimestamp()
+* (bug [[bugzilla:13770|13770]]) Use Preprocessor_Hash by default to avoid
+missing DOM module errors
+* (bug [[bugzilla:13442|13442]]) API: Missing pages in prop=langlinks and
+prop=extlinks are now handled properly.
+* (bug [[bugzilla:13482|13482]]) API: Disabled search types handled properly
+* (bug [[bugzilla:13836|13836]]) API: Fixed fatal errors resulting from
+combining iiprop=metadata with format=xml
+* (bug [[bugzilla:11633|11633]]) API: Explicitly convert redirect titles to
+strings due to PHP's very weak typing on array keys.
+* API: Fixing main page display in meta=siteinfo
+* (bug [[bugzilla:11719|11719]]) API: Remove trailing blanks in YAML output.
+* (bug [[bugzilla:13718|13718]]) API: Return the proper continue parameter for
+cmsort=timestamp
+* Security: Work around misconfiguration by requiring strict comparisons for
+in_array in User::isAllowed().
+* Security: Fixed XSS vulnerability in useskin parameter.
+
+== MediaWiki 1.12.0 ==
+
+This is the quarterly branch release of [[MediaWiki]] for Winter 2008.
+
+MediaWiki is now using a "continuous integration" development model with
+quarterly snapshot releases. The latest development code is always kept "ready
+to run", and in fact runs our own sites on [[wikipedia:|Wikipedia]].
+
+Release branches will continue to receive security updates for about a year
+from first release, but nonessential bugfixes and feature developments will be
+made on the development trunk and appear in the next quarterly release.
+
+Those wishing to use the latest code instead of a branch release can obtain it
+from source control: [[Download from SVN]].
+
+Changes since 1.12.0rc1:
+*(bug [[bugzilla:13359|13359]]) Double-escaping in [[Special:Allpages]].
+*Localization updates.
+
+== MediaWiki 1.12.0rc1 ==
+
+This is a release candidate of the Winter 2008 quarterly snapshot release of
+[[MediaWiki]].
+
+MediaWiki is now using a "continuous integration" development model with
+quarterly snapshot releases. The latest development code is always kept "ready
+to run", and in fact runs our own sites on [[wikipedia:|Wikipedia]].
+
+Release branches will continue to receive security updates for about a year
+from first release, but nonessential bugfixes and feature developments will be
+made on the development trunk and appear in the next quarterly release.
+
+Those wishing to use the latest code instead of a branch release can obtain it
+from source control: [[Download from SVN]].
This is the Winter 2007 quarterly release.
== MediaWiki 1.11 ==
+== MediaWiki 1.11.2 ==
+
+March 2, 2008
+
+This is a security release of the Fall 2007 snapshot release of MediaWiki.
+Possible cross-site information leaks using the callback parameter for
+JSON-formatted results in the API are prevented by dropping user credentials.
+
+MediaWiki release versions prior to 1.11 are not vulnerable, as they do not
+include the callback feature which allows client-side JavaScript on other sites
+to reach API data.
+
+Changes in this release:
+
+* User credentials are dropped for API JSON requests using a callback
+* Edit tokens are not reported for API JSON requests using a callback
+
+== MediaWiki 1.11.1 ==
+
+January 23, 2008
+
+This is a security and bugfix release of the Fall 2007 snapshot release of
+ MediaWiki. A potential XSS injection vector affecting api.php only for
+ Microsoft Internet Explorer users has been closed.
+
+Changes in this release:
+* (bug [[bugzilla:11450|11450]]) Fix creation of objectcache table on upgrade
+* (bug [[bugzilla:11462|11462]]) Fix typo in LanguageGetSpecialPageAliases hook
+name
+* Fix regression in LinkBatch.php breaking PHP 5.0
+* Security fix for API on MSIE
+
+To work around the vulnerability without upgrading, you may disable the API if
+you don't need it:
+:[[Manual:$wgEnableAPI|$wgEnableAPI]] = false;
+
+Not vulnerable versions:
+* 1.12 or later
+* 1.11 >= 1.11.1
+* 1.10 >= 1.10.3
+* 1.9 >= 1.9.5
+* 1.8 any version (if $wgEnableAPI has been left off)
+
+Vulnerable versions:
+* 1.11 <= 1.11.0rc1
+* 1.10 <= 1.10.2
+* 1.9 <= 1.9.4
+* 1.8 any version (if $wgEnableAPI has been switched on)
+
+MediaWiki 1.7 and below are not affected as they do not include the API
+functionality, however the BotQuery extension is similarly vulnerable unless
+updated to the latest SVN version.
+
+== MediaWiki 1.11.0 ==
+
+September 10, 2007
+
+This is the Fall 2007 snapshot release of MediaWiki.
+
+MediaWiki is now using a "continuous integration" development model with
+quarterly snapshot releases. The latest development code is always kept "ready
+to run", and in fact runs our own sites on Wikipedia.
+
+Release branches will continue to receive security updates for about a year
+from first release, but nonessential bugfixes and feature developments will be
+made on the development trunk and appear in the next quarterly release.
+
+Those wishing to use the latest code instead of a branch release can obtain it
+from source control: [[Download from SVN]]
+
This is the Summer 2007 branch release of MediaWiki.
MediaWiki is now using a "continuous integration" development model with
Those wishing to use the latest code instead of a branch release can obtain
it from source control: https://www.mediawiki.org/wiki/Download_from_SVN
+== Changes since 1.11.0rc1 ==
+
+A possible HTML/XSS injection vector in the API pretty-printing mode has been
+found and fixed.
+
+The vulnerability may be worked around in an unfixed version by simply
+disabling the API interface if it is not in use, by adding this to
+[[Manual:LocalSettings.php|LocalSettings.php]]:<br />
+<code>[[Manual:$wgEnableAPI|$wgEnableAPI]] = false;</code> <br />
+(This is the default setting in 1.8.x.)
+
+Not vulnerable versions:
+* 1.11 >= 1.11.0
+* 1.10 >= 1.10.2
+* 1.9 >= 1.9.4
+* 1.8 >= 1.8.5
+
+Vulnerable versions:
+* 1.11 <= 1.11.0rc1
+* 1.10 <= 1.10.1
+* 1.9 <= 1.9.3
+* 1.8 <= 1.8.4 (if [[Manual:$wgEnableAPI|$wgEnableAPI]] has been switched on)
+
+MediaWiki 1.7 and below are not affected as they do not include the faulty
+function, however the [[Extension:BotQuery|BotQuery extension]] is similarly
+vulnerable unless updated to the latest SVN version.
+
== Configuration changes since 1.10 ==
* $wgThumbUpright - Adjust width of upright images when parameter 'upright' is
usergroups
* $wgEnotifImpersonal, $wgEnotifUseJobQ - Bulk mail options for large sites
* $wgShowHostnames - Expose server host names through the API and HTML comments
-* $wgSaveDeletedFiles has been removed, the feature is now enabled unconditionally
+* $wgSaveDeletedFiles has been removed, the feature is now enabled
+unconditionally
== New features since 1.10 ==
is no longer a problem, because the code now ensures the timestamp is always
higher than the previous one. The writes are guarded with CAS logic (check
and set), which prevents updates that would overlap.
+* $wgDBmysql5 (T196185) - This experimental setting, deprecated in 1.31, has
+ been removed.
=== New user-facing features in 1.33 ===
* (T96041) __EXPECTUNUSEDCATEGORY__ on a category page causes the category
'DumpFilter' => __DIR__ . '/includes/export/DumpFilter.php',
'DumpGZipOutput' => __DIR__ . '/includes/export/DumpGZipOutput.php',
'DumpIterator' => __DIR__ . '/maintenance/dumpIterator.php',
+ 'DumpLBZip2Output' => __DIR__ . '/includes/export/DumpLBZip2Output.php',
'DumpLatestFilter' => __DIR__ . '/includes/export/DumpLatestFilter.php',
'DumpLinks' => __DIR__ . '/maintenance/dumpLinks.php',
'DumpMessages' => __DIR__ . '/maintenance/language/dumpMessages.php',
'Maintenance' => __DIR__ . '/maintenance/Maintenance.php',
'MakeTestEdits' => __DIR__ . '/maintenance/makeTestEdits.php',
'MalformedTitleException' => __DIR__ . '/includes/title/MalformedTitleException.php',
- 'ManageForeignResources' => __DIR__ . '/maintenance/resources/manageForeignResources.php',
+ 'ManageForeignResources' => __DIR__ . '/maintenance/manageForeignResources.php',
'ManageJobs' => __DIR__ . '/maintenance/manageJobs.php',
'ManualLogEntry' => __DIR__ . '/includes/logging/LogEntry.php',
'MapCacheLRU' => __DIR__ . '/includes/libs/MapCacheLRU.php',
'PhpXmlBugTester' => __DIR__ . '/includes/installer/PhpBugTests.php',
'Pingback' => __DIR__ . '/includes/Pingback.php',
'PoolCounter' => __DIR__ . '/includes/poolcounter/PoolCounter.php',
+ 'PoolCounterNull' => __DIR__ . '/includes/poolcounter/PoolCounterNull.php',
'PoolCounterRedis' => __DIR__ . '/includes/poolcounter/PoolCounterRedis.php',
'PoolCounterWork' => __DIR__ . '/includes/poolcounter/PoolCounterWork.php',
'PoolCounterWorkViaCallback' => __DIR__ . '/includes/poolcounter/PoolCounterWorkViaCallback.php',
- 'PoolCounter_Stub' => __DIR__ . '/includes/poolcounter/PoolCounter.php',
'PoolWorkArticleView' => __DIR__ . '/includes/poolcounter/PoolWorkArticleView.php',
'PopulateArchiveRevId' => __DIR__ . '/maintenance/populateArchiveRevId.php',
'PopulateBacklinkNamespace' => __DIR__ . '/maintenance/populateBacklinkNamespace.php',
'Revision' => __DIR__ . '/includes/Revision.php',
'RevisionDeleteUser' => __DIR__ . '/includes/revisiondelete/RevisionDeleteUser.php',
'RevisionDeleter' => __DIR__ . '/includes/revisiondelete/RevisionDeleter.php',
- 'RevisionItem' => __DIR__ . '/includes/RevisionList.php',
- 'RevisionItemBase' => __DIR__ . '/includes/RevisionList.php',
- 'RevisionList' => __DIR__ . '/includes/RevisionList.php',
- 'RevisionListBase' => __DIR__ . '/includes/RevisionList.php',
+ 'RevisionItem' => __DIR__ . '/includes/revisionlist/RevisionItem.php',
+ 'RevisionItemBase' => __DIR__ . '/includes/revisionlist/RevisionItemBase.php',
+ 'RevisionList' => __DIR__ . '/includes/revisionlist/RevisionList.php',
+ 'RevisionListBase' => __DIR__ . '/includes/revisionlist/RevisionListBase.php',
'RiffExtractor' => __DIR__ . '/includes/libs/RiffExtractor.php',
'RightsLogFormatter' => __DIR__ . '/includes/logging/RightsLogFormatter.php',
'RollbackAction' => __DIR__ . '/includes/actions/RollbackAction.php',
"hamcrest/hamcrest-php": "^2.0",
"jakub-onderka/php-console-highlighter": "0.3.2",
"jakub-onderka/php-parallel-lint": "0.9.2",
- "jetbrains/phpstorm-stubs": "dev-master#38ff1a581b297f7901e961b8c923862ea80c3b96",
"justinrainbow/json-schema": "~5.2",
"mediawiki/mediawiki-codesniffer": "24.0.0",
"monolog/monolog": "~1.22.1",
"wikimedia/avro": "1.8.0",
"wikimedia/testing-access-wrapper": "~1.0",
"wmde/hamcrest-html-matchers": "^0.1.0",
- "mediawiki/mediawiki-phan-config": "0.3.0"
+ "mediawiki/mediawiki-phan-config": "0.5.0"
},
"replace": {
"symfony/polyfill-ctype": "1.99",
$wikiPage: the WikiPage (object) being saved
$user: the user (object) saving the article
$content: the new article content, as a Content object
-$summary: the article summary (comment)
-$isminor: minor flag
-$iswatch: watch flag
-$section: section #
+&$summary: CommentStoreComment object containing the edit comment. Can be replaced with a new one.
+$isminor: Boolean flag specifying if the edit was marked as minor.
+$iswatch: Previously a watch flag. Currently unused, always null.
+$section: Previously the section number being edited. Currently unused, always null.
+$flags: All EDIT_… flags (including EDIT_MINOR) as an integer number. See WikiPage::doEditContent
+ documentation for flags' definition.
+$status: StatusValue object for the hook handlers resulting status. Either set $status->fatal() or
+ return false to abort the save action.
'PageContentSaveComplete': After an article has been updated.
$wikiPage: WikiPage modified
* @return string[] [ $text, $actor ]
*/
private static function getFieldNames( $key ) {
- if ( isset( self::$specialFields[$key] ) ) {
- return self::$specialFields[$key];
- }
-
- return [ $key . '_text', substr( $key, 0, -5 ) . '_actor' ];
+ return self::$specialFields[$key] ?? [ $key . '_text', substr( $key, 0, -5 ) . '_actor' ];
}
/**
$this->setBlocker( $options['byText'] );
}
- $this->mReason = $options['reason'];
+ $this->setReason( $options['reason'] );
$this->mTimestamp = wfTimestamp( TS_MW, $options['timestamp'] );
- $this->mExpiry = wfGetDB( DB_REPLICA )->decodeExpiry( $options['expiry'] );
+ $this->setExpiry( wfGetDB( DB_REPLICA )->decodeExpiry( $options['expiry'] ) );
# Boolean settings
$this->mAuto = (bool)$options['auto'];
- $this->mHideName = (bool)$options['hideName'];
+ $this->setHideName( (bool)$options['hideName'] );
$this->isHardblock( !$options['anonOnly'] );
$this->isAutoblocking( (bool)$options['enableAutoblock'] );
$this->isSitewide( (bool)$options['sitewide'] );
&& $this->mAuto == $block->mAuto
&& $this->isHardblock() == $block->isHardblock()
&& $this->isCreateAccountBlocked() == $block->isCreateAccountBlocked()
- && $this->mExpiry == $block->mExpiry
+ && $this->getExpiry() == $block->getExpiry()
&& $this->isAutoblocking() == $block->isAutoblocking()
- && $this->mHideName == $block->mHideName
+ && $this->getHideName() == $block->getHideName()
&& $this->isEmailBlocked() == $block->isEmailBlocked()
&& $this->isUsertalkEditAllowed() == $block->isUsertalkEditAllowed()
- && $this->mReason == $block->mReason
+ && $this->getReason() == $block->getReason()
&& $this->isSitewide() == $block->isSitewide()
// Block::getRestrictions() may perform a database query, so keep it at
// the end.
$this->mTimestamp = wfTimestamp( TS_MW, $row->ipb_timestamp );
$this->mAuto = $row->ipb_auto;
- $this->mHideName = $row->ipb_deleted;
+ $this->setHideName( $row->ipb_deleted );
$this->mId = (int)$row->ipb_id;
$this->mParentBlockId = $row->ipb_parent_block_id;
// I wish I didn't have to do this
$db = wfGetDB( DB_REPLICA );
- $this->mExpiry = $db->decodeExpiry( $row->ipb_expiry );
- $this->mReason = CommentStore::getStore()
+ $this->setExpiry( $db->decodeExpiry( $row->ipb_expiry ) );
+ $this->setReason(
+ CommentStore::getStore()
// Legacy because $row may have come from self::selectFields()
- ->getCommentLegacy( $db, 'ipb_reason', $row )->text;
+ ->getCommentLegacy( $db, 'ipb_reason', $row )->text
+ );
$this->isHardblock( !$row->ipb_anon_only );
$this->isAutoblocking( $row->ipb_enable_autoblock );
* @return array
*/
protected function getDatabaseArray( IDatabase $dbw ) {
- $expiry = $dbw->encodeExpiry( $this->mExpiry );
+ $expiry = $dbw->encodeExpiry( $this->getExpiry() );
if ( $this->forcedTargetID ) {
$uid = $this->forcedTargetID;
$a = [
'ipb_address' => (string)$this->target,
'ipb_user' => $uid,
- 'ipb_timestamp' => $dbw->timestamp( $this->mTimestamp ),
+ 'ipb_timestamp' => $dbw->timestamp( $this->getTimestamp() ),
'ipb_auto' => $this->mAuto,
'ipb_anon_only' => !$this->isHardblock(),
'ipb_create_account' => $this->isCreateAccountBlocked(),
'ipb_expiry' => $expiry,
'ipb_range_start' => $this->getRangeStart(),
'ipb_range_end' => $this->getRangeEnd(),
- 'ipb_deleted' => intval( $this->mHideName ), // typecast required for SQLite
+ 'ipb_deleted' => intval( $this->getHideName() ), // typecast required for SQLite
'ipb_block_email' => $this->isEmailBlocked(),
'ipb_allow_usertalk' => $this->isUsertalkEditAllowed(),
'ipb_parent_block_id' => $this->mParentBlockId,
'ipb_sitewide' => $this->isSitewide(),
- ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->mReason )
+ ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->getReason() )
+ ActorMigration::newMigration()->getInsertValues( $dbw, 'ipb_by', $this->getBlocker() );
return $a;
protected function getAutoblockUpdateArray( IDatabase $dbw ) {
return [
'ipb_create_account' => $this->isCreateAccountBlocked(),
- 'ipb_deleted' => (int)$this->mHideName, // typecast required for SQLite
+ 'ipb_deleted' => (int)$this->getHideName(), // typecast required for SQLite
'ipb_allow_usertalk' => $this->isUsertalkEditAllowed(),
'ipb_sitewide' => $this->isSitewide(),
- ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->mReason )
+ ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->getReason() )
+ ActorMigration::newMigration()->getInsertValues( $dbw, 'ipb_by', $this->getBlocker() );
}
# Check if the block is an autoblock and would exceed the user block
# if renewed. If so, do nothing, otherwise prolong the block time...
if ( $ipblock->mAuto && // @todo Why not compare $ipblock->mExpiry?
- $this->mExpiry > self::getAutoblockExpiry( $ipblock->mTimestamp )
+ $this->getExpiry() > self::getAutoblockExpiry( $ipblock->getTimestamp() )
) {
# Reset block timestamp to now and its expiry to
# $wgAutoblockExpiry in the future
wfDebug( "Autoblocking {$this->getTarget()}@" . $autoblockIP . "\n" );
$autoblock->setTarget( $autoblockIP );
$autoblock->setBlocker( $this->getBlocker() );
- $autoblock->mReason = wfMessage( 'autoblocker', $this->getTarget(), $this->mReason )
- ->inContentLanguage()->plain();
+ $autoblock->setReason(
+ wfMessage( 'autoblocker', $this->getTarget(), $this->getReason() )
+ ->inContentLanguage()->plain()
+ );
$timestamp = wfTimestampNow();
$autoblock->mTimestamp = $timestamp;
$autoblock->mAuto = 1;
$autoblock->isCreateAccountBlocked( $this->isCreateAccountBlocked() );
# Continue suppressing the name if needed
- $autoblock->mHideName = $this->mHideName;
+ $autoblock->setHideName( $this->getHideName() );
$autoblock->isUsertalkEditAllowed( $this->isUsertalkEditAllowed() );
$autoblock->mParentBlockId = $this->mId;
$autoblock->isSitewide( $this->isSitewide() );
$autoblock->setRestrictions( $this->getRestrictions() );
- if ( $this->mExpiry == 'infinity' ) {
+ if ( $this->getExpiry() == 'infinity' ) {
# Original block was indefinite, start an autoblock now
- $autoblock->mExpiry = self::getAutoblockExpiry( $timestamp );
+ $autoblock->setExpiry( self::getAutoblockExpiry( $timestamp ) );
} else {
# If the user is already blocked with an expiry date, we don't
# want to pile on top of that.
- $autoblock->mExpiry = min( $this->mExpiry, self::getAutoblockExpiry( $timestamp ) );
+ $autoblock->setExpiry( min( $this->getExpiry(), self::getAutoblockExpiry( $timestamp ) ) );
}
# Insert the block...
$timestamp = wfTimestampNow();
wfDebug( "Block::isExpired() checking current " . $timestamp . " vs $this->mExpiry\n" );
- if ( !$this->mExpiry ) {
+ if ( !$this->getExpiry() ) {
return false;
} else {
- return $timestamp > $this->mExpiry;
+ return $timestamp > $this->getExpiry();
}
}
public function updateTimestamp() {
if ( $this->mAuto ) {
$this->mTimestamp = wfTimestamp();
- $this->mExpiry = self::getAutoblockExpiry( $this->mTimestamp );
+ $this->setExpiry( self::getAutoblockExpiry( $this->getTimestamp() ) );
$dbw = wfGetDB( DB_MASTER );
$dbw->update( 'ipblocks',
[ /* SET */
- 'ipb_timestamp' => $dbw->timestamp( $this->mTimestamp ),
- 'ipb_expiry' => $dbw->timestamp( $this->mExpiry ),
+ 'ipb_timestamp' => $dbw->timestamp( $this->getTimestamp() ),
+ 'ipb_expiry' => $dbw->timestamp( $this->getExpiry() ),
],
[ /* WHERE */
'ipb_id' => $this->getId(),
return $this;
}
+ /**
+ * Get the reason given for creating the block
+ *
+ * @since 1.33
+ * @return string
+ */
+ public function getReason() {
+ return $this->mReason;
+ }
+
+ /**
+ * Set the reason for creating the block
+ *
+ * @since 1.33
+ * @param string $reason
+ */
+ public function setReason( $reason ) {
+ $this->mReason = $reason;
+ }
+
+ /**
+ * Get whether the block hides the target's username
+ *
+ * @since 1.33
+ * @return bool The block hides the username
+ */
+ public function getHideName() {
+ return $this->mHideName;
+ }
+
+ /**
+ * Set whether ths block hides the target's username
+ *
+ * @since 1.33
+ * @param bool $hideName The block hides the username
+ */
+ public function setHideName( $hideName ) {
+ $this->mHideName = $hideName;
+ }
+
/**
* Get the system block type, if any
* @since 1.29
}
/**
- * @since 1.19
+ * Get the block expiry time
*
- * @return mixed|string
+ * @since 1.19
+ * @return string
*/
public function getExpiry() {
return $this->mExpiry;
}
+ /**
+ * Set the block expiry time
+ *
+ * @since 1.33
+ * @param string $expiry
+ */
+ public function setExpiry( $expiry ) {
+ $this->mExpiry = $expiry;
+ }
+
+ /**
+ * Get the timestamp indicating when the block was created
+ *
+ * @since 1.33
+ * @return string
+ */
+ public function getTimestamp() {
+ return $this->mTimestamp;
+ }
+
+ /**
+ * Set the timestamp indicating when the block was created
+ *
+ * @since 1.33
+ * @param string $timestamp
+ */
+ public function setTimestamp( $timestamp ) {
+ $this->mTimestamp = $timestamp;
+ }
+
/**
* Set the target for this block, and update $this->type accordingly
* @param mixed $target
$link = $blocker;
}
- $reason = $this->mReason;
+ $reason = $this->getReason();
if ( $reason == '' ) {
$reason = $context->msg( 'blockednoreason' )->text();
}
$context->getRequest()->getIP(),
$this->getByName(),
$systemBlockType ?? $this->getId(),
- $lang->formatExpiry( $this->mExpiry ),
+ $lang->formatExpiry( $this->getExpiry() ),
(string)$intended,
- $lang->userTimeAndDate( $this->mTimestamp, $context->getUser() ),
+ $lang->userTimeAndDate( $this->getTimestamp(), $context->getUser() ),
];
}
*/
$wgDBerrorLogTZ = false;
-/**
- * Set to true to engage MySQL 4.1/5.0 charset-related features;
- * for now will just cause sending of 'SET NAMES=utf8' on connect.
- *
- * @warning THIS IS EXPERIMENTAL!
- *
- * May break if you're not using the table defs from mysql5/tables.sql.
- * May break if you're upgrading an existing wiki if set differently.
- * Broken symptoms likely to include incorrect behavior with page titles,
- * usernames, comments etc containing non-ASCII characters.
- * Might also cause failures on the object cache and other things.
- *
- * Even correct usage may cause failures with Unicode supplementary
- * characters (those not in the Basic Multilingual Plane) unless MySQL
- * has enhanced their Unicode support.
- *
- * @deprecated since 1.31
- */
-$wgDBmysql5 = false;
-
/**
* Set true to enable Oracle DCRP (supported from 11gR1 onward)
*
private $registryFile;
private $libDir;
private $tmpParentDir;
+ private $cacheDir;
private $infoPrinter;
private $errorPrinter;
private $verbosePrinter;
private $action;
+ private $registry;
/**
* @param string $registryFile Path to YAML file
// Use a temporary directory under the destination directory instead
// of wfTempDir() because PHP's rename() does not work across file
- // systems, as the user's /tmp and $IP may be on different filesystems.
- $this->tmpParentDir = "{$this->libDir}/.tmp";
+ // systems, and the user's /tmp and $IP may be on different filesystems.
+ $this->tmpParentDir = "{$this->libDir}/.foreign/tmp";
+
+ $cacheHome = getenv( 'XDG_CACHE_HOME' ) ? realpath( getenv( 'XDG_CACHE_HOME' ) ) : false;
+ $this->cacheDir = $cacheHome ? "$cacheHome/mw-foreign" : "{$this->libDir}/.foreign/cache";
}
/**
* @throws Exception
*/
public function run( $action, $module ) {
- if ( !in_array( $action, [ 'update', 'verify', 'make-sri' ] ) ) {
- throw new Exception( 'Invalid action parameter.' );
+ $actions = [ 'update', 'verify', 'make-sri' ];
+ if ( !in_array( $action, $actions ) ) {
+ $this->error( "Invalid action.\n\nMust be one of " . implode( ', ', $actions ) . '.' );
+ return false;
}
$this->action = $action;
- $registry = $this->parseBasicYaml( file_get_contents( $this->registryFile ) );
+ $this->registry = $this->parseBasicYaml( file_get_contents( $this->registryFile ) );
if ( $module === 'all' ) {
- $modules = $registry;
- } elseif ( isset( $registry[ $module ] ) ) {
- $modules = [ $module => $registry[ $module ] ];
+ $modules = $this->registry;
+ } elseif ( isset( $this->registry[ $module ] ) ) {
+ $modules = [ $module => $this->registry[ $module ] ];
} else {
- throw new Exception( 'Unknown module name.' );
+ $this->error( "Unknown module name.\n\nMust be one of:\n" .
+ wordwrap( implode( ', ', array_keys( $this->registry ) ), 80 ) .
+ '.'
+ );
+ return false;
}
foreach ( $modules as $moduleName => $info ) {
}
}
- $this->cleanUp();
$this->output( "\nDone!\n" );
+ $this->cleanUp();
if ( $this->hasErrors ) {
// The verify mode should check all modules/files and fail after, not during.
return false;
return true;
}
+ private function cacheKey( $src, $integrity ) {
+ $key = basename( $src ) . '_' . substr( $integrity, -12 );
+ $key = preg_replace( '/[.\/+?=_-]+/', '_', $key );
+ return rtrim( $key, '_' );
+ }
+
+ /** @return string|false */
+ private function cacheGet( $key ) {
+ return Wikimedia\quietCall( 'file_get_contents', "{$this->cacheDir}/$key.data" );
+ }
+
+ private function cacheSet( $key, $data ) {
+ wfMkdirParents( $this->cacheDir );
+ file_put_contents( "{$this->cacheDir}/$key.data", $data, LOCK_EX );
+ }
+
private function fetch( $src, $integrity ) {
+ $key = $this->cacheKey( $src, $integrity );
+ $data = $this->cacheGet( $key );
+ if ( $data ) {
+ return $data;
+ }
+
$req = MWHttpRequest::factory( $src, [ 'method' => 'GET', 'followRedirects' => false ] );
if ( !$req->execute()->isOK() ) {
throw new Exception( "Failed to download resource at {$src}" );
$actualIntegrity = $algo . '-' . base64_encode( hash( $algo, $data, true ) );
if ( $integrity === $actualIntegrity ) {
$this->verbose( "... passed integrity check for {$src}\n" );
+ $this->cacheSet( $key, $data );
} else {
if ( $this->action === 'make-sri' ) {
$this->output( "Integrity for {$src}\n\tintegrity: ${actualIntegrity}\n" );
private function cleanUp() {
wfRecursiveRemoveDir( $this->tmpParentDir );
+
+ // Prune the cache of files we don't recognise.
+ $knownKeys = [];
+ foreach ( $this->registry as $info ) {
+ if ( $info['type'] === 'file' || $info['type'] === 'tar' ) {
+ $knownKeys[] = $this->cacheKey( $info['src'], $info['integrity'] );
+ } elseif ( $info['type'] === 'multi-file' ) {
+ foreach ( $info['files'] as $file ) {
+ $knownKeys[] = $this->cacheKey( $file['src'], $file['integrity'] );
+ }
+ }
+ }
+ foreach ( glob( "{$this->cacheDir}/*" ) as $cacheFile ) {
+ if ( !in_array( basename( $cacheFile, '.data' ), $knownKeys ) ) {
+ unlink( $cacheFile );
+ }
+ }
}
/**
static function splitTrail( $trail ) {
$regex = MediaWikiServices::getInstance()->getContentLanguage()->linkTrail();
$inside = '';
- if ( $trail !== '' ) {
- $m = [];
- if ( preg_match( $regex, $trail, $m ) ) {
- $inside = $m[1];
- $trail = $m[2];
- }
+ if ( $trail !== '' && preg_match( $regex, $trail, $m ) ) {
+ list( , $inside, $trail ) = $m;
}
return [ $inside, $trail ];
}
return $hash[1][$text];
}
$lc = $this->factory->getContentLanguage()->lc( $text );
- if ( isset( $hash[0][$lc] ) ) {
- return $hash[0][$lc];
- }
- return false;
+ return $hash[0][$lc] ?? false;
}
/**
// phpcs:disable Generic.Arrays.DisallowLongArraySyntax,PSR2.Classes.PropertyDeclaration,MediaWiki.Usage.DirUsage
// phpcs:disable Squiz.Scope.MemberVarScope.Missing,Squiz.Scope.MethodScope.Missing
+// @phan-file-suppress PhanPluginDuplicateConditionalNullCoalescing
/**
* Check PHP Version, as well as for composer dependencies in entry points,
* and display something vaguely comprehensible in the event of a totally
+++ /dev/null
-<?php
-/**
- * Holders of revision list for a single page
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-use MediaWiki\MediaWikiServices;
-use Wikimedia\Rdbms\ResultWrapper;
-use Wikimedia\Rdbms\IDatabase;
-
-/**
- * List for revision table items for a single page
- */
-abstract class RevisionListBase extends ContextSource implements Iterator {
- /** @var Title */
- public $title;
-
- /** @var array */
- protected $ids;
-
- /** @var ResultWrapper|bool */
- protected $res;
-
- /** @var bool|Revision */
- protected $current;
-
- /**
- * Construct a revision list for a given title
- * @param IContextSource $context
- * @param Title $title
- */
- function __construct( IContextSource $context, Title $title ) {
- $this->setContext( $context );
- $this->title = $title;
- }
-
- /**
- * Select items only where the ID is any of the specified values
- * @param array $ids
- */
- function filterByIds( array $ids ) {
- $this->ids = $ids;
- }
-
- /**
- * Get the internal type name of this list. Equal to the table name.
- * Override this function.
- * @return null
- */
- public function getType() {
- return null;
- }
-
- /**
- * Initialise the current iteration pointer
- */
- protected function initCurrent() {
- $row = $this->res->current();
- if ( $row ) {
- $this->current = $this->newItem( $row );
- } else {
- $this->current = false;
- }
- }
-
- /**
- * Start iteration. This must be called before current() or next().
- * @return Revision First list item
- */
- public function reset() {
- if ( !$this->res ) {
- $this->res = $this->doQuery( wfGetDB( DB_REPLICA ) );
- } else {
- $this->res->rewind();
- }
- $this->initCurrent();
- return $this->current;
- }
-
- public function rewind() {
- $this->reset();
- }
-
- /**
- * Get the current list item, or false if we are at the end
- * @return Revision
- */
- public function current() {
- return $this->current;
- }
-
- /**
- * Move the iteration pointer to the next list item, and return it.
- * @return Revision
- */
- public function next() {
- $this->res->next();
- $this->initCurrent();
- return $this->current;
- }
-
- public function key() {
- return $this->res ? $this->res->key() : 0;
- }
-
- public function valid() {
- return $this->res ? $this->res->valid() : false;
- }
-
- /**
- * Get the number of items in the list.
- * @return int
- */
- public function length() {
- if ( !$this->res ) {
- return 0;
- } else {
- return $this->res->numRows();
- }
- }
-
- /**
- * Do the DB query to iterate through the objects.
- * @param IDatabase $db DB object to use for the query
- */
- abstract public function doQuery( $db );
-
- /**
- * Create an item object from a DB result row
- * @param object $row
- */
- abstract public function newItem( $row );
-}
-
-/**
- * Abstract base class for revision items
- */
-abstract class RevisionItemBase {
- /** @var RevisionListBase The parent */
- protected $list;
-
- /** The database result row */
- protected $row;
-
- /**
- * @param RevisionListBase $list
- * @param object $row DB result row
- */
- public function __construct( $list, $row ) {
- $this->list = $list;
- $this->row = $row;
- }
-
- /**
- * Get the DB field name associated with the ID list.
- * Override this function.
- * @return null
- */
- public function getIdField() {
- return null;
- }
-
- /**
- * Get the DB field name storing timestamps.
- * Override this function.
- * @return bool
- */
- public function getTimestampField() {
- return false;
- }
-
- /**
- * Get the DB field name storing user ids.
- * Override this function.
- * @return bool
- */
- public function getAuthorIdField() {
- return false;
- }
-
- /**
- * Get the DB field name storing user names.
- * Override this function.
- * @return bool
- */
- public function getAuthorNameField() {
- return false;
- }
-
- /**
- * Get the DB field name storing actor ids.
- * Override this function.
- * @since 1.31
- * @return bool
- */
- public function getAuthorActorField() {
- return false;
- }
-
- /**
- * Get the ID, as it would appear in the ids URL parameter
- * @return int
- */
- public function getId() {
- $field = $this->getIdField();
- return $this->row->$field;
- }
-
- /**
- * Get the date, formatted in user's language
- * @return string
- */
- public function formatDate() {
- return $this->list->getLanguage()->userDate( $this->getTimestamp(),
- $this->list->getUser() );
- }
-
- /**
- * Get the time, formatted in user's language
- * @return string
- */
- public function formatTime() {
- return $this->list->getLanguage()->userTime( $this->getTimestamp(),
- $this->list->getUser() );
- }
-
- /**
- * Get the timestamp in MW 14-char form
- * @return mixed
- */
- public function getTimestamp() {
- $field = $this->getTimestampField();
- return wfTimestamp( TS_MW, $this->row->$field );
- }
-
- /**
- * Get the author user ID
- * @return int
- */
- public function getAuthorId() {
- $field = $this->getAuthorIdField();
- return intval( $this->row->$field );
- }
-
- /**
- * Get the author user name
- * @return string
- */
- public function getAuthorName() {
- $field = $this->getAuthorNameField();
- return strval( $this->row->$field );
- }
-
- /**
- * Get the author actor ID
- * @since 1.31
- * @return string
- */
- public function getAuthorActor() {
- $field = $this->getAuthorActorField();
- return strval( $this->row->$field );
- }
-
- /**
- * Returns true if the current user can view the item
- */
- abstract public function canView();
-
- /**
- * Returns true if the current user can view the item text/file
- */
- abstract public function canViewContent();
-
- /**
- * Get the HTML of the list item. Should be include "<li></li>" tags.
- * This is used to show the list in HTML form, by the special page.
- */
- abstract public function getHTML();
-
- /**
- * Returns an instance of LinkRenderer
- * @return \MediaWiki\Linker\LinkRenderer
- */
- protected function getLinkRenderer() {
- return MediaWikiServices::getInstance()->getLinkRenderer();
- }
-}
-
-class RevisionList extends RevisionListBase {
- public function getType() {
- return 'revision';
- }
-
- /**
- * @param IDatabase $db
- * @return mixed
- */
- public function doQuery( $db ) {
- $conds = [ 'rev_page' => $this->title->getArticleID() ];
- if ( $this->ids !== null ) {
- $conds['rev_id'] = array_map( 'intval', $this->ids );
- }
- $revQuery = Revision::getQueryInfo( [ 'page', 'user' ] );
- return $db->select(
- $revQuery['tables'],
- $revQuery['fields'],
- $conds,
- __METHOD__,
- [ 'ORDER BY' => 'rev_id DESC' ],
- $revQuery['joins']
- );
- }
-
- public function newItem( $row ) {
- return new RevisionItem( $this, $row );
- }
-}
-
-/**
- * Item class for a live revision table row
- */
-class RevisionItem extends RevisionItemBase {
- /** @var Revision */
- protected $revision;
-
- /** @var RequestContext */
- protected $context;
-
- public function __construct( $list, $row ) {
- parent::__construct( $list, $row );
- $this->revision = new Revision( $row );
- $this->context = $list->getContext();
- }
-
- public function getIdField() {
- return 'rev_id';
- }
-
- public function getTimestampField() {
- return 'rev_timestamp';
- }
-
- public function getAuthorIdField() {
- return 'rev_user';
- }
-
- public function getAuthorNameField() {
- return 'rev_user_text';
- }
-
- public function canView() {
- return $this->revision->userCan( Revision::DELETED_RESTRICTED, $this->context->getUser() );
- }
-
- public function canViewContent() {
- return $this->revision->userCan( Revision::DELETED_TEXT, $this->context->getUser() );
- }
-
- public function isDeleted() {
- return $this->revision->isDeleted( Revision::DELETED_TEXT );
- }
-
- /**
- * Get the HTML link to the revision text.
- * @todo Essentially a copy of RevDelRevisionItem::getRevisionLink. That class
- * should inherit from this one, and implement an appropriate interface instead
- * of extending RevDelItem
- * @return string
- */
- protected function getRevisionLink() {
- $date = $this->list->getLanguage()->userTimeAndDate(
- $this->revision->getTimestamp(), $this->list->getUser() );
-
- if ( $this->isDeleted() && !$this->canViewContent() ) {
- return htmlspecialchars( $date );
- }
- $linkRenderer = $this->getLinkRenderer();
- return $linkRenderer->makeKnownLink(
- $this->list->title,
- $date,
- [],
- [
- 'oldid' => $this->revision->getId(),
- 'unhide' => 1
- ]
- );
- }
-
- /**
- * Get the HTML link to the diff.
- * @todo Essentially a copy of RevDelRevisionItem::getDiffLink. That class
- * should inherit from this one, and implement an appropriate interface instead
- * of extending RevDelItem
- * @return string
- */
- protected function getDiffLink() {
- if ( $this->isDeleted() && !$this->canViewContent() ) {
- return $this->context->msg( 'diff' )->escaped();
- } else {
- $linkRenderer = $this->getLinkRenderer();
- return $linkRenderer->makeKnownLink(
- $this->list->title,
- $this->list->msg( 'diff' )->text(),
- [],
- [
- 'diff' => $this->revision->getId(),
- 'oldid' => 'prev',
- 'unhide' => 1
- ]
- );
- }
- }
-
- /**
- * @todo Essentially a copy of RevDelRevisionItem::getHTML. That class
- * should inherit from this one, and implement an appropriate interface instead
- * of extending RevDelItem
- * @return string
- */
- public function getHTML() {
- $difflink = $this->context->msg( 'parentheses' )
- ->rawParams( $this->getDiffLink() )->escaped();
- $revlink = $this->getRevisionLink();
- $userlink = Linker::revUserLink( $this->revision );
- $comment = Linker::revComment( $this->revision );
- if ( $this->isDeleted() ) {
- $revlink = "<span class=\"history-deleted\">$revlink</span>";
- }
- return "<li>$difflink $revlink $userlink $comment</li>";
- }
-}
$block = Block::newFromTarget( $target, null, true );
if ( $block instanceof Block ) {
- $res['expiry'] = ApiResult::formatExpiry( $block->mExpiry, 'infinite' );
+ $res['expiry'] = ApiResult::formatExpiry( $block->getExpiry(), 'infinite' );
$res['id'] = $block->getId();
} else {
# should be unreachable
if ( !is_array( $paramSettings ) ) {
return $paramSettings;
- } elseif ( isset( $paramSettings[self::PARAM_DFLT] ) ) {
- return $paramSettings[self::PARAM_DFLT];
- } else {
- return null;
}
+
+ return $paramSettings[self::PARAM_DFLT] ?? null;
}
/**
$vals['blockid'] = $block->getId();
$vals['blockedby'] = $block->getByName();
$vals['blockedbyid'] = $block->getBy();
- $vals['blockreason'] = $block->mReason;
- $vals['blockedtimestamp'] = wfTimestamp( TS_ISO_8601, $block->mTimestamp );
+ $vals['blockreason'] = $block->getReason();
+ $vals['blockedtimestamp'] = wfTimestamp( TS_ISO_8601, $block->getTimestamp() );
$vals['blockexpiry'] = ApiResult::formatExpiry( $block->getExpiry(), 'infinite' );
$vals['blockpartial'] = !$block->isSitewide();
if ( $block->getSystemBlockType() !== null ) {
throw new InvalidArgumentException( 'Content value must be named' );
}
$this->addContentField( $path, $name, $flags );
- $this->addValue( $path, $name, $value, $flags );
+ return $this->addValue( $path, $name, $value, $flags );
}
/**
if ( $block ) {
$errorParams = [
$block->getTarget(),
- $block->mReason ?: wfMessage( 'blockednoreason' )->text(),
+ $block->getReason() ?: wfMessage( 'blockednoreason' )->text(),
$block->getByName()
];
public function testUserForCreation( $user, $autocreate, array $options = [] ) {
$block = $user->isBlockedFromCreateAccount();
if ( $block ) {
- if ( $block->mReason ) {
- $reason = $block->mReason;
+ if ( $block->getReason() ) {
+ $reason = $block->getReason();
} else {
$msg = \Message::newFromKey( 'blockednoreason' );
if ( !\RequestContext::getMain()->getUser()->isSafeToLoad() ) {
* @return bool
*/
public static function insert( array $restrictions ) {
- if ( empty( $restrictions ) ) {
+ if ( !$restrictions ) {
return false;
}
$rows[] = $restriction->toRow();
}
- if ( empty( $rows ) ) {
+ if ( !$rows ) {
return false;
}
$dbw = wfGetDB( DB_MASTER );
- return $dbw->insert(
+ $dbw->insert(
'ipblocks_restrictions',
$rows,
__METHOD__,
[ 'IGNORE' ]
);
+
+ return true;
}
/**
return $encoded;
}
- $type = $encoded[0];
- $data = $encoded[1];
+ list( $type, $data ) = $encoded;
switch ( $type ) {
case 'a':
}
} elseif ( $_fileType == 'aliases' ) {
if ( isset( $aliases ) ) {
- /** @suppress PhanUndeclaredVariable */
$data['aliases'] = $aliases;
}
} else {
// Return nothing.
$conds[] = '0';
break;
- };
+ }
}
if ( $filterTagIds !== [] ) {
if ( !$this->has( $name, true ) ) {
throw new \ConfigException( 'The configuration option ' . $name . ' does not exist.' );
}
- if ( isset( $this->configItems['public'][$name] ) ) {
- return $this->configItems['public'][$name];
- }
- return $this->configItems['private'][$name];
+
+ return $this->configItems['public'][$name] ?? $this->configItems['private'][$name];
}
/**
return new Blob( $b );
}
- function decodeBlob( $b ) {
- if ( $b instanceof Blob ) {
- $b = $b->fetch();
- }
-
- return $b;
- }
-
function unionQueries( $sqls, $all ) {
$glue = ' UNION ALL ';
return 'BITOR(' . $fieldLeft . ', ' . $fieldRight . ')';
}
- function getServer() {
- return $this->server;
- }
-
public function buildGroupConcatField(
$delim, $table, $field, $conds = '', $join_conds = []
) {
}
$ldTP = $mainConfig->get( 'DBprefix' ); // local domain prefix
- $srvTP = $server['tablePrefix'] ?? null; // server table prefix
+ $srvTP = $server['tablePrefix'] ?? ''; // server table prefix
if ( $srvTP !== '' && $srvTP !== $ldTP ) {
self::reportMismatchedPrefixes( $srvTP, $ldTP );
}
'tablePrefix' => $mainConfig->get( 'DBprefix' ),
'flags' => DBO_DEFAULT,
'sqlMode' => $mainConfig->get( 'SQLMode' ),
- 'utf8Mode' => $mainConfig->get( 'DBmysql5' )
];
$lbConf['servers'][$i] = $server;
'load' => 1,
'flags' => $flags,
'sqlMode' => $mainConfig->get( 'SQLMode' ),
- 'utf8Mode' => $mainConfig->get( 'DBmysql5' )
];
if ( in_array( $server['type'], $typesWithSchema, true ) ) {
$server += [ 'schema' => $mainConfig->get( 'DBmwschema' ) ];
$lbConf['serverTemplate']['schema'] = $mainConfig->get( 'DBmwschema' );
}
$lbConf['serverTemplate']['sqlMode'] = $mainConfig->get( 'SQLMode' );
- $lbConf['serverTemplate']['utf8Mode'] = $mainConfig->get( 'DBmysql5' );
}
}
// need to store these so we don't lose them when they're
// overwritten by the recursion
- $len = $snake[2];
- $startx = $snake[0];
- $starty = $snake[1];
+ list( $startx, $starty, $len ) = $snake;
// the middle snake is part of the LCS, store it
for ( $i = 0; $i < $len; ++$i ) {
--- /dev/null
+<?php
+/**
+ * Sends dump output via the lbzip2 compressor.
+ *
+ * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright © 2019 Wikimedia Foundation Inc.
+ * https://www.mediawiki.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * @ingroup Dump
+ * @since 1.33
+ */
+class DumpLBZip2Output extends DumpPipeOutput {
+ /**
+ * @param string $file
+ */
+ function __construct( $file ) {
+ # use only one core
+ parent::__construct( "lbzip2 -n 1", $file );
+ }
+}
* null on error
*/
public function getExtendedMetadata() {
- if ( isset( $this->mInfo['extmetadata'] ) ) {
- return $this->mInfo['extmetadata'];
- }
-
- return null;
+ return $this->mInfo['extmetadata'] ?? null;
}
/**
$lang = $this->getRenderLang();
# Output each image...
foreach ( $this->mImages as $pair ) {
+ // "text" means "caption" here
/** @var Title $nt */
- $nt = $pair[0];
- $text = $pair[1]; # "text" means "caption" here
- $alt = $pair[2];
- $link = $pair[3];
+ list( $nt, $text, $alt, $link ) = $pair;
$descQuery = false;
if ( $nt->getNamespace() === NS_FILE ) {
$this->updatesSkipped = [];
foreach ( $updates as $funcList ) {
- $func = $funcList[0];
- $args = $funcList[1];
- $origParams = $funcList[2];
+ list( $func, $args, $origParams ) = $funcList;
$func( ...$args );
flush();
$this->updatesSkipped[] = $origParams;
$data = $registry->readFromQueue( $queue );
$wgAutoloadClasses += $data['autoload'];
- /** @suppress PhanUndeclaredVariable $wgHooks is set by DefaultSettings */
+ // @phan-suppress-next-line PhanUndeclaredVariable $wgHooks is set by DefaultSettings
$hooksWeWant = $wgHooks['LoadExtensionSchemaUpdates'] ?? [];
if ( isset( $data['globals']['wgHooks']['LoadExtensionSchemaUpdates'] ) ) {
*/
public function __construct( array $config ) {
$this->name = $config['name'];
- $this->domainId = isset( $config['domainId'] )
- ? $config['domainId'] // e.g. "my_wiki-en_"
- : $config['wikiId']; // b/c alias
+ $this->domainId = $config['domainId'] // e.g. "my_wiki-en_"
+ ?? $config['wikiId']; // b/c alias
if ( !preg_match( '!^[a-zA-Z0-9-_]{1,255}$!', $this->name ) ) {
throw new InvalidArgumentException( "Backend name '{$this->name}' is invalid." );
} elseif ( !is_string( $this->domainId ) ) {
* This will apply such updates post-send for web requests. Note that
* any checks from "syncChecks" are still synchronous.
*
- * Bogus warning
- * @suppress PhanAccessMethodProtected
- *
* @param array $config
* @throws FileBackendError
*/
* @throws InvalidArgumentException
*/
public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
- return $this->mergeViaLock( $key, $callback, $exptime, $attempts, $flags );
+ return $this->mergeViaCas( $key, $callback, $exptime, $attempts, $flags );
}
/**
// Derive the new value from the old value
$value = call_user_func( $callback, $this, $key, $currentValue, $exptime );
+ $hadNoCurrentValue = ( $currentValue === false );
+ unset( $currentValue ); // free RAM in case the value is large
$this->clearLastError();
if ( $value === false ) {
$success = true; // do nothing
- } elseif ( $currentValue === false ) {
+ } elseif ( $hadNoCurrentValue ) {
// Try to create the key, failing if it gets created in the meantime
$success = $this->add( $key, $value, $exptime, $flags );
} else {
return $success;
}
- /**
- * @see BagOStuff::merge()
- *
- * @param string $key
- * @param callable $callback Callback method to be executed
- * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
- * @param int $attempts The amount of times to attempt a merge in case of failure
- * @param int $flags Bitfield of BagOStuff::WRITE_* constants
- * @return bool Success
- */
- protected function mergeViaLock( $key, $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
- if ( $attempts <= 1 ) {
- $timeout = 0; // clearly intended to be "non-blocking"
- } else {
- $timeout = 3;
- }
-
- if ( !$this->lock( $key, $timeout ) ) {
- return false;
- }
-
- $this->clearLastError();
- $reportDupes = $this->reportDupes;
- $this->reportDupes = false;
- $currentValue = $this->get( $key, self::READ_LATEST );
- $this->reportDupes = $reportDupes;
-
- if ( $this->getLastError() ) {
- $this->logger->warning(
- __METHOD__ . ' failed due to I/O error on get() for {key}.',
- [ 'key' => $key ]
- );
-
- $success = false;
- } else {
- // Derive the new value from the old value
- $value = call_user_func( $callback, $this, $key, $currentValue, $exptime );
- if ( $value === false ) {
- $success = true; // do nothing
- } else {
- $success = $this->set( $key, $value, $exptime, $flags ); // set the new value
- }
- }
-
- if ( !$this->unlock( $key ) ) {
- // this should never happen
- trigger_error( "Could not release lock for key '$key'." );
- }
-
- return $success;
- }
-
/**
* Change the expiration on a key if it exists
*
if ( ( $flags & self::READ_LATEST ) == self::READ_LATEST ) {
// If the latest write was a delete(), we do NOT want to fallback
// to the other tiers and possibly see the old value. Also, this
- // is used by mergeViaLock(), which only needs to hit the primary.
+ // is used by merge(), which only needs to hit the primary.
return $this->caches[0]->get( $key, $flags );
}
$this->client->setLogger( $logger );
}
- /**
- * @param string $key
- * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
- * @return mixed Returns false on failure and if the item does not exist
- */
protected function doGet( $key, $flags = 0 ) {
+ $casToken = null;
+
+ return $this->getWithToken( $key, $casToken, $flags );
+ }
+
+ protected function getWithToken( $key, &$casToken, $flags = 0 ) {
+ $casToken = null;
+
$req = [
'method' => 'GET',
'url' => $this->url . rawurlencode( $key ),
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->client->run( $req );
if ( $rcode === 200 ) {
if ( is_string( $rbody ) ) {
- return unserialize( $rbody );
+ $value = unserialize( $rbody );
+ /// @FIXME: use some kind of hash or UUID header as CAS token
+ $casToken = ( $value !== false ) ? $rbody : null;
+
+ return $value;
}
return false;
}
return false;
}
- /**
- * Handle storage error
- * @param string $msg Error message
- * @param int $rcode Error code from client
- * @param string $rerr Error message from client
- * @return false
- */
- protected function handleError( $msg, $rcode, $rerr ) {
- $this->logger->error( "$msg : ({code}) {error}", [
- 'code' => $rcode,
- 'error' => $rerr
- ] );
- $this->setLastError( $rcode === 0 ? self::ERR_UNREACHABLE : self::ERR_UNEXPECTED );
- return false;
- }
-
public function set( $key, $value, $exptime = 0, $flags = 0 ) {
// @TODO: respect WRITE_SYNC (e.g. EACH_QUORUM)
// @TODO: respect $exptime
return false;
}
+
+ public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
+ return $this->mergeViaCas( $key, $callback, $exptime, $attempts, $flags );
+ }
+
+ /**
+ * Handle storage error
+ * @param string $msg Error message
+ * @param int $rcode Error code from client
+ * @param string $rerr Error message from client
+ * @return false
+ */
+ protected function handleError( $msg, $rcode, $rerr ) {
+ $this->logger->error( "$msg : ({code}) {error}", [
+ 'code' => $rcode,
+ 'error' => $rerr
+ ] );
+ $this->setLastError( $rcode === 0 ? self::ERR_UNREACHABLE : self::ERR_UNEXPECTED );
+ return false;
+ }
}
} elseif ( substr_count( $realServer, ':' ) == 1 ) {
// If we have a colon and something that's not a port number
// inside the hostname, assume it's the socket location
- $hostAndSocket = explode( ':', $realServer, 2 );
- $realServer = $hostAndSocket[0];
- $socket = $hostAndSocket[1];
+ list( $realServer, $socket ) = explode( ':', $realServer, 2 );
}
$mysqli = mysqli_init();
}
public function closeAll() {
- $this->forEachLBCallMethod( 'closeAll', [] );
+ $this->forEachLBCallMethod( 'closeAll' );
}
public function setAgentName( $agent ) {
*/
private $readOnlyBySection = [];
- /** @var array Load balancer factory configuration */
- private $conf;
-
/** @var LoadBalancer[] */
private $mainLBs = [];
public function __construct( array $conf ) {
parent::__construct( $conf );
- $this->conf = $conf;
$required = [ 'sectionsByDB', 'sectionLoads', 'serverTemplate' ];
$optional = [ 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName',
'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer',
}
protected function getHandlerClass( $type ) {
- if ( isset( $this->registry[$type] ) ) {
- return $this->registry[$type];
- } else {
- return false;
- }
+ return $this->registry[$type] ?? false;
}
/**
global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType;
$candidates = [ $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType ];
foreach ( $candidates as $candidate ) {
- $cache = false;
if ( $candidate !== CACHE_NONE && $candidate !== CACHE_ANYTHING ) {
$cache = self::getInstance( $candidate );
// CACHE_ACCEL might default to nothing if no APCu
$dirmark = $lang->getDirMarkEntity();
$request = $this->getContext()->getRequest();
- $max = $this->getImageLimitsFromOption( $user, 'imagesize' );
- $maxWidth = $max[0];
- $maxHeight = $max[1];
+ list( $maxWidth, $maxHeight ) = $this->getImageLimitsFromOption( $user, 'imagesize' );
if ( $this->displayImg->exists() ) {
# image
*
* @param User $user
* @param string $optionName Name of a option to check, typically imagesize or thumbsize
- * @return array
+ * @return int[]
* @since 1.21
*/
public function getImageLimitsFromOption( $user, $optionName ) {
* @private
*/
public function replaceTextCallback( $matches ) {
- $type = $matches[1];
- $key = $matches[2];
+ list( , $type, $key ) = $matches;
if ( $type == 'LINK' ) {
list( $ns, $index ) = explode( ':', $key, 2 );
if ( isset( $this->internals[$ns][$index]['text'] ) ) {
$inside = $p[5];
} else {
# tag
- $element = $p[1];
- $attributes = $p[2];
- $close = $p[3];
- $inside = $p[4];
+ list( , $element, $attributes, $close, $inside ) = $p;
}
$marker = self::MARKER_PREFIX . "-$element-" . sprintf( '%08X', $n++ ) . self::MARKER_SUFFIX;
$tail = '';
$text = '';
} else {
- $tail = $q[1];
- $text = $q[2];
+ list( , $tail, $text ) = $q;
}
}
if ( $useLinkPrefixExtension ) {
if ( preg_match( $e2, $s, $m ) ) {
- $prefix = $m[2];
- $s = $m[1];
+ list( , $s, $prefix ) = $m;
} else {
$prefix = '';
}
* that start with "nowait:". However, only 0 timeouts (non-blocking requests)
* can be used with "nowait:" keys.
*
- * By default PoolCounter_Stub is used, which provides no locking. You
+ * By default PoolCounterNull is used, which provides no locking. You
* can get a useful one in the PoolCounter extension.
*/
abstract class PoolCounter {
public static function factory( $type, $key ) {
global $wgPoolCounterConf;
if ( !isset( $wgPoolCounterConf[$type] ) ) {
- return new PoolCounter_Stub;
+ return new PoolCounterNull;
}
$conf = $wgPoolCounterConf[$type];
$class = $conf['class'];
return $type . ':' . ( hexdec( substr( sha1( $key ), 0, 4 ) ) % $slots );
}
}
-
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PoolCounter_Stub extends PoolCounter {
-
- public function __construct() {
- /* No parameters needed */
- }
-
- public function acquireForMe() {
- return Status::newGood( PoolCounter::LOCKED );
- }
-
- public function acquireForAnyone() {
- return Status::newGood( PoolCounter::LOCKED );
- }
-
- public function release() {
- return Status::newGood( PoolCounter::RELEASED );
- }
-}
--- /dev/null
+<?php
+/**
+ * Provides of semaphore semantics for restricting the number
+ * of workers that may be concurrently performing the same task.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * A default PoolCounter, which provides no locking.
+ */
+class PoolCounterNull extends PoolCounter {
+
+ public function __construct() {
+ /* No parameters needed */
+ }
+
+ public function acquireForMe() {
+ return Status::newGood( PoolCounter::LOCKED );
+ }
+
+ public function acquireForAnyone() {
+ return Status::newGood( PoolCounter::LOCKED );
+ }
+
+ public function release() {
+ return Status::newGood( PoolCounter::RELEASED );
+ }
+}
*
* @since 1.22
*/
-class RedisPubSubFeedEngine extends RCFeedEngine {
+class RedisPubSubFeedEngine extends FormattedRCFeed {
/**
* @see FormattedRCFeed::send
if ( $conn !== false ) {
$conn->publish( $channel, $line );
return true;
- } else {
- return false;
}
+
+ return false;
}
}
* @param string $path
* @param array $info
* @param int $version manifest_version for info
- * @return array
*/
public function extractInfo( $path, array $info, $version ) {
$dir = dirname( $path );
* @param string $path Absolute path of JSON file
* @param array $info
* @param int $version manifest_version for info
- * @return array "credits" information to store
*/
public function extractInfo( $path, array $info, $version );
--- /dev/null
+<?php
+/**
+ * Holders of revision list for a single page
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Item class for a live revision table row
+ */
+class RevisionItem extends RevisionItemBase {
+ /** @var Revision */
+ protected $revision;
+
+ /** @var RequestContext */
+ protected $context;
+
+ public function __construct( $list, $row ) {
+ parent::__construct( $list, $row );
+ $this->revision = new Revision( $row );
+ $this->context = $list->getContext();
+ }
+
+ public function getIdField() {
+ return 'rev_id';
+ }
+
+ public function getTimestampField() {
+ return 'rev_timestamp';
+ }
+
+ public function getAuthorIdField() {
+ return 'rev_user';
+ }
+
+ public function getAuthorNameField() {
+ return 'rev_user_text';
+ }
+
+ public function canView() {
+ return $this->revision->userCan( Revision::DELETED_RESTRICTED, $this->context->getUser() );
+ }
+
+ public function canViewContent() {
+ return $this->revision->userCan( Revision::DELETED_TEXT, $this->context->getUser() );
+ }
+
+ public function isDeleted() {
+ return $this->revision->isDeleted( Revision::DELETED_TEXT );
+ }
+
+ /**
+ * Get the HTML link to the revision text.
+ * @todo Essentially a copy of RevDelRevisionItem::getRevisionLink. That class
+ * should inherit from this one, and implement an appropriate interface instead
+ * of extending RevDelItem
+ * @return string
+ */
+ protected function getRevisionLink() {
+ $date = $this->list->getLanguage()->userTimeAndDate(
+ $this->revision->getTimestamp(), $this->list->getUser() );
+
+ if ( $this->isDeleted() && !$this->canViewContent() ) {
+ return htmlspecialchars( $date );
+ }
+ $linkRenderer = $this->getLinkRenderer();
+ return $linkRenderer->makeKnownLink(
+ $this->list->title,
+ $date,
+ [],
+ [
+ 'oldid' => $this->revision->getId(),
+ 'unhide' => 1
+ ]
+ );
+ }
+
+ /**
+ * Get the HTML link to the diff.
+ * @todo Essentially a copy of RevDelRevisionItem::getDiffLink. That class
+ * should inherit from this one, and implement an appropriate interface instead
+ * of extending RevDelItem
+ * @return string
+ */
+ protected function getDiffLink() {
+ if ( $this->isDeleted() && !$this->canViewContent() ) {
+ return $this->context->msg( 'diff' )->escaped();
+ } else {
+ $linkRenderer = $this->getLinkRenderer();
+ return $linkRenderer->makeKnownLink(
+ $this->list->title,
+ $this->list->msg( 'diff' )->text(),
+ [],
+ [
+ 'diff' => $this->revision->getId(),
+ 'oldid' => 'prev',
+ 'unhide' => 1
+ ]
+ );
+ }
+ }
+
+ /**
+ * @todo Essentially a copy of RevDelRevisionItem::getHTML. That class
+ * should inherit from this one, and implement an appropriate interface instead
+ * of extending RevDelItem
+ * @return string
+ */
+ public function getHTML() {
+ $difflink = $this->context->msg( 'parentheses' )
+ ->rawParams( $this->getDiffLink() )->escaped();
+ $revlink = $this->getRevisionLink();
+ $userlink = Linker::revUserLink( $this->revision );
+ $comment = Linker::revComment( $this->revision );
+ if ( $this->isDeleted() ) {
+ $revlink = "<span class=\"history-deleted\">$revlink</span>";
+ }
+ return "<li>$difflink $revlink $userlink $comment</li>";
+ }
+}
--- /dev/null
+<?php
+/**
+ * Holders of revision list for a single page
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * Abstract base class for revision items
+ */
+abstract class RevisionItemBase {
+ /** @var RevisionListBase The parent */
+ protected $list;
+
+ /** The database result row */
+ protected $row;
+
+ /**
+ * @param RevisionListBase $list
+ * @param object $row DB result row
+ */
+ public function __construct( $list, $row ) {
+ $this->list = $list;
+ $this->row = $row;
+ }
+
+ /**
+ * Get the DB field name associated with the ID list.
+ * Override this function.
+ * @return null
+ */
+ public function getIdField() {
+ return null;
+ }
+
+ /**
+ * Get the DB field name storing timestamps.
+ * Override this function.
+ * @return bool
+ */
+ public function getTimestampField() {
+ return false;
+ }
+
+ /**
+ * Get the DB field name storing user ids.
+ * Override this function.
+ * @return bool
+ */
+ public function getAuthorIdField() {
+ return false;
+ }
+
+ /**
+ * Get the DB field name storing user names.
+ * Override this function.
+ * @return bool
+ */
+ public function getAuthorNameField() {
+ return false;
+ }
+
+ /**
+ * Get the DB field name storing actor ids.
+ * Override this function.
+ * @since 1.31
+ * @return bool
+ */
+ public function getAuthorActorField() {
+ return false;
+ }
+
+ /**
+ * Get the ID, as it would appear in the ids URL parameter
+ * @return int
+ */
+ public function getId() {
+ $field = $this->getIdField();
+ return $this->row->$field;
+ }
+
+ /**
+ * Get the date, formatted in user's language
+ * @return string
+ */
+ public function formatDate() {
+ return $this->list->getLanguage()->userDate( $this->getTimestamp(),
+ $this->list->getUser() );
+ }
+
+ /**
+ * Get the time, formatted in user's language
+ * @return string
+ */
+ public function formatTime() {
+ return $this->list->getLanguage()->userTime( $this->getTimestamp(),
+ $this->list->getUser() );
+ }
+
+ /**
+ * Get the timestamp in MW 14-char form
+ * @return mixed
+ */
+ public function getTimestamp() {
+ $field = $this->getTimestampField();
+ return wfTimestamp( TS_MW, $this->row->$field );
+ }
+
+ /**
+ * Get the author user ID
+ * @return int
+ */
+ public function getAuthorId() {
+ $field = $this->getAuthorIdField();
+ return intval( $this->row->$field );
+ }
+
+ /**
+ * Get the author user name
+ * @return string
+ */
+ public function getAuthorName() {
+ $field = $this->getAuthorNameField();
+ return strval( $this->row->$field );
+ }
+
+ /**
+ * Get the author actor ID
+ * @since 1.31
+ * @return string
+ */
+ public function getAuthorActor() {
+ $field = $this->getAuthorActorField();
+ return strval( $this->row->$field );
+ }
+
+ /**
+ * Returns true if the current user can view the item
+ */
+ abstract public function canView();
+
+ /**
+ * Returns true if the current user can view the item text/file
+ */
+ abstract public function canViewContent();
+
+ /**
+ * Get the HTML of the list item. Should be include "<li></li>" tags.
+ * This is used to show the list in HTML form, by the special page.
+ */
+ abstract public function getHTML();
+
+ /**
+ * Returns an instance of LinkRenderer
+ * @return \MediaWiki\Linker\LinkRenderer
+ */
+ protected function getLinkRenderer() {
+ return MediaWikiServices::getInstance()->getLinkRenderer();
+ }
+}
--- /dev/null
+<?php
+/**
+ * Holders of revision list for a single page
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+use Wikimedia\Rdbms\IDatabase;
+
+class RevisionList extends RevisionListBase {
+ public function getType() {
+ return 'revision';
+ }
+
+ /**
+ * @param IDatabase $db
+ * @return mixed
+ */
+ public function doQuery( $db ) {
+ $conds = [ 'rev_page' => $this->title->getArticleID() ];
+ if ( $this->ids !== null ) {
+ $conds['rev_id'] = array_map( 'intval', $this->ids );
+ }
+ $revQuery = Revision::getQueryInfo( [ 'page', 'user' ] );
+ return $db->select(
+ $revQuery['tables'],
+ $revQuery['fields'],
+ $conds,
+ __METHOD__,
+ [ 'ORDER BY' => 'rev_id DESC' ],
+ $revQuery['joins']
+ );
+ }
+
+ public function newItem( $row ) {
+ return new RevisionItem( $this, $row );
+ }
+}
--- /dev/null
+<?php
+/**
+ * Holders of revision list for a single page
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+use Wikimedia\Rdbms\ResultWrapper;
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * List for revision table items for a single page
+ */
+abstract class RevisionListBase extends ContextSource implements Iterator {
+ /** @var Title */
+ public $title;
+
+ /** @var array */
+ protected $ids;
+
+ /** @var ResultWrapper|bool */
+ protected $res;
+
+ /** @var bool|Revision */
+ protected $current;
+
+ /**
+ * Construct a revision list for a given title
+ * @param IContextSource $context
+ * @param Title $title
+ */
+ function __construct( IContextSource $context, Title $title ) {
+ $this->setContext( $context );
+ $this->title = $title;
+ }
+
+ /**
+ * Select items only where the ID is any of the specified values
+ * @param array $ids
+ */
+ function filterByIds( array $ids ) {
+ $this->ids = $ids;
+ }
+
+ /**
+ * Get the internal type name of this list. Equal to the table name.
+ * Override this function.
+ * @return null
+ */
+ public function getType() {
+ return null;
+ }
+
+ /**
+ * Initialise the current iteration pointer
+ */
+ protected function initCurrent() {
+ $row = $this->res->current();
+ if ( $row ) {
+ $this->current = $this->newItem( $row );
+ } else {
+ $this->current = false;
+ }
+ }
+
+ /**
+ * Start iteration. This must be called before current() or next().
+ * @return Revision First list item
+ */
+ public function reset() {
+ if ( !$this->res ) {
+ $this->res = $this->doQuery( wfGetDB( DB_REPLICA ) );
+ } else {
+ $this->res->rewind();
+ }
+ $this->initCurrent();
+ return $this->current;
+ }
+
+ public function rewind() {
+ $this->reset();
+ }
+
+ /**
+ * Get the current list item, or false if we are at the end
+ * @return Revision
+ */
+ public function current() {
+ return $this->current;
+ }
+
+ /**
+ * Move the iteration pointer to the next list item, and return it.
+ * @return Revision
+ */
+ public function next() {
+ $this->res->next();
+ $this->initCurrent();
+ return $this->current;
+ }
+
+ public function key() {
+ return $this->res ? $this->res->key() : 0;
+ }
+
+ public function valid() {
+ return $this->res ? $this->res->valid() : false;
+ }
+
+ /**
+ * Get the number of items in the list.
+ * @return int
+ */
+ public function length() {
+ if ( !$this->res ) {
+ return 0;
+ } else {
+ return $this->res->numRows();
+ }
+ }
+
+ /**
+ * Do the DB query to iterate through the objects.
+ * @param IDatabase $db DB object to use for the query
+ */
+ abstract public function doQuery( $db );
+
+ /**
+ * Create an item object from a DB result row
+ * @param object $row
+ */
+ abstract public function newItem( $row );
+}
// Delete the data for the old session ID now
$this->store->delete( $this->store->makeKey( 'MWSession', $oldId ) );
}
+
+ return $this->id;
}
/**
$block = Block::newFromTarget( $this->target );
- if ( $block instanceof Block && !$block->mAuto # The block exists and isn't an autoblock
- && ( $this->type != Block::TYPE_RANGE # The block isn't a rangeblock
- || $block->getTarget() == $this->target ) # or if it is, the range is what we're about to block
+ // Populate fields if there is a block that is not an autoblock; if it is a range
+ // block, only populate the fields if the range is the same as $this->target
+ if ( $block instanceof Block && $block->getType() !== Block::TYPE_AUTO
+ && ( $this->type != Block::TYPE_RANGE
+ || $block->getTarget() == $this->target )
) {
$fields['HardBlock']['default'] = $block->isHardblock();
$fields['CreateAccount']['default'] = $block->isCreateAccountBlocked();
}
if ( isset( $fields['HideUser'] ) ) {
- $fields['HideUser']['default'] = $block->mHideName;
+ $fields['HideUser']['default'] = $block->getHideName();
}
if ( isset( $fields['DisableUTEdit'] ) ) {
// If the username was hidden (ipb_deleted == 1), don't show the reason
// unless this user also has rights to hideuser: T37839
- if ( !$block->mHideName || $this->getUser()->isAllowed( 'hideuser' ) ) {
- $fields['Reason']['default'] = $block->mReason;
+ if ( !$block->getHideName() || $this->getUser()->isAllowed( 'hideuser' ) ) {
+ $fields['Reason']['default'] = $block->getReason();
} else {
$fields['Reason']['default'] = '';
}
$fields['Confirm']['default'] = 1;
}
- if ( $block->mExpiry == 'infinity' ) {
+ if ( $block->getExpiry() == 'infinity' ) {
$fields['Expiry']['default'] = 'infinite';
} else {
- $fields['Expiry']['default'] = wfTimestamp( TS_RFC2822, $block->mExpiry );
+ $fields['Expiry']['default'] = wfTimestamp( TS_RFC2822, $block->getExpiry() );
}
$fields['BlockId']['default'] = $block->getId();
$block = new Block();
$block->setTarget( $target );
$block->setBlocker( $performer );
- $block->mReason = $data['Reason'][0];
- $block->mExpiry = $expiryTime;
+ $block->setReason( $data['Reason'][0] );
+ $block->setExpiry( $expiryTime );
$block->isCreateAccountBlocked( $data['CreateAccount'] );
$block->isUsertalkEditAllowed( !$wgBlockAllowsUTEdit || !$data['DisableUTEdit'] );
$block->isEmailBlocked( $data['DisableEmail'] );
$block->isHardblock( $data['HardBlock'] );
$block->isAutoblocking( $data['AutoBlock'] );
- $block->mHideName = $data['HideUser'];
+ $block->setHideName( $data['HideUser'] );
if ( $isPartialBlock ) {
$block->isSitewide( false );
}
# If the name was hidden and the blocking user cannot hide
# names, then don't allow any block changes...
- if ( $currentBlock->mHideName && !$performer->isAllowed( 'hideuser' ) ) {
+ if ( $currentBlock->getHideName() && !$performer->isAllowed( 'hideuser' ) ) {
return [ 'cant-see-hidden-user' ];
}
$priorBlock = clone $currentBlock;
$currentBlock->isHardblock( $block->isHardblock() );
$currentBlock->isCreateAccountBlocked( $block->isCreateAccountBlocked() );
- $currentBlock->mExpiry = $block->mExpiry;
+ $currentBlock->setExpiry( $block->getExpiry() );
$currentBlock->isAutoblocking( $block->isAutoblocking() );
- $currentBlock->mHideName = $block->mHideName;
+ $currentBlock->setHideName( $block->getHideName() );
$currentBlock->isEmailBlocked( $block->isEmailBlocked() );
$currentBlock->isUsertalkEditAllowed( $block->isUsertalkEditAllowed() );
- $currentBlock->mReason = $block->mReason;
+ $currentBlock->setReason( $block->getReason() );
if ( $enablePartialBlocks ) {
// Maintain the sitewide status. If partial blocks is not enabled,
$logaction = 'reblock';
# Unset _deleted fields if requested
- if ( $currentBlock->mHideName && !$data['HideUser'] ) {
+ if ( $currentBlock->getHideName() && !$data['HideUser'] ) {
RevisionDeleteUser::unsuppressUserName( $target, $userId );
}
# If hiding/unhiding a name, this should go in the private logs
- if ( (bool)$currentBlock->mHideName ) {
+ if ( (bool)$currentBlock->getHideName() ) {
$data['HideUser'] = true;
}
# If the name was hidden and the blocking user cannot hide
# names, then don't allow any block removals...
- if ( !$performer->isAllowed( 'hideuser' ) && $block->mHideName ) {
+ if ( !$performer->isAllowed( 'hideuser' ) && $block->getHideName() ) {
return [ 'unblock-hideuser' ];
}
Hooks::run( 'UnblockUserComplete', [ $block, $performer ] );
# Unset _deleted fields as needed
- if ( $block->mHideName ) {
+ if ( $block->getHideName() ) {
# Something is deeply FUBAR if this is not a User object, but who knows?
$id = $block->getTarget() instanceof User
? $block->getTarget()->getId()
LinkRenderer $linkRenderer
) {
$this->mConds = $conds;
- $this->type = ( $type ) ? $type : 'edit';
+ $this->type = $type ?: 'edit';
$this->level = $level;
$this->namespace = $namespace;
$this->sizetype = $sizetype;
*/
list( $partname, $ext ) = $this->splitExtensions( $this->mFilteredName );
- if ( count( $ext ) ) {
- $this->mFinalExtension = trim( $ext[count( $ext ) - 1] );
+ if ( $ext !== [] ) {
+ $this->mFinalExtension = trim( end( $ext ) );
} else {
$this->mFinalExtension = '';
if ( $block instanceof Block ) {
# Mangle the reason to alert the user that the block
# originated from matching the X-Forwarded-For header.
- $block->mReason = wfMessage( 'xffblockreason', $block->mReason )->plain();
+ $block->setReason( wfMessage( 'xffblockreason', $block->getReason() )->plain() );
}
}
wfDebug( __METHOD__ . ": Found block.\n" );
$this->mBlock = $block;
$this->mBlockedby = $block->getByName();
- $this->mBlockreason = $block->mReason;
- $this->mHideName = $block->mHideName;
+ $this->mBlockreason = $block->getReason();
+ $this->mHideName = $block->getHideName();
$this->mAllowUsertalk = $block->isUsertalkEditAllowed();
} else {
$this->mBlock = null;
$time = $info['time'];
$counter = $info['offsetCounter'];
} else {
- $time = $info[0];
- $counter = $info[1];
+ list( $time, $counter ) = $info;
}
// Take the 46 LSBs of "milliseconds since epoch"
$id_bin = $this->millisecondsSinceEpochBinary( $time );
$counter = $info['offsetCounter'];
$clkSeq = $info['clkSeq'];
} else {
- $time = $info[0];
- $counter = $info[1];
- $clkSeq = $info[2];
+ list( $time, $counter, $clkSeq ) = $info;
}
// Take the 46 LSBs of "milliseconds since epoch"
$id_bin = $this->millisecondsSinceEpochBinary( $time );
$secondPerson = [ "з" ]; // 1st plural, 2nd formal
$thirdPerson = [ "Ñ‹", "Ñ–" ]; // 3rd
- $lastLetter = $this->lastLetter( $word, $allVowels );
- $wordEnding =& $lastLetter[0];
- $wordLastVowel =& $lastLetter[1];
+ list( $wordEnding, $wordLastVowel ) = $this->lastLetter( $word, $allVowels );
// Now convert the word
switch ( $case ) {
$secondPerson = [ "z" ]; // 1st plural, 2nd formal
$thirdPerson = [ "ı", "i" ]; // 3rd
- $lastLetter = $this->lastLetter( $word, $allVowels );
- $wordEnding =& $lastLetter[0];
- $wordLastVowel =& $lastLetter[1];
+ list( $wordEnding, $wordLastVowel ) = $this->lastLetter( $word, $allVowels );
// Now convert the word
switch ( $case ) {
$secondPerson = [ "ز" ]; // 1st plural, 2nd formal
$thirdPerson = [ "Ù‰", "Ù¸" ]; // 3rd
- $lastLetter = $this->lastLetter( $word, $allVowels );
- $wordEnding = $lastLetter[0];
- $wordLastVowel = $lastLetter[1];
+ list( $wordEnding, $wordLastVowel ) = $this->lastLetter( $word, $allVowels );
// Now convert the word
switch ( $case ) {
/**
* @param string $word
- * @param array $allVowels
+ * @param string[] $allVowels
* @return array
*/
function lastLetter( $word, $allVowels ) {
* @param array $tableParams A child array of self::$tables
*/
protected function cleanupTable( $tableParams ) {
- $table = $tableParams[0];
- $prefix = $tableParams[1];
+ list( $table, $prefix ) = $tableParams;
$idField = $tableParams['idField'] ?? "{$prefix}_id";
$nsField = $tableParams['nsField'] ?? "{$prefix}_namespace";
$titleField = $tableParams['titleField'] ?? "{$prefix}_title";
if ( $newAddress === false ) {
return false;
}
+ $newAddress = trim( $newAddress );
if ( strpos( $newAddress, ':' ) === false ) {
$newAddress = SqlBlobStore::makeAddressFromTextId( intval( $newAddress ) );
}
*/
require_once __DIR__ . '/../Maintenance.php';
+require_once __DIR__ . '/../../includes/export/WikiExporter.php';
use MediaWiki\MediaWikiServices;
use Wikimedia\Rdbms\LoadBalancer;
$this->registerOutput( 'gzip', DumpGZipOutput::class );
$this->registerOutput( 'bzip2', DumpBZip2Output::class );
$this->registerOutput( 'dbzip2', DumpDBZip2Output::class );
+ $this->registerOutput( 'lbzip2', DumpLBZip2Output::class );
$this->registerOutput( '7zip', Dump7ZipOutput::class );
$this->registerFilter( 'latest', DumpLatestFilter::class );
$this->addOption( 'plugin', 'Load a dump plugin class. Specify as <class>[:<file>].',
false, true, false, true );
$this->addOption( 'output', 'Begin a filtered output stream; Specify as <type>:<file>. ' .
- '<type>s: file, gzip, bzip2, 7zip, dbzip2', false, true, false, true );
+ '<type>s: file, gzip, bzip2, 7zip, dbzip2, lbzip2', false, true, false, true );
$this->addOption( 'filter', 'Add a filter on an output branch. Specify as ' .
'<type>[:<options>]. <types>s: latest, notalk, namespace', false, true, false, true );
$this->addOption( 'report', 'Report position and speed after every n pages processed. ' .
$options = $this->orderedOptions;
foreach ( $options as $arg ) {
- $opt = $arg[0];
- $param = $arg[1];
+ list( $opt, $param ) = $arg;
switch ( $opt ) {
case 'plugin':
--- /dev/null
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+
+require_once __DIR__ . '/Maintenance.php';
+
+/**
+ * Manage foreign resources registered with ResourceLoader.
+ *
+ * @ingroup Maintenance
+ * @since 1.32
+ */
+class ManageForeignResources extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->addDescription( <<<TEXT
+Manage foreign resources registered with ResourceLoader.
+
+This helps developers with downloading, verifying, and updating local copies of upstream
+libraries registered as ResourceLoader modules. See resources/lib/foreign-resources.yaml.
+
+Use the "update" action to download urls specified in foreign-resources.yaml, and unpack
+them to the resources directory. This will also verify them against the integrity hashes.
+
+Use the "verify" action to verify the files currently in the resources directory match
+what "update" would replace them with. This is effectively a dry-run and will not change
+any module resources on disk.
+
+Use the "make-sri" action to compute an integrity hash for upstreams that do not publish
+one themselves. Add or update the urls foreign-resources.yaml as needed, but omit (or
+leave empty) the "integrity" key. Then, run the "make-sri" action for the module and
+copy the integrity into the file. Then, you can use "verify" or "update" normally.
+TEXT
+ );
+ $this->addArg( 'action', 'One of "update", "verify" or "make-sri"', true );
+ $this->addArg( 'module', 'Name of a single module (Default: all)', false );
+ $this->addOption( 'verbose', 'Be verbose', false, false, 'v' );
+ }
+
+ /**
+ * @return bool
+ * @throws Exception
+ */
+ public function execute() {
+ global $IP;
+ $frm = new ForeignResourceManager(
+ "{$IP}/resources/lib/foreign-resources.yaml",
+ "{$IP}/resources/lib",
+ function ( $text ) {
+ $this->output( $text );
+ },
+ function ( $text ) {
+ $this->error( $text );
+ },
+ function ( $text ) {
+ if ( $this->hasOption( 'verbose' ) ) {
+ $this->output( $text );
+ }
+ }
+ );
+
+ $action = $this->getArg( 0 );
+ $module = $this->getArg( 1, 'all' );
+ return $frm->run( $action, $module );
+ }
+}
+
+$maintClass = ManageForeignResources::class;
+require_once RUN_MAINTENANCE_IF_MAIN;
} elseif ( substr_count( $realServer, ':' ) == 1 ) {
// If we have a colon and something that's not a port number
// inside the hostname, assume it's the socket location
- $hostAndSocket = explode( ':', $realServer, 2 );
- $realServer = $hostAndSocket[0];
- $socket = $hostAndSocket[1];
+ list( $realServer, $socket ) = explode( ':', $realServer, 2 );
}
if ( $dbName === false ) {
+++ /dev/null
-### Format of this file
-#
-# The top-level keys are directory names (under resources/lib/).
-# They should match module names (as registered in Resources.php), but there are exceptions.
-# Each top-level key holds a resource descriptor that must have one of
-# the following `type` values:
-#
-# - `tar`: For tarball archive (may be gzip-compressed).
-# - `file: For a plain file.
-# - `multi-file`: For multiple plain files.
-#
-### Type tar
-#
-# The `src` and `integrity` keys are required.
-#
-# * `src`: Full URL to the remote resource.
-# * `integrity`: Cryptographic hash (integrity metadata format per <https://www.w3.org/TR/SRI/>).
-# * `dest`: An object mapping paths to files or directory from the remote resource to a destination
-# in the module directory. The value of key in dest may be omitted, which will extract the key
-# directly to the module directory.
-#
-### Type file
-#
-# The `src` and `integrity` keys are required.
-#
-# * `src`: Full URL to the remote resource.
-# * `integrity`: Cryptographic hash (integrity metadata format per <https://www.w3.org/TR/SRI/>).
-# * `dest`: The name of the file in the module directory. Default: Basename of URL.
-#
-### Type multi-file
-#
-# The `files` key is required.
-#
-# * `files`: An object mapping destination paths to an object containing `src` and `integrity`
-# keys.
-
-CLDRPluralRuleParser:
- type: file
- src: https://raw.githubusercontent.com/santhoshtr/CLDRPluralRuleParser/0dda851/src/CLDRPluralRuleParser.js
- integrity: sha384-M4taeYYG2+9Ob1/La16iO+zlRRmBV5lBR3xUKkQT6kfkJ0aLbCi6yc0RYI1BDzdh
-
-easy-deflate:
- type: multi-file
- files:
- deflate.js:
- src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/deflate.js
- integrity: sha384-sHnZLDSWMUhA2w9ygkzCK8YFvoh/fQKY6lXMbvmrYzjuNURiLB0DZFCDNMpGyZ77
- easydeflate.js:
- src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/easydeflate.js
- integrity: sha384-EwPfP2RMkDPa1HkzQsXgzTsy1KEjcIzQPA1HDS/JPHjvEMvVUsCxWwm1oXql/jk2
- inflate.js:
- src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/inflate.js
- integrity: sha384-hMg44Hw424mUYvmzKl0JT4J8UU/1YYhTiGRtR0YX/MXNLK9qWTK0d62FBCDGxmxw
- README.md:
- src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/README.md
- integrity: sha384-6kwcfCLivvqXBZy2ATyya+mTVWLk3eaQyBdC6tbpBtkygnBrM2SNkq3jz/l7IkvP
-
-html5shiv:
- type: file
- src: https://raw.githubusercontent.com/aFarkas/html5shiv/3.7.3/src/html5shiv.js
- integrity: sha384-RPXhaTf22QktT8KTwZ6bUz/C+7CnccaIw5W/y/t0FW5WSDGj3wc3YtRIJC0w47in
-
-jquery:
- type: file
- src: https://code.jquery.com/jquery-3.3.1.js
- # Integrity from link modals https://code.jquery.com/jquery/
- integrity: sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=
- dest: jquery.js
-
-jquery.client:
- type: tar
- src: https://registry.npmjs.org/jquery-client/-/jquery-client-2.0.2.tgz
- integrity: sha256-8c8nBbBykHEMc4I7ksdKJvvw/P7WkaC2X46RTPdz/pw=
- dest:
- package/AUTHORS.txt:
- package/jquery.client.js:
- package/LICENSE-MIT:
- package/README.md:
-
-jquery.cookie:
- type: multi-file
- files:
- jquery.cookie.js:
- src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/jquery.cookie.js
- integrity: sha384-Xxq63E9KDgzUJ6WPNPqVeOtRIwZyx6y9DzEwY2u6LYKSnWrjSoGtWSKmTindYBf2
- MIT-LICENSE.txt:
- src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/MIT-LICENSE.txt
- integrity: sha384-zYsGf3KJ7S0AhOICjcoh0kkn7aGZlzYUXXX5xz8dwR9KjLMM+/JPR2g/jVOGGeId
- CHANGELOG.md:
- src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/CHANGELOG.md
- integrity: sha384-SQOHhLc7PHxHDQpGE/zv9XfXKL0A7OBu8kuyVDnHVp+zSoWyRw4xUJ+LSm5ql4kS
-
-jquery.form:
- type: file
- src: https://raw.githubusercontent.com/jquery-form/form/ff80d9ddf4/jquery.form.js
- integrity: sha384-h4G2CrcSbixzMvrrK259cNBYaL/vS1D4+KdUN9NJDzQnTU1bQ6Avluget+Id13M7
- dest: jquery.form.js
-
-jquery.fullscreen:
- type: file
- src: https://raw.githubusercontent.com/theopolisme/jquery-fullscreen/v2.1.0/jquery.fullscreen.js
- integrity: sha384-G4KPs2d99tgcsyUnJ3eeZ1r2hEKDwZfc4+/xowL/LIemq2VVwEE8HpVAWt4WYNLR
- dest: jquery.fullscreen.js
-
-jquery.hoverIntent:
- type: file
- src: https://raw.githubusercontent.com/briancherne/jquery-hoverIntent/823603fdac/jquery.hoverIntent.js
- integrity: sha384-lca0haN0hqFGGh2aYUhtAgX9dhVHfQnTADH4svDeM6gcXnL7aFGeAi1NYwipDMyS
- dest: jquery.hoverIntent.js
-
-jquery.jStorage:
- type: file
- src: https://raw.githubusercontent.com/andris9/jStorage/v0.4.12/jstorage.js
- integrity: sha384-geMeN8k803kPp6cqRL4VNfuSM1L8DcbKRk0St/KHJzxgpX9S0y9FA6HxA/JgucrJ
- dest: jstorage.js
-
-jquery.throttle-debounce:
- type: file
- src: https://raw.githubusercontent.com/cowboy/jquery-throttle-debounce/v1.1/jquery.ba-throttle-debounce.js
- integrity: sha384-ULOy4DbAghrCqRcrTJLXOY9e4gDpWh0BeEf6xMSL0VtNudXWggcb6AmrVrl4KDAP
- dest: jquery.ba-throttle-debounce.js
-
-moment:
- type: tar
- src: https://codeload.github.com/moment/moment/tar.gz/2.24.0
- integrity: sha384-2/I9rfqkN8AAgh5wOXXphuo827uV7lMmOodrCfIvqC6W6JKKiDGOwd+lE3e8R0yz
- dest:
- moment-2.24.0/moment.js:
- moment-2.24.0/CHANGELOG.md:
- moment-2.24.0/README.md:
- moment-2.24.0/LICENSE:
- moment-2.24.0/locale/*.js: locale
-
-mustache:
- type: multi-file
- files:
- mustache.js:
- src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/mustache.js
- integrity: sha384-k2UYqmzoiq/qgIzZvcYBxbXQW4YdPAsXDOTkHTGb9TCZ9sjCkyT4TlaUN0wQRkql
- LICENSE:
- src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/LICENSE
- integrity: sha384-MYVwXwula9+YkyXexOJVZ0v0DaVvG22uX57mNq5Di+7u8OH9EG9q3yuXkp1Iehiq
-
-oojs:
- type: tar
- src: https://registry.npmjs.org/oojs/-/oojs-2.2.2.tgz
- integrity: sha256-ebgQW2EGrSkBCnDJBGqDpsBDjA3PMN/M8U5DyLHt9mw=
- dest:
- package/dist/oojs.jquery.js:
- package/AUTHORS.txt:
- package/LICENSE-MIT:
- package/README.md:
-
-oojs-router:
- type: tar
- src: https://registry.npmjs.org/oojs-router/-/oojs-router-0.2.0.tgz
- integrity: sha384-VngYqdQ3vTDMXbm4e4FUZCCGos7fB0Jkr9V+kBL5MElprK1h0yQZOzBNnMHtSJS/
- dest:
- package/dist/oojs-router.js:
- package/LICENSE:
- package/AUTHORS.txt:
- package/History.md:
-
-ooui:
- type: tar
- src: https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.31.1.tgz
- integrity: sha384-M9KdU6u02zSKCVczcw6YJmSvFLhdeagNg9CPhizYVqrybL8bamrF5u6YfrFGEyiv
- dest:
- # Main stuff
- package/dist/oojs-ui-core.js{,.map.json}:
- package/dist/oojs-ui-core-{wikimediaui,apex}.css:
- package/dist/oojs-ui-widgets.js{,.map.json}:
- package/dist/oojs-ui-widgets-{wikimediaui,apex}.css:
- package/dist/oojs-ui-toolbars.js{,.map.json}:
- package/dist/oojs-ui-toolbars-{wikimediaui,apex}.css:
- package/dist/oojs-ui-windows.js{,.map.json}:
- package/dist/oojs-ui-windows-{wikimediaui,apex}.css:
- package/dist/oojs-ui-{wikimediaui,apex}.js{,.map.json}:
- package/dist/i18n:
- package/dist/images:
- # WikimediaUI theme
- package/dist/themes/wikimediaui/images/icons/*.{svg,png}: themes/wikimediaui/images/icons
- package/dist/themes/wikimediaui/images/indicators/*.{svg,png}: themes/wikimediaui/images/indicators
- package/dist/themes/wikimediaui/images/textures/*.{gif,svg}: themes/wikimediaui/images/textures
- package/src/themes/wikimediaui/*.json: themes/wikimediaui
- package/dist/wikimedia-ui-base.less:
- # Apex theme (icons, indicators, and textures)
- package/src/themes/apex/*.json: themes/apex
- # Misc stuff
- package/dist/AUTHORS.txt:
- package/dist/History.md:
- package/dist/LICENSE-MIT:
- package/dist/README.md:
-
-qunitjs:
- type: multi-file
- # Integrity from link modals at https://code.jquery.com/qunit/
- files:
- qunit.js:
- src: http://code.jquery.com/qunit/qunit-2.9.1.js
- integrity: sha256-eNccBdxd8zReziWcVjEsPeyJDi3LKMYnzMXyDv8bzsU=
- qunit.css:
- src: https://code.jquery.com/qunit/qunit-2.9.1.css
- integrity: sha256-SSS7o92V7wzcIFg3qnJL9mc4msePaT4klbxtuSGvVVo=
-
-sinonjs:
- type: file
- src: https://sinonjs.org/releases/sinon-1.17.7.js
- integrity: sha384-wR63Jwy75KqwBfzCmXd6gYws6uj3qV/XMAybzXrkEYGYG3AQ58ZWwr1fVpkHa5e8
- dest: sinon.js
+++ /dev/null
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Maintenance
- */
-
-require_once __DIR__ . '/../Maintenance.php';
-
-/**
- * Manage foreign resources registered with ResourceLoader.
- *
- * @ingroup Maintenance
- * @since 1.32
- */
-class ManageForeignResources extends Maintenance {
- public function __construct() {
- parent::__construct();
- $this->addDescription( <<<TEXT
-Manage foreign resources registered with ResourceLoader.
-
-This helps developers to download, verify and update local copies of upstream
-libraries registered as ResourceLoader modules. See also foreign-resources.yaml.
-
-For sources that don't publish an integrity hash, omit "integrity" (or leave empty)
-and run the "make-sri" action to compute the missing hashes.
-
-This script runs in dry-run mode by default. Use --update to actually change,
-remove, or add files to resources/lib/.
-TEXT
- );
- $this->addArg( 'action', 'One of "update", "verify" or "make-sri"', true );
- $this->addArg( 'module', 'Name of a single module (Default: all)', false );
- $this->addOption( 'verbose', 'Be verbose', false, false, 'v' );
- }
-
- /**
- * @return bool
- * @throws Exception
- */
- public function execute() {
- global $IP;
- $frm = new ForeignResourceManager(
- __DIR__ . '/foreign-resources.yaml',
- "{$IP}/resources/lib",
- function ( $text ) {
- $this->output( $text );
- },
- function ( $text ) {
- $this->error( $text );
- },
- function ( $text ) {
- if ( $this->hasOption( 'verbose' ) ) {
- $this->output( $text );
- }
- }
- );
-
- $action = $this->getArg( 0 );
- $module = $this->getArg( 1, 'all' );
- return $frm->run( $action, $module );
- }
-}
-
-$maintClass = ManageForeignResources::class;
-require_once RUN_MAINTENANCE_IF_MAIN;
'group' => 'jquery.ui',
],
'jquery.ui.spinner' => [
+ 'deprecated' => 'Please use "jquery.spinner" instead.',
'scripts' => 'resources/lib/jquery.ui/jquery.ui.spinner.js',
'dependencies' => [
'jquery.ui.core',
--- /dev/null
+# ## Format of this file
+#
+# The top-level keys in this file correspond with directories under resources/lib/.
+# These in turn are registered as module bundles in Resources.php.
+#
+# ## How to install an foreign resource
+#
+# 1. Add or update the url(s) for the upstream module to this YAML file.
+#
+# Look at other modules for examples. To install a module from npm,
+# we use the tarball distribution from npmjs.org. This is the same as what
+# the npm CLI uses. For example, to install jquery-client@9.2.0, use:
+# <https://registry.npmjs.org/jquery-client/-/jquery-client-9.2.0.tgz>.
+#
+# 2. If the upstream maintainers publish an integrity hash, set that as well.
+# Otherwise, use manageForeignResources.php to compute the integrity hash.
+#
+# Run `php manageForeignResources.php make-sri "my module name"`
+#
+# This will download the specified file(s) and print their integrity hashes,
+# already formatted in YAML, ready for copying to this file.
+#
+# 3. Last but not least, decide where files go.
+#
+# If you specified a direct url to JavaScript or CSS file, this step is
+# optional. See the corresponding documentation section below for more
+# information and examples for "dest" keys. Once you've set any "dest" keys,
+# run `php manageForeignResources.php update "my module name"`.
+#
+# ## Package formats
+#
+# Each top-level key must use one of these types:
+#
+# - `file`: For a plain file.
+# - `multi-file`: For multiple plain files.
+# - `tar`: For a tarball archive (may be compressed).
+#
+# ### The "file" type
+#
+# * `src`: Full URL to the remote resource.
+# * `integrity`: Cryptographic hash (integrity metadata format per <https://www.w3.org/TR/SRI/>).
+# * `dest`: [optional] The file name to use in the module directory. Default: Basename of URL.
+#
+# For example, the following would produce resources/lib/mymodule/x.js:
+#
+# mymodule:
+# type: file
+# src: https://mymodule.example/1.2.3/x.js
+# integrity: sha384-Je+NE+saisQuoi
+#
+# ### The "multi-file" type
+#
+# * `files`: An object mapping destination paths to `src` and `integrity` keys.
+#
+# For example:
+#
+# mymodule:
+# type: multi-file
+# files:
+# x.js:
+# src: https://mymodule.example/1.2.3/x.js
+# integrity: sha384-Je+NE+saisQuoi
+# x.css:
+# src: https://mymodule.example/1.2.3/x.css
+# integrity: sha384-Je+NE+saisQuoi
+#
+# ### The "tar" type
+#
+# * `src`: Full URL to the remote resource.
+# * `integrity`: Cryptographic hash (integrity metadata format per <https://www.w3.org/TR/SRI/>).
+# * `dest`: [optional] The default is to extract all files from the package.
+# To only extract some of the files or directories, use "dest" to specify
+# files, directories, and/or glob patterns. You can use a site like https://unpkg.com/
+# to easily inspect an npm package, like <https://unpkg.com/jquery-client@2.0.2/>.
+#
+# For example:
+#
+# mymodule:
+# type: tar
+# src: https://registry.npmjs.org/jquery-client/-/jquery-client-9.2.0.tgz
+# integrity: sha384-Je+NE+saisQuoi
+# dest:
+# package/dist/x.js:
+# package/dist/i18n:
+# package/dist/style/*.css:
+#
+# The would extract the "x.js" file, the "i18n" directory (recursive),
+# and any "*.css" files from the "style" directory.
+#
+
+CLDRPluralRuleParser:
+ type: file
+ src: https://raw.githubusercontent.com/santhoshtr/CLDRPluralRuleParser/0dda851/src/CLDRPluralRuleParser.js
+ integrity: sha384-M4taeYYG2+9Ob1/La16iO+zlRRmBV5lBR3xUKkQT6kfkJ0aLbCi6yc0RYI1BDzdh
+
+easy-deflate:
+ type: multi-file
+ files:
+ deflate.js:
+ src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/deflate.js
+ integrity: sha384-sHnZLDSWMUhA2w9ygkzCK8YFvoh/fQKY6lXMbvmrYzjuNURiLB0DZFCDNMpGyZ77
+ easydeflate.js:
+ src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/easydeflate.js
+ integrity: sha384-EwPfP2RMkDPa1HkzQsXgzTsy1KEjcIzQPA1HDS/JPHjvEMvVUsCxWwm1oXql/jk2
+ inflate.js:
+ src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/inflate.js
+ integrity: sha384-hMg44Hw424mUYvmzKl0JT4J8UU/1YYhTiGRtR0YX/MXNLK9qWTK0d62FBCDGxmxw
+ README.md:
+ src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/README.md
+ integrity: sha384-6kwcfCLivvqXBZy2ATyya+mTVWLk3eaQyBdC6tbpBtkygnBrM2SNkq3jz/l7IkvP
+
+html5shiv:
+ type: file
+ src: https://raw.githubusercontent.com/aFarkas/html5shiv/3.7.3/src/html5shiv.js
+ integrity: sha384-RPXhaTf22QktT8KTwZ6bUz/C+7CnccaIw5W/y/t0FW5WSDGj3wc3YtRIJC0w47in
+
+jquery:
+ type: file
+ src: https://code.jquery.com/jquery-3.3.1.js
+ # Integrity from link modals https://code.jquery.com/jquery/
+ integrity: sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=
+ dest: jquery.js
+
+jquery.client:
+ type: tar
+ src: https://registry.npmjs.org/jquery-client/-/jquery-client-2.0.2.tgz
+ integrity: sha256-8c8nBbBykHEMc4I7ksdKJvvw/P7WkaC2X46RTPdz/pw=
+ dest:
+ package/AUTHORS.txt:
+ package/jquery.client.js:
+ package/LICENSE-MIT:
+ package/README.md:
+
+jquery.cookie:
+ type: multi-file
+ files:
+ jquery.cookie.js:
+ src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/jquery.cookie.js
+ integrity: sha384-Xxq63E9KDgzUJ6WPNPqVeOtRIwZyx6y9DzEwY2u6LYKSnWrjSoGtWSKmTindYBf2
+ MIT-LICENSE.txt:
+ src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/MIT-LICENSE.txt
+ integrity: sha384-zYsGf3KJ7S0AhOICjcoh0kkn7aGZlzYUXXX5xz8dwR9KjLMM+/JPR2g/jVOGGeId
+ CHANGELOG.md:
+ src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/CHANGELOG.md
+ integrity: sha384-SQOHhLc7PHxHDQpGE/zv9XfXKL0A7OBu8kuyVDnHVp+zSoWyRw4xUJ+LSm5ql4kS
+
+jquery.form:
+ type: file
+ src: https://raw.githubusercontent.com/jquery-form/form/ff80d9ddf4/jquery.form.js
+ integrity: sha384-h4G2CrcSbixzMvrrK259cNBYaL/vS1D4+KdUN9NJDzQnTU1bQ6Avluget+Id13M7
+
+jquery.fullscreen:
+ type: file
+ src: https://raw.githubusercontent.com/theopolisme/jquery-fullscreen/v2.1.0/jquery.fullscreen.js
+ integrity: sha384-G4KPs2d99tgcsyUnJ3eeZ1r2hEKDwZfc4+/xowL/LIemq2VVwEE8HpVAWt4WYNLR
+
+jquery.hoverIntent:
+ type: file
+ src: https://raw.githubusercontent.com/briancherne/jquery-hoverIntent/823603fdac/jquery.hoverIntent.js
+ integrity: sha384-lca0haN0hqFGGh2aYUhtAgX9dhVHfQnTADH4svDeM6gcXnL7aFGeAi1NYwipDMyS
+
+jquery.jStorage:
+ type: file
+ src: https://raw.githubusercontent.com/andris9/jStorage/v0.4.12/jstorage.js
+ integrity: sha384-geMeN8k803kPp6cqRL4VNfuSM1L8DcbKRk0St/KHJzxgpX9S0y9FA6HxA/JgucrJ
+
+jquery.throttle-debounce:
+ type: file
+ src: https://raw.githubusercontent.com/cowboy/jquery-throttle-debounce/v1.1/jquery.ba-throttle-debounce.js
+ integrity: sha384-ULOy4DbAghrCqRcrTJLXOY9e4gDpWh0BeEf6xMSL0VtNudXWggcb6AmrVrl4KDAP
+
+moment:
+ type: tar
+ src: https://codeload.github.com/moment/moment/tar.gz/2.24.0
+ integrity: sha384-2/I9rfqkN8AAgh5wOXXphuo827uV7lMmOodrCfIvqC6W6JKKiDGOwd+lE3e8R0yz
+ dest:
+ moment-2.24.0/moment.js:
+ moment-2.24.0/CHANGELOG.md:
+ moment-2.24.0/README.md:
+ moment-2.24.0/LICENSE:
+ moment-2.24.0/locale/*.js: locale
+
+mustache:
+ type: multi-file
+ files:
+ mustache.js:
+ src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/mustache.js
+ integrity: sha384-k2UYqmzoiq/qgIzZvcYBxbXQW4YdPAsXDOTkHTGb9TCZ9sjCkyT4TlaUN0wQRkql
+ LICENSE:
+ src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/LICENSE
+ integrity: sha384-MYVwXwula9+YkyXexOJVZ0v0DaVvG22uX57mNq5Di+7u8OH9EG9q3yuXkp1Iehiq
+
+oojs:
+ type: tar
+ src: https://registry.npmjs.org/oojs/-/oojs-2.2.2.tgz
+ integrity: sha256-ebgQW2EGrSkBCnDJBGqDpsBDjA3PMN/M8U5DyLHt9mw=
+ dest:
+ package/dist/oojs.jquery.js:
+ package/AUTHORS.txt:
+ package/LICENSE-MIT:
+ package/README.md:
+
+oojs-router:
+ type: tar
+ src: https://registry.npmjs.org/oojs-router/-/oojs-router-0.2.0.tgz
+ integrity: sha384-VngYqdQ3vTDMXbm4e4FUZCCGos7fB0Jkr9V+kBL5MElprK1h0yQZOzBNnMHtSJS/
+ dest:
+ package/dist/oojs-router.js:
+ package/LICENSE:
+ package/AUTHORS.txt:
+ package/History.md:
+
+ooui:
+ type: tar
+ src: https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.31.1.tgz
+ integrity: sha384-M9KdU6u02zSKCVczcw6YJmSvFLhdeagNg9CPhizYVqrybL8bamrF5u6YfrFGEyiv
+ dest:
+ # Main stuff
+ package/dist/oojs-ui-core.js{,.map.json}:
+ package/dist/oojs-ui-core-{wikimediaui,apex}.css:
+ package/dist/oojs-ui-widgets.js{,.map.json}:
+ package/dist/oojs-ui-widgets-{wikimediaui,apex}.css:
+ package/dist/oojs-ui-toolbars.js{,.map.json}:
+ package/dist/oojs-ui-toolbars-{wikimediaui,apex}.css:
+ package/dist/oojs-ui-windows.js{,.map.json}:
+ package/dist/oojs-ui-windows-{wikimediaui,apex}.css:
+ package/dist/oojs-ui-{wikimediaui,apex}.js{,.map.json}:
+ package/dist/i18n:
+ package/dist/images:
+ # WikimediaUI theme
+ package/dist/themes/wikimediaui/images/icons/*.{svg,png}: themes/wikimediaui/images/icons
+ package/dist/themes/wikimediaui/images/indicators/*.{svg,png}: themes/wikimediaui/images/indicators
+ package/dist/themes/wikimediaui/images/textures/*.{gif,svg}: themes/wikimediaui/images/textures
+ package/src/themes/wikimediaui/*.json: themes/wikimediaui
+ package/dist/wikimedia-ui-base.less:
+ # Apex theme (icons, indicators, and textures)
+ package/src/themes/apex/*.json: themes/apex
+ # Misc stuff
+ package/dist/AUTHORS.txt:
+ package/dist/History.md:
+ package/dist/LICENSE-MIT:
+ package/dist/README.md:
+
+qunitjs:
+ type: multi-file
+ # Integrity from link modals at https://code.jquery.com/qunit/
+ files:
+ qunit.js:
+ src: http://code.jquery.com/qunit/qunit-2.9.1.js
+ integrity: sha256-eNccBdxd8zReziWcVjEsPeyJDi3LKMYnzMXyDv8bzsU=
+ qunit.css:
+ src: https://code.jquery.com/qunit/qunit-2.9.1.css
+ integrity: sha256-SSS7o92V7wzcIFg3qnJL9mc4msePaT4klbxtuSGvVVo=
+
+sinonjs:
+ type: file
+ src: https://sinonjs.org/releases/sinon-1.17.7.js
+ integrity: sha384-wR63Jwy75KqwBfzCmXd6gYws6uj3qV/XMAybzXrkEYGYG3AQ58ZWwr1fVpkHa5e8
+ dest: sinon.js
-.feedback-spinner {
- display: inline-block;
- zoom: 1;
- *display: inline; /* IE7 and below */ /* stylelint-disable declaration-block-no-duplicate-properties */
- /* @embed */
- background: url( images/spinner.gif );
- width: 18px;
- height: 18px;
-}
-
.mw-feedbackDialog-welcome-message,
.mw-feedbackDialog-feedback-terms {
line-height: 1.4;
padded: true
} );
- this.$spinner = $( '<div>' )
- .addClass( 'feedback-spinner' );
-
// Feedback form
this.feedbackMessageLabel = new OO.ui.LabelWidget( {
classes: [ 'mw-feedbackDialog-welcome-message' ]
*
* @param {OO.ui.TextInputWidget} textInputWidget Text input widget
* @param {number} [limit] Byte limit, defaults to $input's maxlength
+ * @param {Function} [filterFunction] Function to call on the string before assessing the length.
*/
- mw.widgets.visibleByteLimit = function ( textInputWidget, limit ) {
+ mw.widgets.visibleByteLimit = function ( textInputWidget, limit, filterFunction ) {
limit = limit || +textInputWidget.$input.attr( 'maxlength' );
+ if ( !filterFunction || typeof filterFunction !== 'function' ) {
+ filterFunction = undefined;
+ }
function updateCount() {
- var remaining = limit - byteLength( textInputWidget.getValue() );
+ var value = textInputWidget.getValue(),
+ remaining;
+ if ( filterFunction ) {
+ value = filterFunction( value );
+ }
+ remaining = limit - byteLength( value );
if ( remaining > 99 ) {
remaining = '';
} else {
updateCount();
// Actually enforce limit
- textInputWidget.$input.byteLimit( limit );
+ textInputWidget.$input.byteLimit( limit, filterFunction );
};
/**
* Uses jQuery#codePointLimit to enforce the limit.
*
* @param {OO.ui.TextInputWidget} textInputWidget Text input widget
- * @param {number} [limit] Byte limit, defaults to $input's maxlength
+ * @param {number} [limit] Code point limit, defaults to $input's maxlength
+ * @param {Function} [filterFunction] Function to call on the string before assessing the length.
*/
- mw.widgets.visibleCodePointLimit = function ( textInputWidget, limit ) {
+ mw.widgets.visibleCodePointLimit = function ( textInputWidget, limit, filterFunction ) {
limit = limit || +textInputWidget.$input.attr( 'maxlength' );
+ if ( !filterFunction || typeof filterFunction !== 'function' ) {
+ filterFunction = undefined;
+ }
function updateCount() {
- var remaining = limit - codePointLength( textInputWidget.getValue() );
+ var value = textInputWidget.getValue(),
+ remaining;
+ if ( filterFunction ) {
+ value = filterFunction( value );
+ }
+ remaining = limit - codePointLength( value );
if ( remaining > 99 ) {
remaining = '';
} else {
updateCount();
// Actually enforce limit
- textInputWidget.$input.codePointLimit( limit );
+ textInputWidget.$input.codePointLimit( limit, filterFunction );
};
}() );
*/
private static function getOptionValue( $key, $opts, $default ) {
$key = strtolower( $key );
-
- if ( isset( $opts[$key] ) ) {
- return $opts[$key];
- } else {
- return $default;
- }
+ return $opts[$key] ?? $default;
}
/**
+++ /dev/null
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-$cfg = require __DIR__ . '/../../vendor/mediawiki/mediawiki-phan-config/src/config.php';
-
-$cfg['file_list'] = array_merge(
- $cfg['file_list'],
- function_exists( 'register_postsend_function' ) ? [] : [ 'tests/phan/stubs/hhvm.php' ],
- function_exists( 'wikidiff2_do_diff' ) ? [] : [ 'tests/phan/stubs/wikidiff.php' ],
- function_exists( 'tideways_enable' ) ? [] : [ 'tests/phan/stubs/tideways.php' ],
- class_exists( PEAR::class ) ? [] : [ 'tests/phan/stubs/mail.php' ],
- class_exists( Memcached::class ) ? [] : [ 'tests/phan/stubs/memcached.php' ],
- // Per composer.json, PHPUnit 6 is used for PHP 7.0+, PHPUnit 4 otherwise.
- // Load the interface for the version of PHPUnit that isn't installed.
- // Phan only supports PHP 7.0+ (and not HHVM), so we only need to stub PHPUnit 4.
- class_exists( PHPUnit_TextUI_Command::class ) ? [] : [ 'tests/phan/stubs/phpunit4.php' ],
- class_exists( ProfilerExcimer::class ) ? [] : [ 'tests/phan/stubs/excimer.php' ],
- [
- 'maintenance/7zip.inc',
- 'maintenance/cleanupTable.inc',
- 'maintenance/CodeCleanerGlobalsPass.inc',
- 'maintenance/commandLine.inc',
- 'maintenance/sqlite.inc',
- 'maintenance/userDupes.inc',
- 'maintenance/language/checkLanguage.inc',
- 'maintenance/language/languages.inc',
- ]
-);
-
-$cfg['directory_list'] = [
- 'includes/',
- 'languages/',
- 'maintenance/',
- 'mw-config/',
- 'resources/',
- 'vendor/',
-];
-
-$cfg['exclude_analysis_directory_list'] = [
- 'vendor/',
- 'tests/phan/stubs/',
- // The referenced classes are not available in vendor, only when
- // included from composer.
- 'includes/composer/',
- // Directly references classes that only exist in Translate extension
- 'maintenance/language/',
- // External class
- 'includes/libs/jsminplus.php',
-];
-
-$cfg['suppress_issue_types'] = array_merge( $cfg['suppress_issue_types'], [
- // approximate error count: 29
- "PhanCommentParamOnEmptyParamList",
- // approximate error count: 33
- "PhanCommentParamWithoutRealParam",
- // approximate error count: 17
- "PhanNonClassMethodCall",
- // approximate error count: 888
- "PhanParamSignatureMismatch",
- // approximate error count: 7
- "PhanParamSignatureMismatchInternal",
- // approximate error count: 1
- "PhanParamSignatureRealMismatchTooFewParameters",
- // approximate error count: 125
- "PhanParamTooMany",
- // approximate error count: 3
- "PhanParamTooManyInternal",
- // approximate error count: 2
- "PhanTraitParentReference",
- // approximate error count: 3
- "PhanTypeComparisonFromArray",
- // approximate error count: 2
- "PhanTypeComparisonToArray",
- // approximate error count: 218
- "PhanTypeMismatchArgument",
- // approximate error count: 13
- "PhanTypeMismatchArgumentInternal",
- // approximate error count: 5
- "PhanTypeMismatchDimAssignment",
- // approximate error count: 2
- "PhanTypeMismatchDimEmpty",
- // approximate error count: 1
- "PhanTypeMismatchDimFetch",
- // approximate error count: 14
- "PhanTypeMismatchForeach",
- // approximate error count: 56
- "PhanTypeMismatchProperty",
- // approximate error count: 74
- "PhanTypeMismatchReturn",
- // approximate error count: 5
- "PhanTypeNonVarPassByRef",
- // approximate error count: 32
- "PhanUndeclaredConstant",
- // approximate error count: 233
- "PhanUndeclaredMethod",
- // approximate error count: 1224
- "PhanUndeclaredProperty",
- // approximate error count: 58
- "PhanUndeclaredVariableDim",
-] );
-
-$cfg['ignore_undeclared_variables_in_global_scope'] = true;
-$cfg['globals_type_map']['IP'] = 'string';
-
-return $cfg;
+++ /dev/null
-These stubs describe how code that is not available at analysis time should be
-used. No implementations are necessary, just define the classes and their
-methods and use phpdoc to describe what arguments are allowed.
+++ /dev/null
-<?php
-
-// phpcs:ignoreFile
-
-class ExcimerProfiler {
- public function __construct() {
- }
- public function setPeriod( $period ) {
- }
- public function setEventType( $event_type ) {
- }
- public function setMaxDepth( $maxDepth ) {
- }
- public function setFlushCallback( $callback, $max_samples ) {
- }
- public function clearFlushCallback() {
- }
- public function start() {
- }
- public function stop() {
- }
- public function getLog() {
- }
- public function flush() {
- }
-}
-
-class ExcimerLog {
- private final function __construct() {
- }
- function formatCollapsed() {
- }
- function aggregateByFunction() {
- }
- function getEventCount() {
- }
- function current() {
- }
- function key() {
- }
- function next() {
- }
- function rewind() {
- }
- function valid() {
- }
- function count() {
- }
- function offsetExists( $offset ) {
- }
- function offsetGet( $offset ) {
- }
- function offsetSet( $offset, $value ) {
- }
- function offsetUnset( $offset ) {
- }
-
-}
-
-class ExcimerLogEntry {
- private final function __construct() {
- }
- function getTimestamp() {
- }
- function getEventCount() {
- }
- function getTrace() {
- }
-}
-
-class ExcimerTimer {
- function setEventType( $event_type ) {
- }
- function setInterval( $interval ) {
- }
- function setPeriod( $period ) {
- }
- function setCallback( $callback ) {
- }
- function start() {
- }
- function stop() {
- }
- function getTime() {
- }
-}
+++ /dev/null
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- */
-
-// phpcs:ignoreFile
-
-/**
- * @param callable $callback
- * @param mixed ...$parameters
- */
-function register_postsend_function( $callback ) {
-}
+++ /dev/null
-<?php
-
-/**
- * Minimal set of classes necessary for UserMailer to be happy. Types
- * taken from documentation at pear.php.net.
- * phpcs:ignoreFile
- */
-
-class PEAR {
- /**
- * @param mixed $data
- * @return bool
- */
- public static function isError( $data ) {
- }
-}
-
-class PEAR_Error {
- /**
- * @return string
- */
- public function getMessage() {
- }
-}
-
-class Mail {
- /**
- * @param string $driver
- * @param array $params
- * @return self
- */
- static public function factory( $driver, array $params = [] ) {
- }
-
- /**
- * @param mixed $recipients
- * @param array $headers
- * @param string $body
- * @return bool|PEAR_Error
- */
- public function send( $recipients, array $headers, $body ) {
- }
-}
-
-class Mail_smtp extends Mail {
-}
-
-class Mail_mime {
- /**
- * @param mixed $params
- */
- public function __construct( $params = [] ) {
- }
-
- /**
- * @param string $data
- * @param bool $isfile
- * @param bool $append
- * @return bool|PEAR_Error
- */
- public function setTXTBody( $data, $isfile = false, $append = false ) {
- }
-
- /**
- * @param string $data
- * @param bool $isfile
- * @return bool|PEAR_Error
- */
- public function setHTMLBody( $data, $isfile = false ) {
- }
-
- /**
- * @param array|null $parms
- * @param mixed $filename
- * @param bool $skip_head
- * @return string|bool|PEAR_Error
- */
- public function get( $params = null, $filename = null, $skip_head = false ) {
- }
-
- /**
- * @param array|null $xtra_headers
- * @param bool $overwrite
- * @param bool $skip_content
- * @return array
- */
- public function headers( array $xtra_headers = null, $overwrite = false, $skip_content = false ) {
- }
-}
+++ /dev/null
-<?php
-
-/**
- * The phpstorm stubs package includes the Memcached class with two parameters and docs saying
- * that they are optional. Phan can not detect this and thus throws an error for a usage with
- * no params. So we have this small stub just for the constructor to allow no params.
- * @see https://secure.php.net/manual/en/memcached.construct.php
- * phpcs:ignoreFile
- */
-
-class Memcached {
-
- public function __construct() {
- }
-
-}
+++ /dev/null
-<?php
-
-/**
- * Some old classes from PHPUnit 4 that MediaWiki (conditionally) references.
- *
- * phpcs:ignoreFile
- */
-
-class PHPUnit_TextUI_Command {
-
-}
+++ /dev/null
-<?php
-
-/**
- * Minimal set of classes necessary for Xhprof using tideways
- * phpcs:ignoreFile
- */
-
-function tideways_enable(){
-}
-
-function tideways_disable(){
-}
+++ /dev/null
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- */
-
-// phpcs:ignoreFile
-
-/**
- * @param string $text1
- * @param string $text2
- * @param int $numContextLines
- * @param int $movedParagraphDetectionCutoff
- * @return string
- */
-function wikidiff2_do_diff( $text1, $text2, $numContextLines, $movedParagraphDetectionCutoff = 0 ) {
-}
-
-/**
- * @param string $text1
- * @param string $text2
- * @param int $numContextLines
- * @param int $maxMovedLines
- * @return string
- */
-function wikidiff2_inline_diff( $text1, $text2, $numContextLines, $maxMovedLines = 25 ) {
-}
"$type file '$fileName' is inaccessible."
);
- $lines = count( $file );
-
- for ( $i = 0; $i < $lines; $i++ ) {
- $line = $file[$i];
-
+ foreach ( $file as $i => $line ) {
+ $num = $i + 1;
$this->assertLessThanOrEqual(
// FILE_IGNORE_NEW_LINES drops the \n at the EOL, so max length is 80 not 81.
80,
mb_strlen( $line ),
- "$type file '$fileName' line $i is longer than 80 chars:\n\t'$line'"
+ "$type file '$fileName' line $num, is longer than 80 chars:\n\t'$line'"
);
}
}
$madeAt = wfTimestamp( TS_MW );
// delta to stop one-off errors when things happen to go over a second mark.
- $delta = abs( $madeAt - $block->mTimestamp );
+ $delta = abs( $madeAt - $block->getTimestamp() );
$this->assertLessThan(
2,
$delta,
$block = new Block();
$block->setTarget( $target );
$block->setBlocker( $blocker );
- $block->mReason = $insBlock['desc'];
- $block->mExpiry = 'infinity';
+ $block->setReason( $insBlock['desc'] );
+ $block->setExpiry( 'infinity' );
$block->isCreateAccountBlocked( $insBlock['ACDisable'] );
$block->isHardblock( $insBlock['isHardblock'] );
$block->isAutoblocking( $insBlock['isAutoBlocking'] );
$xffblocks = Block::getBlocksForIPList( $list, true );
$this->assertEquals( $exCount, count( $xffblocks ), 'Number of blocks for ' . $xff );
$block = Block::chooseBlock( $xffblocks, $list );
- $this->assertEquals( $exResult, $block->mReason, 'Correct block type for XFF header ' . $xff );
+ $this->assertEquals(
+ $exResult, $block->getReason(), 'Correct block type for XFF header ' . $xff
+ );
}
/**
'auto' => true,
'expiry' => 0
] );
- $this->user->mBlock->mTimestamp = 0;
+ $this->user->mBlock->setTimestamp( 0 );
$this->assertEquals( [ [ 'autoblockedtext',
'[[User:Useruser|Useruser]]', 'no reason given', '127.0.0.1',
'Useruser', null, 'infinite', '127.0.8.1',
$this->assertTrue( !is_null( $block ), 'Block is valid' );
$this->assertSame( $this->mUser->getName(), (string)$block->getTarget() );
- $this->assertSame( 'Some reason', $block->mReason );
+ $this->assertSame( 'Some reason', $block->getReason() );
return $ret;
}
$importRevision = new WikiRevision( new HashConfig() );
$importRevision->setTitle( $r3->getTitle() );
$importRevision->setComment( 'Imported edit' );
- $importRevision->setTimestamp( '19991122334455' );
+ $importRevision->setTimestamp( '19991122001122' );
$importRevision->setText( 'IMPORTED OLD TEST' );
$importRevision->setUsername( 'Alan Smithee' );
/**
* @covers BagOStuff::merge
- * @covers BagOStuff::mergeViaLock
* @covers BagOStuff::mergeViaCas
*/
public function testMerge() {
$key = $this->cache->makeKey( self::TEST_KEY );
- $locks = false;
- $checkLockingCallback = function ( BagOStuff $cache, $key, $oldVal ) use ( &$locks ) {
- $locks = $cache->get( "$key:lock" );
-
- return false;
- };
-
- $this->cache->merge( $key, $checkLockingCallback, 5 );
- $this->assertFalse( $this->cache->get( $key ) );
$calls = 0;
$casRace = false; // emulate a race
$this->assertEquals( 'mergedmerged', $this->cache->get( $key ) );
$calls = 0;
- if ( $locks ) {
- // merge were something else already was merging (e.g. had the lock)
- $this->cache->lock( $key );
- $this->assertFalse(
- $this->cache->merge( $key, $callback, 5, 1 ),
- 'Non-blocking merge (locking)'
- );
- $this->cache->unlock( $key );
- $this->assertEquals( 0, $calls );
- } else {
- $casRace = true;
- $this->assertFalse(
- $this->cache->merge( $key, $callback, 5, 1 ),
- 'Non-blocking merge (CAS)'
- );
- $this->assertEquals( 1, $calls );
- }
+ $casRace = true;
+ $this->assertFalse(
+ $this->cache->merge( $key, $callback, 5, 1 ),
+ 'Non-blocking merge (CAS)'
+ );
+ $this->assertEquals( 1, $calls );
}
/**
* @covers BagOStuff::merge
- * @covers BagOStuff::mergeViaLock
* @dataProvider provideTestMerge_fork
*/
- public function testMerge_fork( $exists, $winsLocking, $resLocking, $resCAS ) {
+ public function testMerge_fork( $exists, $childWins, $resCAS ) {
$key = $this->cache->makeKey( self::TEST_KEY );
$pCallback = function ( BagOStuff $cache, $key, $oldVal ) {
return ( $oldVal === false ) ? 'init-parent' : $oldVal . '-merged-parent';
$fork &= !$this->cache instanceof MultiWriteBagOStuff;
if ( $fork ) {
$pid = null;
- $locked = false;
// Function to start merge(), run another merge() midway through, then finish
- $func = function ( BagOStuff $cache, $key, $cur )
- use ( $pCallback, $cCallback, &$pid, &$locked )
- {
+ $func = function ( $cache, $key, $cur ) use ( $pCallback, $cCallback, &$pid ) {
$pid = pcntl_fork();
if ( $pid == -1 ) {
return false;
} elseif ( $pid ) {
- $locked = $cache->get( "$key:lock" ); // parent has lock?
pcntl_wait( $status );
return $pCallback( $cache, $key, $cur );
return; // can't fork, ignore this test...
}
- if ( $locked ) {
- // merge succeed since child was locked out
- $this->assertEquals( $winsLocking, $merged );
- $this->assertEquals( $this->cache->get( $key ), $resLocking );
- } else {
- // merge has failed because child process was merging (and we only attempted once)
- $this->assertEquals( !$winsLocking, $merged );
- $this->assertEquals( $this->cache->get( $key ), $resCAS );
- }
+ // merge has failed because child process was merging (and we only attempted once)
+ $this->assertEquals( !$childWins, $merged );
+ $this->assertEquals( $this->cache->get( $key ), $resCAS );
} else {
$this->markTestSkipped( 'No pcntl methods available' );
}
function provideTestMerge_fork() {
return [
- // (already exists, parent wins if locking, result if locking, result if CAS)
- [ false, true, 'init-parent', 'init-child' ],
- [ true, true, 'x-merged-parent', 'x-merged-child' ]
+ // (already exists, child wins CAS, result of CAS)
+ [ false, true, 'init-child' ],
+ [ true, true, 'x-merged-child' ]
];
}
parent::__construct();
foreach ( QueryPage::getPages() as $page ) {
- $class = $page[0];
- $name = $page[1];
+ list( $class, $name ) = $page;
if ( !in_array( $class, $this->manualTest ) ) {
$this->queryPages[$class] =
MediaWikiServices::getInstance()->getSpecialPageFactory()->getPage( $name );
$this->assertSame( $block->isCreateAccountBlocked(), $fields['CreateAccount']['default'] );
$this->assertSame( $block->isAutoblocking(), $fields['AutoBlock']['default'] );
$this->assertSame( !$block->isUsertalkEditAllowed(), $fields['DisableUTEdit']['default'] );
- $this->assertSame( $block->mReason, $fields['Reason']['default'] );
+ $this->assertSame( $block->getReason(), $fields['Reason']['default'] );
$this->assertSame( 'infinite', $fields['Expiry']['default'] );
}
$this->assertTrue( $result );
$block = Block::newFromTarget( $badActor );
- $this->assertSame( $reason, $block->mReason );
+ $this->assertSame( $reason, $block->getReason() );
$this->assertSame( $expiry, $block->getExpiry() );
}
$this->assertTrue( $result );
$block = Block::newFromTarget( $badActor );
- $this->assertSame( $reason, $block->mReason );
+ $this->assertSame( $reason, $block->getReason() );
$this->assertSame( $expiry, $block->getExpiry() );
$this->assertSame( '1', $block->isAutoblocking() );
}
$this->assertTrue( $result );
$block = Block::newFromTarget( $badActor );
- $this->assertSame( $reason, $block->mReason );
+ $this->assertSame( $reason, $block->getReason() );
$this->assertSame( $expiry, $block->getExpiry() );
$this->assertCount( 2, $block->getRestrictions() );
$this->assertTrue( BlockRestriction::equals( $block->getRestrictions(), [
$this->assertTrue( $result );
$block = Block::newFromTarget( $badActor );
- $this->assertSame( $reason, $block->mReason );
+ $this->assertSame( $reason, $block->getReason() );
$this->assertSame( $expiry, $block->getExpiry() );
$this->assertFalse( $block->isSitewide() );
$this->assertCount( 2, $block->getRestrictions() );
$this->assertTrue( $result );
$block = Block::newFromTarget( $badActor );
- $this->assertSame( $reason, $block->mReason );
+ $this->assertSame( $reason, $block->getReason() );
$this->assertSame( $expiry, $block->getExpiry() );
$this->assertFalse( $block->isSitewide() );
$this->assertCount( 1, $block->getRestrictions() );
$this->assertTrue( $result );
$block = Block::newFromTarget( $badActor );
- $this->assertSame( $reason, $block->mReason );
+ $this->assertSame( $reason, $block->getReason() );
$this->assertSame( $expiry, $block->getExpiry() );
$this->assertFalse( $block->isSitewide() );
$this->assertCount( 0, $block->getRestrictions() );
$this->assertTrue( $result );
$block = Block::newFromTarget( $badActor );
- $this->assertSame( $reason, $block->mReason );
+ $this->assertSame( $reason, $block->getReason() );
$this->assertSame( $expiry, $block->getExpiry() );
$this->assertTrue( $block->isSitewide() );
$this->assertCount( 0, $block->getRestrictions() );
// 3. Change the block's expiry (to 2 hours), and the cookie's should be changed also.
$newExpiry = wfTimestamp() + 2 * 60 * 60;
- $block->mExpiry = wfTimestamp( TS_MW, $newExpiry );
+ $block->setExpiry( wfTimestamp( TS_MW, $newExpiry ) );
$block->update();
$user2tmp = $this->getTestUser()->getUser();
$request2 = new FauxRequest();
try {
$executor->executeSpecialPage( $page, '', null, null, $user );
+ } catch ( \PHPUnit\Framework\Error\Error $error ) {
+ // Let phpunit settings working:
+ // - convertErrorsToExceptions="true"
+ // - convertNoticesToExceptions="true"
+ // - convertWarningsToExceptions="true"
+ throw $error;
} catch ( Exception $e ) {
- // Exceptions are allowed
+ // Other exceptions are allowed
}
// If the page fataled phpunit will have already died