]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/shell/Shell.php
MediaWiki 1.30.2
[autoinstalls/mediawiki.git] / includes / shell / Shell.php
1 <?php
2 /**
3  * Class used for executing shell commands
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  * @file
21  */
22
23 namespace MediaWiki\Shell;
24
25 use MediaWiki\MediaWikiServices;
26
27 /**
28  * Executes shell commands
29  *
30  * @since 1.30
31  *
32  * Use call chaining with this class for expressiveness:
33  *  $result = Shell::command( 'some command' )
34  *       ->environment( [ 'ENVIRONMENT_VARIABLE' => 'VALUE' ] )
35  *       ->limits( [ 'time' => 300 ] )
36  *       ->execute();
37  *
38  *  ... = $result->getExitCode();
39  *  ... = $result->getStdout();
40  */
41 class Shell {
42
43         /**
44          * Returns a new instance of Command class
45          *
46          * @param string|string[] $command String or array of strings representing the command to
47          * be executed, each value will be escaped.
48          *   Example:   [ 'convert', '-font', 'font name' ] would produce "'convert' '-font' 'font name'"
49          * @return Command
50          */
51         public static function command( $command ) {
52                 $args = func_get_args();
53                 if ( count( $args ) === 1 && is_array( reset( $args ) ) ) {
54                         // If only one argument has been passed, and that argument is an array,
55                         // treat it as a list of arguments
56                         $args = reset( $args );
57                 }
58                 $command = MediaWikiServices::getInstance()
59                         ->getShellCommandFactory()
60                         ->create();
61
62                 return $command->params( $args );
63         }
64
65         /**
66          * Check if this class is effectively disabled via php.ini config
67          *
68          * @return bool
69          */
70         public static function isDisabled() {
71                 static $disabled = null;
72
73                 if ( is_null( $disabled ) ) {
74                         if ( !function_exists( 'proc_open' ) ) {
75                                 wfDebug( "proc_open() is disabled\n" );
76                                 $disabled = true;
77                         } else {
78                                 $disabled = false;
79                         }
80                 }
81
82                 return $disabled;
83         }
84
85         /**
86          * Version of escapeshellarg() that works better on Windows.
87          *
88          * Originally, this fixed the incorrect use of single quotes on Windows
89          * (https://bugs.php.net/bug.php?id=26285) and the locale problems on Linux in
90          * PHP 5.2.6+ (bug backported to earlier distro releases of PHP).
91          *
92          * @param string $args,... strings to escape and glue together, or a single array of
93          *     strings parameter. Null values are ignored.
94          * @return string
95          */
96         public static function escape( /* ... */ ) {
97                 $args = func_get_args();
98                 if ( count( $args ) === 1 && is_array( reset( $args ) ) ) {
99                         // If only one argument has been passed, and that argument is an array,
100                         // treat it as a list of arguments
101                         $args = reset( $args );
102                 }
103
104                 $first = true;
105                 $retVal = '';
106                 foreach ( $args as $arg ) {
107                         if ( $arg === null ) {
108                                 continue;
109                         }
110                         if ( !$first ) {
111                                 $retVal .= ' ';
112                         } else {
113                                 $first = false;
114                         }
115
116                         if ( wfIsWindows() ) {
117                                 // Escaping for an MSVC-style command line parser and CMD.EXE
118                                 // @codingStandardsIgnoreStart For long URLs
119                                 // Refs:
120                                 //  * https://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html
121                                 //  * https://technet.microsoft.com/en-us/library/cc723564.aspx
122                                 //  * T15518
123                                 //  * CR r63214
124                                 // Double the backslashes before any double quotes. Escape the double quotes.
125                                 // @codingStandardsIgnoreEnd
126                                 $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE );
127                                 $arg = '';
128                                 $iteration = 0;
129                                 foreach ( $tokens as $token ) {
130                                         if ( $iteration % 2 == 1 ) {
131                                                 // Delimiter, a double quote preceded by zero or more slashes
132                                                 $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"';
133                                         } elseif ( $iteration % 4 == 2 ) {
134                                                 // ^ in $token will be outside quotes, need to be escaped
135                                                 $arg .= str_replace( '^', '^^', $token );
136                                         } else { // $iteration % 4 == 0
137                                                 // ^ in $token will appear inside double quotes, so leave as is
138                                                 $arg .= $token;
139                                         }
140                                         $iteration++;
141                                 }
142                                 // Double the backslashes before the end of the string, because
143                                 // we will soon add a quote
144                                 $m = [];
145                                 if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) {
146                                         $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] );
147                                 }
148
149                                 // Add surrounding quotes
150                                 $retVal .= '"' . $arg . '"';
151                         } else {
152                                 $retVal .= escapeshellarg( $arg );
153                         }
154                 }
155                 return $retVal;
156         }
157 }