Merge "Port BCP47 formatter from PHP to JavaScript"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 12 Sep 2017 11:43:49 +0000 (11:43 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 12 Sep 2017 11:43:49 +0000 (11:43 +0000)
1  2 
includes/GlobalFunctions.php

@@@ -26,10 -26,8 +26,10 @@@ if ( !defined( 'MEDIAWIKI' ) ) 
  
  use Liuggio\StatsdClient\Sender\SocketSender;
  use MediaWiki\Logger\LoggerFactory;
 +use MediaWiki\ProcOpenError;
  use MediaWiki\Session\SessionManager;
  use MediaWiki\MediaWikiServices;
 +use MediaWiki\Shell\Shell;
  use Wikimedia\ScopedCallback;
  use Wikimedia\Rdbms\DBReplicationWaitError;
  
@@@ -2239,12 -2237,64 +2239,12 @@@ function wfIniGetBool( $setting ) 
   * @param string $args,... strings to escape and glue together,
   *  or a single array of strings parameter
   * @return string
 + * @deprecated since 1.30 use MediaWiki\Shell::escape()
   */
  function wfEscapeShellArg( /*...*/ ) {
        $args = func_get_args();
 -      if ( count( $args ) === 1 && is_array( reset( $args ) ) ) {
 -              // If only one argument has been passed, and that argument is an array,
 -              // treat it as a list of arguments
 -              $args = reset( $args );
 -      }
 -
 -      $first = true;
 -      $retVal = '';
 -      foreach ( $args as $arg ) {
 -              if ( !$first ) {
 -                      $retVal .= ' ';
 -              } else {
 -                      $first = false;
 -              }
 -
 -              if ( wfIsWindows() ) {
 -                      // Escaping for an MSVC-style command line parser and CMD.EXE
 -                      // @codingStandardsIgnoreStart For long URLs
 -                      // Refs:
 -                      //  * https://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html
 -                      //  * https://technet.microsoft.com/en-us/library/cc723564.aspx
 -                      //  * T15518
 -                      //  * 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;
 -                      foreach ( $tokens as $token ) {
 -                              if ( $iteration % 2 == 1 ) {
 -                                      // Delimiter, a double quote preceded by zero or more slashes
 -                                      $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"';
 -                              } elseif ( $iteration % 4 == 2 ) {
 -                                      // ^ in $token will be outside quotes, need to be escaped
 -                                      $arg .= str_replace( '^', '^^', $token );
 -                              } else { // $iteration % 4 == 0
 -                                      // ^ in $token will appear inside double quotes, so leave as is
 -                                      $arg .= $token;
 -                              }
 -                              $iteration++;
 -                      }
 -                      // Double the backslashes before the end of the string, because
 -                      // we will soon add a quote
 -                      $m = [];
 -                      if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) {
 -                              $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] );
 -                      }
  
 -                      // Add surrounding quotes
 -                      $retVal .= '"' . $arg . '"';
 -              } else {
 -                      $retVal .= escapeshellarg( $arg );
 -              }
 -      }
 -      return $retVal;
 +      return call_user_func_array( Shell::class . '::escape', $args );
  }
  
  /**
   *
   * @return bool|string False or 'disabled'
   * @since 1.22
 + * @deprecated since 1.30 use MediaWiki\Shell::isDisabled()
   */
  function wfShellExecDisabled() {
 -      static $disabled = null;
 -      if ( is_null( $disabled ) ) {
 -              if ( !function_exists( 'proc_open' ) ) {
 -                      wfDebug( "proc_open() is disabled\n" );
 -                      $disabled = 'disabled';
 -              } else {
 -                      $disabled = false;
 -              }
 -      }
 -      return $disabled;
 +      return Shell::isDisabled() ? 'disabled' : false;
  }
  
  /**
   *     method. Set this to a string for an alternative method to profile from
   *
   * @return string Collected stdout as a string
 + * @deprecated since 1.30 use class MediaWiki\Shell\Shell
   */
  function wfShellExec( $cmd, &$retval = null, $environ = [],
        $limits = [], $options = []
  ) {
 -      global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime,
 -              $wgMaxShellWallClockTime, $wgShellCgroup;
 -
 -      $disabled = wfShellExecDisabled();
 -      if ( $disabled ) {
 +      if ( Shell::isDisabled() ) {
                $retval = 1;
 +              // Backwards compatibility be upon us...
                return 'Unable to run external programs, proc_open() is disabled.';
        }
  
 -      $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
 -      $profileMethod = isset( $options['profileMethod'] ) ? $options['profileMethod'] : wfGetCaller();
 -
 -      $envcmd = '';
 -      foreach ( $environ as $k => $v ) {
 -              if ( wfIsWindows() ) {
 -                      /* Surrounding a set in quotes (method used by wfEscapeShellArg) makes the quotes themselves
 -                       * appear in the environment variable, so we must use carat escaping as documented in
 -                       * https://technet.microsoft.com/en-us/library/cc723564.aspx
 -                       * Note however that the quote isn't listed there, but is needed, and the parentheses
 -                       * are listed there but doesn't appear to need it.
 -                       */
 -                      $envcmd .= "set $k=" . preg_replace( '/([&|()<>^"])/', '^\\1', $v ) . '&& ';
 -              } else {
 -                      /* Assume this is a POSIX shell, thus required to accept variable assignments before the command
 -                       * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_09_01
 -                       */
 -                      $envcmd .= "$k=" . escapeshellarg( $v ) . ' ';
 -              }
 -      }
        if ( is_array( $cmd ) ) {
 -              $cmd = wfEscapeShellArg( $cmd );
 +              $cmd = Shell::escape( $cmd );
        }
  
 -      $cmd = $envcmd . $cmd;
 +      $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
 +      $profileMethod = isset( $options['profileMethod'] ) ? $options['profileMethod'] : wfGetCaller();
  
 -      $useLogPipe = false;
 -      if ( is_executable( '/bin/bash' ) ) {
 -              $time = intval( isset( $limits['time'] ) ? $limits['time'] : $wgMaxShellTime );
 -              if ( isset( $limits['walltime'] ) ) {
 -                      $wallTime = intval( $limits['walltime'] );
 -              } elseif ( isset( $limits['time'] ) ) {
 -                      $wallTime = $time;
 -              } else {
 -                      $wallTime = intval( $wgMaxShellWallClockTime );
 -              }
 -              $mem = intval( isset( $limits['memory'] ) ? $limits['memory'] : $wgMaxShellMemory );
 -              $filesize = intval( isset( $limits['filesize'] ) ? $limits['filesize'] : $wgMaxShellFileSize );
 -
 -              if ( $time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0 ) {
 -                      $cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' .
 -                              escapeshellarg( $cmd ) . ' ' .
 -                              escapeshellarg(
 -                                      "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_USE_LOG_PIPE=yes"
 -                              );
 -                      $useLogPipe = true;
 -              } elseif ( $includeStderr ) {
 -                      $cmd .= ' 2>&1';
 -              }
 -      } elseif ( $includeStderr ) {
 -              $cmd .= ' 2>&1';
 -      }
 -      wfDebug( "wfShellExec: $cmd\n" );
 -
 -      // Don't try to execute commands that exceed Linux's MAX_ARG_STRLEN.
 -      // Other platforms may be more accomodating, but we don't want to be
 -      // accomodating, because very long commands probably include user
 -      // input. See T129506.
 -      if ( strlen( $cmd ) > SHELL_MAX_ARG_STRLEN ) {
 -              throw new Exception( __METHOD__ .
 -                      '(): total length of $cmd must not exceed SHELL_MAX_ARG_STRLEN' );
 -      }
 -
 -      $desc = [
 -              0 => [ 'file', 'php://stdin', 'r' ],
 -              1 => [ 'pipe', 'w' ],
 -              2 => [ 'file', 'php://stderr', 'w' ] ];
 -      if ( $useLogPipe ) {
 -              $desc[3] = [ 'pipe', 'w' ];
 -      }
 -      $pipes = null;
 -      $scoped = Profiler::instance()->scopedProfileIn( __FUNCTION__ . '-' . $profileMethod );
 -      $proc = proc_open( $cmd, $desc, $pipes );
 -      if ( !$proc ) {
 -              wfDebugLog( 'exec', "proc_open() failed: $cmd" );
 +      try {
 +              $result = Shell::command( [] )
 +                      ->unsafeParams( (array)$cmd )
 +                      ->environment( $environ )
 +                      ->limits( $limits )
 +                      ->includeStderr( $includeStderr )
 +                      ->profileMethod( $profileMethod )
 +                      ->execute();
 +      } catch ( ProcOpenError $ex ) {
                $retval = -1;
                return '';
        }
 -      $outBuffer = $logBuffer = '';
 -      $emptyArray = [];
 -      $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]";
 -
 -      $running = true;
 -      $timeout = null;
 -      $numReadyPipes = 0;
 -
 -      while ( $running === true || $numReadyPipes !== 0 ) {
 -              if ( $running ) {
 -                      $status = proc_get_status( $proc );
 -                      // If the process has terminated, switch to nonblocking selects
 -                      // for getting any data still waiting to be read.
 -                      if ( !$status['running'] ) {
 -                              $running = false;
 -                              $timeout = 0;
 -                      }
 -              }
 -
 -              $readyPipes = $pipes;
 -
 -              // Clear last error
 -              // @codingStandardsIgnoreStart Generic.PHP.NoSilencedErrors.Discouraged
 -              @trigger_error( '' );
 -              $numReadyPipes = @stream_select( $readyPipes, $emptyArray, $emptyArray, $timeout );
 -              if ( $numReadyPipes === false ) {
 -                      // @codingStandardsIgnoreEnd
 -                      $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 $fd => $pipe ) {
 -                      $block = fread( $pipe, 65536 );
 -                      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 );
 -      }
 -
 -      // Use the status previously collected if possible, since proc_get_status()
 -      // just calls waitpid() which will not return anything useful the second time.
 -      if ( $running ) {
 -              $status = proc_get_status( $proc );
 -      }
 +      $retval = $result->getExitCode();
  
 -      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" );
 -      }
 -
 -      return $outBuffer;
 +      return $result->getStdout();
  }
  
  /**
   * @param array $limits Optional array with limits(filesize, memory, time, walltime)
   *   this overwrites the global wgMaxShell* limits.
   * @return string Collected stdout and stderr as a string
 + * @deprecated since 1.30 use class MediaWiki\Shell\Shell
   */
  function wfShellExecWithStderr( $cmd, &$retval = null, $environ = [], $limits = [] ) {
        return wfShellExec( $cmd, $retval, $environ, $limits,
@@@ -2371,7 -2609,7 +2371,7 @@@ function wfShellWikiCmd( $script, arra
        }
        $cmd[] = $script;
        // Escape each parameter for shell
 -      return wfEscapeShellArg( array_merge( $cmd, $parameters ) );
 +      return Shell::escape( array_merge( $cmd, $parameters ) );
  }
  
  /**
@@@ -2416,7 -2654,7 +2416,7 @@@ function wfMerge( $old, $mine, $yours, 
        fclose( $yourtextFile );
  
        # Check for a conflict
 -      $cmd = wfEscapeShellArg( $wgDiff3, '-a', '--overlap-only', $mytextName,
 +      $cmd = Shell::escape( $wgDiff3, '-a', '--overlap-only', $mytextName,
                $oldtextName, $yourtextName );
        $handle = popen( $cmd, 'r' );
  
        pclose( $handle );
  
        # Merge differences
 -      $cmd = wfEscapeShellArg( $wgDiff3, '-a', '-e', '--merge', $mytextName,
 +      $cmd = Shell::escape( $wgDiff3, '-a', '-e', '--merge', $mytextName,
                $oldtextName, $yourtextName );
        $handle = popen( $cmd, 'r' );
        $result = '';
@@@ -2492,7 -2730,7 +2492,7 @@@ function wfDiff( $before, $after, $para
        fclose( $newtextFile );
  
        // Get the diff of the two files
 -      $cmd = "$wgDiff " . $params . ' ' . wfEscapeShellArg( $oldtextName, $newtextName );
 +      $cmd = "$wgDiff " . $params . ' ' . Shell::escape( $oldtextName, $newtextName );
  
        $h = popen( $cmd, 'r' );
        if ( !$h ) {
   * @see perldoc -f use
   *
   * @param string|int|float $req_ver The version to check, can be a string, an integer, or a float
 + *
 + * @deprecated since 1.30
 + *
   * @throws MWException
   */
  function wfUsePHP( $req_ver ) {
   *
   * @see perldoc -f use
   *
 - * @deprecated since 1.26, use the "requires' property of extension.json
 + * @deprecated since 1.26, use the "requires" property of extension.json
   * @param string|int|float $req_ver The version to check, can be a string, an integer, or a float
   * @throws MWException
   */
@@@ -2681,6 -2916,14 +2681,6 @@@ function wfBaseConvert( $input, $source
        return Wikimedia\base_convert( $input, $sourceBase, $destBase, $pad, $lowercase, $engine );
  }
  
 -/**
 - * @deprecated since 1.27, PHP's session generation isn't used with
 - *  MediaWiki\Session\SessionManager
 - */
 -function wfFixSessionID() {
 -      wfDeprecated( __FUNCTION__, '1.27' );
 -}
 -
  /**
   * Reset the session id
   *
@@@ -3160,6 -3403,7 +3160,7 @@@ function wfShorthandToInteger( $string 
  /**
   * Get the normalised IETF language tag
   * See unit test for examples.
+  * See mediawiki.language.bcp47 for the JavaScript implementation.
   *
   * @param string $code The language code.
   * @return string The language code which complying with BCP 47 standards.