X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FGlobalFunctions.php;h=ff91ba0a9dbc83ae3d53acfbeecb91c1f11fecab;hb=853c6852ecb28f1a4bbcfa9b7f14a4759050b05b;hp=8241d81ae9d8ba6bea4e4834d5f972be7cdb8b69;hpb=b50e1cf5a7eeb8d55e8d0c02cf3bbc772338e2f9;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 8241d81ae9..ff91ba0a9d 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -81,7 +81,6 @@ if ( !function_exists( 'mb_strpos' ) ) { function mb_strpos( $haystack, $needle, $offset = 0, $encoding = '' ) { return Fallback::mb_strpos( $haystack, $needle, $offset, $encoding ); } - } if ( !function_exists( 'mb_strrpos' ) ) { @@ -283,8 +282,8 @@ function wfRandom() { # The maximum random value is "only" 2^31-1, so get two random # values to reduce the chance of dupes $max = mt_getrandmax() + 1; - $rand = number_format( ( mt_rand() * $max + mt_rand() ) - / $max / $max, 12, '.', '' ); + $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12, '.', '' ); + return $rand; } @@ -330,6 +329,7 @@ function wfRandomString( $length = 32 ) { */ function wfUrlencode( $s ) { static $needle; + if ( is_null( $s ) ) { $needle = null; return ''; @@ -337,7 +337,9 @@ function wfUrlencode( $s ) { if ( is_null( $needle ) ) { $needle = array( '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F' ); - if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) || ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false ) ) { + if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) || + ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false ) + ) { $needle[] = '%3A'; } } @@ -470,15 +472,17 @@ function wfAppendQuery( $url, $query ) { } /** - * Expand a potentially local URL to a fully-qualified URL. Assumes $wgServer + * Expand a potentially local URL to a fully-qualified URL. Assumes $wgServer * is correct. * * The meaning of the PROTO_* constants is as follows: * PROTO_HTTP: Output a URL starting with http:// * PROTO_HTTPS: Output a URL starting with https:// * PROTO_RELATIVE: Output a URL starting with // (protocol-relative URL) - * PROTO_CURRENT: Output a URL starting with either http:// or https:// , depending on which protocol was used for the current incoming request - * PROTO_CANONICAL: For URLs without a domain, like /w/index.php , use $wgCanonicalServer. For protocol-relative URLs, use the protocol of $wgCanonicalServer + * PROTO_CURRENT: Output a URL starting with either http:// or https:// , depending + * on which protocol was used for the current incoming request + * PROTO_CANONICAL: For URLs without a domain, like /w/index.php , use $wgCanonicalServer. + * For protocol-relative URLs, use the protocol of $wgCanonicalServer * PROTO_INTERNAL: Like PROTO_CANONICAL, but uses $wgInternalServer instead of $wgCanonicalServer * * @todo this won't work with current-path-relative URLs @@ -486,10 +490,9 @@ function wfAppendQuery( $url, $query ) { * * @param string $url either fully-qualified or a local path + query * @param $defaultProto Mixed: one of the PROTO_* constants. Determines the - * protocol to use if $url or $wgServer is - * protocol-relative + * protocol to use if $url or $wgServer is protocol-relative * @return string Fully-qualified URL, current-path-relative URL or false if - * no valid URL can be constructed + * no valid URL can be constructed */ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) { global $wgServer, $wgCanonicalServer, $wgInternalServer; @@ -513,8 +516,9 @@ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) { if ( $serverHasProto ) { $defaultProto = $bits['scheme'] . '://'; } else { - // $wgCanonicalServer or $wgInternalServer doesn't have a protocol. This really isn't supposed to happen - // Fall back to HTTP in this ridiculous case + // $wgCanonicalServer or $wgInternalServer doesn't have a protocol. + // This really isn't supposed to happen. Fall back to HTTP in this + // ridiculous case. $defaultProto = PROTO_HTTP; } } @@ -524,7 +528,8 @@ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) { if ( substr( $url, 0, 2 ) == '//' ) { $url = $defaultProtoWithoutSlashes . $url; } elseif ( substr( $url, 0, 1 ) == '/' ) { - // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes, otherwise leave it alone + // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes, + // otherwise leave it alone. $url = ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url; } @@ -739,9 +744,10 @@ function wfUrlProtocolsWithoutProtRel() { /** * parse_url() work-alike, but non-broken. Differences: * - * 1) Does not raise warnings on bad URLs (just returns false) - * 2) Handles protocols that don't use :// (e.g., mailto: and news: , as well as protocol-relative URLs) correctly - * 3) Adds a "delimiter" element to the array, either '://', ':' or '//' (see (2)) + * 1) Does not raise warnings on bad URLs (just returns false). + * 2) Handles protocols that don't use :// (e.g., mailto: and news:, as well as + * protocol-relative URLs) correctly. + * 3) Adds a "delimiter" element to the array, either '://', ':' or '//' (see (2)). * * @param string $url a URL to parse * @return Array: bits of the URL in an associative array, per PHP docs @@ -749,8 +755,9 @@ function wfUrlProtocolsWithoutProtRel() { function wfParseUrl( $url ) { global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php - // Protocol-relative URLs are handled really badly by parse_url(). It's so bad that the easiest - // way to handle them is to just prepend 'http:' and strip the protocol out later + // Protocol-relative URLs are handled really badly by parse_url(). It's so + // bad that the easiest way to handle them is to just prepend 'http:' and + // strip the protocol out later. $wasRelative = substr( $url, 0, 2 ) == '//'; if ( $wasRelative ) { $url = "http:$url"; @@ -816,7 +823,11 @@ function wfParseUrl( $url ) { * @return string */ function wfExpandIRI( $url ) { - return preg_replace_callback( '/((?:%[89A-F][0-9A-F])+)/i', 'wfExpandIRI_callback', wfExpandUrl( $url ) ); + return preg_replace_callback( + '/((?:%[89A-F][0-9A-F])+)/i', + 'wfExpandIRI_callback', + wfExpandUrl( $url ) + ); } /** @@ -931,14 +942,12 @@ function wfDebug( $text, $logonly = false ) { MWDebug::debugMsg( $text ); } - if ( wfRunHooks( 'Debug', array( $text, null /* no log group */ ) ) ) { - if ( $wgDebugLogFile != '' && !$wgProfileOnly ) { - # Strip unprintables; they can switch terminal modes when binary data - # gets dumped, which is pretty annoying. - $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text ); - $text = $wgDebugLogPrefix . $text; - wfErrorLog( $text, $wgDebugLogFile ); - } + if ( $wgDebugLogFile != '' && !$wgProfileOnly ) { + # Strip unprintables; they can switch terminal modes when binary data + # gets dumped, which is pretty annoying. + $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text ); + $text = $wgDebugLogPrefix . $text; + wfErrorLog( $text, $wgDebugLogFile ); } } @@ -1013,9 +1022,7 @@ function wfDebugLog( $logGroup, $text, $public = true ) { $time = wfTimestamp( TS_DB ); $wiki = wfWikiID(); $host = wfHostname(); - if ( wfRunHooks( 'Debug', array( $text, $logGroup ) ) ) { - wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] ); - } + wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] ); } elseif ( $public === true ) { wfDebug( "[$logGroup] $text", false ); } @@ -1057,7 +1064,8 @@ function wfLogDBError( $text ) { * Throws a warning that $function is deprecated * * @param $function String - * @param string|bool $version Version of MediaWiki that the function was deprecated in (Added in 1.19). + * @param string|bool $version Version of MediaWiki that the function + * was deprecated in (Added in 1.19). * @param string|bool $component Added in 1.19. * @param $callerOffset integer: How far up the call stack is the original * caller. 2 = function that called the function that called @@ -2329,7 +2337,15 @@ function wfSuppressWarnings( $end = false ) { } } else { if ( !$suppressCount ) { - $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_DEPRECATED | E_USER_DEPRECATED | E_STRICT ) ); + $originalLevel = error_reporting( E_ALL & ~( + E_WARNING | + E_NOTICE | + E_USER_WARNING | + E_USER_NOTICE | + E_DEPRECATED | + E_USER_DEPRECATED | + E_STRICT + ) ); } ++$suppressCount; } @@ -2454,12 +2470,12 @@ function wfIsWindows() { } /** - * Check if we are running under HipHop + * Check if we are running under HHVM * * @return Bool */ -function wfIsHipHop() { - return defined( 'HPHP_VERSION' ); +function wfIsHHVM() { + return defined( 'HHVM_VERSION' ); } /** @@ -2660,12 +2676,14 @@ function wfEscapeShellArg() { if ( wfIsWindows() ) { // Escaping for an MSVC-style command line parser and CMD.EXE + // @codingStandardsIgnoreStart For long URLs // Refs: // * http://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html // * http://technet.microsoft.com/en-us/library/cc723564.aspx // * Bug #13518 // * CR r63214 // Double the backslashes before any double quotes. Escape the double quotes. + // @codingStandardsIgnoreEnd $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE ); $arg = ''; $iteration = 0; @@ -2714,9 +2732,9 @@ function wfShellExecDisabled() { $functions = explode( ',', ini_get( 'disable_functions' ) ); $functions = array_map( 'trim', $functions ); $functions = array_map( 'strtolower', $functions ); - if ( in_array( 'passthru', $functions ) ) { - wfDebug( "passthru is in disabled_functions\n" ); - $disabled = 'passthru'; + if ( in_array( 'proc_open', $functions ) ) { + wfDebug( "proc_open is in disabled_functions\n" ); + $disabled = 'disabled'; } } } @@ -2728,16 +2746,21 @@ function wfShellExecDisabled() { * configuration if supported. * @param string $cmd Command line, properly escaped for shell. * @param &$retval null|Mixed optional, will receive the program's exit code. - * (non-zero is usually failure) + * (non-zero is usually failure). If there is an error from + * read, select, or proc_open(), this will be set to -1. * @param array $environ optional environment variables which should be * added to the executed command environment. * @param array $limits optional array with limits(filesize, memory, time, walltime) * this overwrites the global wgShellMax* limits. - * @param array $options Array of options. Only one is "duplicateStderr" => true, which - * Which duplicates stderr to stdout, including errors from limit.sh + * @param array $options Array of options: + * - duplicateStderr: Set this to true to duplicate stderr to stdout, + * including errors from limit.sh + * * @return string collected stdout as a string */ -function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array(), $options = array() ) { +function wfShellExec( $cmd, &$retval = null, $environ = array(), + $limits = array(), $options = array() +) { global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime, $wgMaxShellWallClockTime, $wgShellCgroup; @@ -2746,7 +2769,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array $retval = 1; return $disabled == 'safemode' ? 'Unable to run external programs in safe mode.' : - 'Unable to run external programs, passthru() is disabled.'; + 'Unable to run external programs, proc_open() is disabled.'; } $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr']; @@ -2772,11 +2795,8 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array } $cmd = $envcmd . $cmd; + $useLogPipe = false; if ( php_uname( 's' ) == 'Linux' ) { - $stderrDuplication = ''; - if ( $includeStderr ) { - $stderrDuplication = 'exec 2>&1; '; - } $time = intval ( isset( $limits['time'] ) ? $limits['time'] : $wgMaxShellTime ); if ( isset( $limits['walltime'] ) ) { $wallTime = intval( $limits['walltime'] ); @@ -2792,14 +2812,16 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array $cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' . escapeshellarg( $cmd ) . ' ' . escapeshellarg( - $stderrDuplication . + "MW_INCLUDE_STDERR=" . ( $includeStderr ? '1' : '' ) . ';' . "MW_CPU_LIMIT=$time; " . 'MW_CGROUP=' . escapeshellarg( $wgShellCgroup ) . '; ' . "MW_MEM_LIMIT=$mem; " . "MW_FILE_SIZE_LIMIT=$filesize; " . - "MW_WALL_CLOCK_LIMIT=$wallTime" + "MW_WALL_CLOCK_LIMIT=$wallTime; " . + "MW_USE_LOG_PIPE=yes" ); - } else { + $useLogPipe = true; + } elseif ( $includeStderr ) { $cmd .= ' 2>&1'; } } elseif ( $includeStderr ) { @@ -2807,19 +2829,135 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array } wfDebug( "wfShellExec: $cmd\n" ); - // Default to an unusual value that shouldn't happen naturally, - // so in the unlikely event of a weird php bug, it would be - // more obvious what happened. - $retval = 200; - ob_start(); - passthru( $cmd, $retval ); - $output = ob_get_contents(); - ob_end_clean(); + $desc = array( + 0 => array( 'file', 'php://stdin', 'r' ), + 1 => array( 'pipe', 'w' ), + 2 => array( 'file', 'php://stderr', 'w' ) ); + if ( $useLogPipe ) { + $desc[3] = array( 'pipe', 'w' ); + } + $pipes = null; + $proc = proc_open( $cmd, $desc, $pipes ); + if ( !$proc ) { + wfDebugLog( 'exec', "proc_open() failed: $cmd\n" ); + $retval = -1; + return ''; + } + $outBuffer = $logBuffer = ''; + $emptyArray = array(); + $status = false; + $logMsg = false; + + // According to the documentation, it is possible for stream_select() + // to fail due to EINTR. I haven't managed to induce this in testing + // despite sending various signals. If it did happen, the error + // message would take the form: + // + // stream_select(): unable to select [4]: Interrupted system call (max_fd=5) + // + // where [4] is the value of the macro EINTR and "Interrupted system + // call" is string which according to the Linux manual is "possibly" + // localised according to LC_MESSAGES. + $eintr = defined( 'SOCKET_EINTR' ) ? SOCKET_EINTR : 4; + $eintrMessage = "stream_select(): unable to select [$eintr]"; + + // Build a table mapping resource IDs to pipe FDs to work around a + // PHP 5.3 issue in which stream_select() does not preserve array keys + // . + $fds = array(); + foreach ( $pipes as $fd => $pipe ) { + $fds[(int)$pipe] = $fd; + } + + while ( true ) { + $status = proc_get_status( $proc ); + if ( !$status['running'] ) { + break; + } + $status = false; + + $readyPipes = $pipes; + + // Clear last error + @trigger_error( '' ); + if ( @stream_select( $readyPipes, $emptyArray, $emptyArray, null ) === false ) { + $error = error_get_last(); + if ( strncmp( $error['message'], $eintrMessage, strlen( $eintrMessage ) ) == 0 ) { + continue; + } else { + trigger_error( $error['message'], E_USER_WARNING ); + $logMsg = $error['message']; + break; + } + } + foreach ( $readyPipes as $pipe ) { + $block = fread( $pipe, 65536 ); + $fd = $fds[(int)$pipe]; + if ( $block === '' ) { + // End of file + fclose( $pipes[$fd] ); + unset( $pipes[$fd] ); + if ( !$pipes ) { + break 2; + } + } elseif ( $block === false ) { + // Read error + $logMsg = "Error reading from pipe"; + break 2; + } elseif ( $fd == 1 ) { + // From stdout + $outBuffer .= $block; + } elseif ( $fd == 3 ) { + // From log FD + $logBuffer .= $block; + if ( strpos( $block, "\n" ) !== false ) { + $lines = explode( "\n", $logBuffer ); + $logBuffer = array_pop( $lines ); + foreach ( $lines as $line ) { + wfDebugLog( 'exec', $line ); + } + } + } + } + } + + foreach ( $pipes as $pipe ) { + fclose( $pipe ); + } - if ( $retval == 127 ) { - wfDebugLog( 'exec', "Possibly missing executable file: $cmd\n" ); + // Use the status previously collected if possible, since proc_get_status() + // just calls waitpid() which will not return anything useful the second time. + if ( $status === false ) { + $status = proc_get_status( $proc ); } - return $output; + + if ( $logMsg !== false ) { + // Read/select error + $retval = -1; + proc_close( $proc ); + } elseif ( $status['signaled'] ) { + $logMsg = "Exited with signal {$status['termsig']}"; + $retval = 128 + $status['termsig']; + proc_close( $proc ); + } else { + if ( $status['running'] ) { + $retval = proc_close( $proc ); + } else { + $retval = $status['exitcode']; + proc_close( $proc ); + } + if ( $retval == 127 ) { + $logMsg = "Possibly missing executable file"; + } elseif ( $retval >= 129 && $retval <= 192 ) { + $logMsg = "Probably exited with signal " . ( $retval - 128 ); + } + } + + if ( $logMsg !== false ) { + wfDebugLog( 'exec', "$logMsg: $cmd\n" ); + } + + return $outBuffer; } /** @@ -3073,6 +3211,14 @@ function wfUsePHP( $req_ver ) { * This is useful for extensions which due to their nature are not kept in sync * with releases * + * Note: Due to the behavior of PHP's version_compare() which is used in this + * fuction, if you want to allow the 'wmf' development versions add a 'c' (or + * any single letter other than 'a', 'b' or 'p') as a post-fix to your + * targeted version number. For example if you wanted to allow any variation + * of 1.22 use `wfUseMW( '1.22c' )`. Using an 'a' or 'b' instead of 'c' will + * not result in the same comparison due to the internal logic of + * version_compare(). + * * @see perldoc -f use * * @param $req_ver Mixed: the version to check, can be a string, an integer, or @@ -3100,9 +3246,12 @@ function wfUseMW( $req_ver ) { * @return String */ function wfBaseName( $path, $suffix = '' ) { - $encSuffix = ( $suffix == '' ) - ? '' - : ( '(?:' . preg_quote( $suffix, '#' ) . ')?' ); + if ( $suffix == '' ) { + $encSuffix = ''; + } else { + $encSuffix = '(?:' . preg_quote( $suffix, '#' ) . ')?'; + } + $matches = array(); if ( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) { return $matches[1]; @@ -3183,7 +3332,9 @@ function wfDoUpdates( $commit = '' ) { * @param string $engine Either "gmp", "bcmath", or "php" * @return string|bool The output number as a string, or false on error */ -function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = true, $engine = 'auto' ) { +function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, + $lowercase = true, $engine = 'auto' +) { $input = (string)$input; if ( $sourceBase < 2 || @@ -3193,7 +3344,10 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = t $sourceBase != (int)$sourceBase || $destBase != (int)$destBase || $pad != (int)$pad || - !preg_match( "/^[" . substr( '0123456789abcdefghijklmnopqrstuvwxyz', 0, $sourceBase ) . "]+$/i", $input ) + !preg_match( + "/^[" . substr( '0123456789abcdefghijklmnopqrstuvwxyz', 0, $sourceBase ) . "]+$/i", + $input + ) ) { return false; } @@ -3327,9 +3481,11 @@ function wfFixSessionID() { // We treat it as disabled if it doesn't have an entropy length of at least 32 $entropyEnabled = wfCheckEntropy(); - // If built-in entropy is not enabled or not sufficient override php's built in session id generation code + // If built-in entropy is not enabled or not sufficient override PHP's + // built in session id generation code if ( !$entropyEnabled ) { - wfDebug( __METHOD__ . ": PHP's built in entropy is disabled or not sufficient, overriding session id generation using our cryptrand source.\n" ); + wfDebug( __METHOD__ . ": PHP's built in entropy is disabled or not sufficient, " . + "overriding session id generation using our cryptrand source.\n" ); session_id( MWCryptRand::generateHex( 32 ) ); } } @@ -3635,9 +3791,7 @@ function wfBoolToStr( $value ) { * @return string */ function wfGetNull() { - return wfIsWindows() - ? 'NUL' - : '/dev/null'; + return wfIsWindows() ? 'NUL' : '/dev/null'; } /** @@ -3647,14 +3801,17 @@ function wfGetNull() { * in maintenance scripts, to avoid causing too much lag. Of course, this is * a no-op if there are no slaves. * - * @param $maxLag Integer (deprecated) - * @param $wiki mixed Wiki identifier accepted by wfGetLB - * @param $cluster string cluster name accepted by LBFactory + * @param int|bool $maxLag (deprecated) + * @param mixed $wiki Wiki identifier accepted by wfGetLB + * @param string|bool $cluster Cluster name accepted by LBFactory. Default: false. */ function wfWaitForSlaves( $maxLag = false, $wiki = false, $cluster = false ) { - $lb = ( $cluster !== false ) - ? wfGetLBFactory()->getExternalLB( $cluster ) - : wfGetLB( $wiki ); + if( $cluster !== false ) { + $lb = wfGetLBFactory()->getExternalLB( $cluster ); + } else { + $lb = wfGetLB( $wiki ); + } + // bug 27975 - Don't try to wait for slaves if there are none // Prevents permission error when getting master position if ( $lb->getServerCount() > 1 ) { @@ -3709,8 +3866,10 @@ function wfCountDown( $n ) { * characters before hashing. * @return string * @codeCoverageIgnore - * @deprecated since 1.20; Please use MWCryptRand for security purposes and wfRandomString for pseudo-random strings - * @warning This method is NOT secure. Additionally it has many callers that use it for pseudo-random purposes. + * @deprecated since 1.20; Please use MWCryptRand for security purposes and + * wfRandomString for pseudo-random strings + * @warning This method is NOT secure. Additionally it has many callers that + * use it for pseudo-random purposes. */ function wfGenerateToken( $salt = '' ) { wfDeprecated( __METHOD__, '1.20' );