Completed
Push — develop ( d12426...aff1bc )
by Zack
18:32
created
symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php 1 patch
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -1,6 +1,6 @@
 block discarded – undo
1 1
 <?php
2 2
 
3
-return array (
3
+return array(
4 4
   '̀' => 230,
5 5
   '́' => 230,
6 6
   '̂' => 230,
Please login to merge, or discard this patch.
vendor/symfony/console/Helper/ProcessHelper.php 3 patches
Indentation   +120 added lines, -120 removed lines patch added patch discarded remove patch
@@ -25,124 +25,124 @@
 block discarded – undo
25 25
  */
26 26
 class ProcessHelper extends Helper
27 27
 {
28
-    /**
29
-     * Runs an external process.
30
-     *
31
-     * @param array|Process $cmd      An instance of Process or an array of the command and arguments
32
-     * @param callable|null $callback A PHP callback to run whenever there is some
33
-     *                                output available on STDOUT or STDERR
34
-     *
35
-     * @return Process The process that ran
36
-     */
37
-    public function run(OutputInterface $output, $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process
38
-    {
39
-        if (!class_exists(Process::class)) {
40
-            throw new \LogicException('The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".');
41
-        }
42
-
43
-        if ($output instanceof ConsoleOutputInterface) {
44
-            $output = $output->getErrorOutput();
45
-        }
46
-
47
-        $formatter = $this->getHelperSet()->get('debug_formatter');
48
-
49
-        if ($cmd instanceof Process) {
50
-            $cmd = [$cmd];
51
-        }
52
-
53
-        if (!\is_array($cmd)) {
54
-            throw new \TypeError(sprintf('The "command" argument of "%s()" must be an array or a "%s" instance, "%s" given.', __METHOD__, Process::class, get_debug_type($cmd)));
55
-        }
56
-
57
-        if (\is_string($cmd[0] ?? null)) {
58
-            $process = new Process($cmd);
59
-            $cmd = [];
60
-        } elseif (($cmd[0] ?? null) instanceof Process) {
61
-            $process = $cmd[0];
62
-            unset($cmd[0]);
63
-        } else {
64
-            throw new \InvalidArgumentException(sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__));
65
-        }
66
-
67
-        if ($verbosity <= $output->getVerbosity()) {
68
-            $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine())));
69
-        }
70
-
71
-        if ($output->isDebug()) {
72
-            $callback = $this->wrapCallback($output, $process, $callback);
73
-        }
74
-
75
-        $process->run($callback, $cmd);
76
-
77
-        if ($verbosity <= $output->getVerbosity()) {
78
-            $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode());
79
-            $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
80
-        }
81
-
82
-        if (!$process->isSuccessful() && null !== $error) {
83
-            $output->writeln(sprintf('<error>%s</error>', $this->escapeString($error)));
84
-        }
85
-
86
-        return $process;
87
-    }
88
-
89
-    /**
90
-     * Runs the process.
91
-     *
92
-     * This is identical to run() except that an exception is thrown if the process
93
-     * exits with a non-zero exit code.
94
-     *
95
-     * @param string|Process $cmd      An instance of Process or a command to run
96
-     * @param callable|null  $callback A PHP callback to run whenever there is some
97
-     *                                 output available on STDOUT or STDERR
98
-     *
99
-     * @return Process The process that ran
100
-     *
101
-     * @throws ProcessFailedException
102
-     *
103
-     * @see run()
104
-     */
105
-    public function mustRun(OutputInterface $output, $cmd, string $error = null, callable $callback = null): Process
106
-    {
107
-        $process = $this->run($output, $cmd, $error, $callback);
108
-
109
-        if (!$process->isSuccessful()) {
110
-            throw new ProcessFailedException($process);
111
-        }
112
-
113
-        return $process;
114
-    }
115
-
116
-    /**
117
-     * Wraps a Process callback to add debugging output.
118
-     */
119
-    public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null): callable
120
-    {
121
-        if ($output instanceof ConsoleOutputInterface) {
122
-            $output = $output->getErrorOutput();
123
-        }
124
-
125
-        $formatter = $this->getHelperSet()->get('debug_formatter');
126
-
127
-        return function ($type, $buffer) use ($output, $process, $callback, $formatter) {
128
-            $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type));
129
-
130
-            if (null !== $callback) {
131
-                $callback($type, $buffer);
132
-            }
133
-        };
134
-    }
135
-
136
-    private function escapeString(string $str): string
137
-    {
138
-        return str_replace('<', '\\<', $str);
139
-    }
140
-
141
-    /**
142
-     * {@inheritdoc}
143
-     */
144
-    public function getName(): string
145
-    {
146
-        return 'process';
147
-    }
28
+	/**
29
+	 * Runs an external process.
30
+	 *
31
+	 * @param array|Process $cmd      An instance of Process or an array of the command and arguments
32
+	 * @param callable|null $callback A PHP callback to run whenever there is some
33
+	 *                                output available on STDOUT or STDERR
34
+	 *
35
+	 * @return Process The process that ran
36
+	 */
37
+	public function run(OutputInterface $output, $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process
38
+	{
39
+		if (!class_exists(Process::class)) {
40
+			throw new \LogicException('The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".');
41
+		}
42
+
43
+		if ($output instanceof ConsoleOutputInterface) {
44
+			$output = $output->getErrorOutput();
45
+		}
46
+
47
+		$formatter = $this->getHelperSet()->get('debug_formatter');
48
+
49
+		if ($cmd instanceof Process) {
50
+			$cmd = [$cmd];
51
+		}
52
+
53
+		if (!\is_array($cmd)) {
54
+			throw new \TypeError(sprintf('The "command" argument of "%s()" must be an array or a "%s" instance, "%s" given.', __METHOD__, Process::class, get_debug_type($cmd)));
55
+		}
56
+
57
+		if (\is_string($cmd[0] ?? null)) {
58
+			$process = new Process($cmd);
59
+			$cmd = [];
60
+		} elseif (($cmd[0] ?? null) instanceof Process) {
61
+			$process = $cmd[0];
62
+			unset($cmd[0]);
63
+		} else {
64
+			throw new \InvalidArgumentException(sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__));
65
+		}
66
+
67
+		if ($verbosity <= $output->getVerbosity()) {
68
+			$output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine())));
69
+		}
70
+
71
+		if ($output->isDebug()) {
72
+			$callback = $this->wrapCallback($output, $process, $callback);
73
+		}
74
+
75
+		$process->run($callback, $cmd);
76
+
77
+		if ($verbosity <= $output->getVerbosity()) {
78
+			$message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode());
79
+			$output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
80
+		}
81
+
82
+		if (!$process->isSuccessful() && null !== $error) {
83
+			$output->writeln(sprintf('<error>%s</error>', $this->escapeString($error)));
84
+		}
85
+
86
+		return $process;
87
+	}
88
+
89
+	/**
90
+	 * Runs the process.
91
+	 *
92
+	 * This is identical to run() except that an exception is thrown if the process
93
+	 * exits with a non-zero exit code.
94
+	 *
95
+	 * @param string|Process $cmd      An instance of Process or a command to run
96
+	 * @param callable|null  $callback A PHP callback to run whenever there is some
97
+	 *                                 output available on STDOUT or STDERR
98
+	 *
99
+	 * @return Process The process that ran
100
+	 *
101
+	 * @throws ProcessFailedException
102
+	 *
103
+	 * @see run()
104
+	 */
105
+	public function mustRun(OutputInterface $output, $cmd, string $error = null, callable $callback = null): Process
106
+	{
107
+		$process = $this->run($output, $cmd, $error, $callback);
108
+
109
+		if (!$process->isSuccessful()) {
110
+			throw new ProcessFailedException($process);
111
+		}
112
+
113
+		return $process;
114
+	}
115
+
116
+	/**
117
+	 * Wraps a Process callback to add debugging output.
118
+	 */
119
+	public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null): callable
120
+	{
121
+		if ($output instanceof ConsoleOutputInterface) {
122
+			$output = $output->getErrorOutput();
123
+		}
124
+
125
+		$formatter = $this->getHelperSet()->get('debug_formatter');
126
+
127
+		return function ($type, $buffer) use ($output, $process, $callback, $formatter) {
128
+			$output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type));
129
+
130
+			if (null !== $callback) {
131
+				$callback($type, $buffer);
132
+			}
133
+		};
134
+	}
135
+
136
+	private function escapeString(string $str): string
137
+	{
138
+		return str_replace('<', '\\<', $str);
139
+	}
140
+
141
+	/**
142
+	 * {@inheritdoc}
143
+	 */
144
+	public function getName(): string
145
+	{
146
+		return 'process';
147
+	}
148 148
 }
Please login to merge, or discard this patch.
Spacing   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -34,53 +34,53 @@  discard block
 block discarded – undo
34 34
      *
35 35
      * @return Process The process that ran
36 36
      */
37
-    public function run(OutputInterface $output, $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process
37
+    public function run( OutputInterface $output, $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE ): Process
38 38
     {
39
-        if (!class_exists(Process::class)) {
40
-            throw new \LogicException('The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".');
39
+        if ( ! class_exists( Process::class ) ) {
40
+            throw new \LogicException( 'The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".' );
41 41
         }
42 42
 
43
-        if ($output instanceof ConsoleOutputInterface) {
43
+        if ( $output instanceof ConsoleOutputInterface ) {
44 44
             $output = $output->getErrorOutput();
45 45
         }
46 46
 
47
-        $formatter = $this->getHelperSet()->get('debug_formatter');
47
+        $formatter = $this->getHelperSet()->get( 'debug_formatter' );
48 48
 
49
-        if ($cmd instanceof Process) {
50
-            $cmd = [$cmd];
49
+        if ( $cmd instanceof Process ) {
50
+            $cmd = [ $cmd ];
51 51
         }
52 52
 
53
-        if (!\is_array($cmd)) {
54
-            throw new \TypeError(sprintf('The "command" argument of "%s()" must be an array or a "%s" instance, "%s" given.', __METHOD__, Process::class, get_debug_type($cmd)));
53
+        if ( ! \is_array( $cmd ) ) {
54
+            throw new \TypeError( sprintf( 'The "command" argument of "%s()" must be an array or a "%s" instance, "%s" given.', __METHOD__, Process::class, get_debug_type( $cmd ) ) );
55 55
         }
56 56
 
57
-        if (\is_string($cmd[0] ?? null)) {
58
-            $process = new Process($cmd);
59
-            $cmd = [];
60
-        } elseif (($cmd[0] ?? null) instanceof Process) {
61
-            $process = $cmd[0];
62
-            unset($cmd[0]);
57
+        if ( \is_string( $cmd[ 0 ] ?? null ) ) {
58
+            $process = new Process( $cmd );
59
+            $cmd = [ ];
60
+        } elseif ( ( $cmd[ 0 ] ?? null ) instanceof Process ) {
61
+            $process = $cmd[ 0 ];
62
+            unset( $cmd[ 0 ] );
63 63
         } else {
64
-            throw new \InvalidArgumentException(sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__));
64
+            throw new \InvalidArgumentException( sprintf( 'Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__ ) );
65 65
         }
66 66
 
67
-        if ($verbosity <= $output->getVerbosity()) {
68
-            $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine())));
67
+        if ( $verbosity <= $output->getVerbosity() ) {
68
+            $output->write( $formatter->start( spl_object_hash( $process ), $this->escapeString( $process->getCommandLine() ) ) );
69 69
         }
70 70
 
71
-        if ($output->isDebug()) {
72
-            $callback = $this->wrapCallback($output, $process, $callback);
71
+        if ( $output->isDebug() ) {
72
+            $callback = $this->wrapCallback( $output, $process, $callback );
73 73
         }
74 74
 
75
-        $process->run($callback, $cmd);
75
+        $process->run( $callback, $cmd );
76 76
 
77
-        if ($verbosity <= $output->getVerbosity()) {
78
-            $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode());
79
-            $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
77
+        if ( $verbosity <= $output->getVerbosity() ) {
78
+            $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf( '%s Command did not run successfully', $process->getExitCode() );
79
+            $output->write( $formatter->stop( spl_object_hash( $process ), $message, $process->isSuccessful() ) );
80 80
         }
81 81
 
82
-        if (!$process->isSuccessful() && null !== $error) {
83
-            $output->writeln(sprintf('<error>%s</error>', $this->escapeString($error)));
82
+        if ( ! $process->isSuccessful() && null !== $error ) {
83
+            $output->writeln( sprintf( '<error>%s</error>', $this->escapeString( $error ) ) );
84 84
         }
85 85
 
86 86
         return $process;
@@ -102,12 +102,12 @@  discard block
 block discarded – undo
102 102
      *
103 103
      * @see run()
104 104
      */
105
-    public function mustRun(OutputInterface $output, $cmd, string $error = null, callable $callback = null): Process
105
+    public function mustRun( OutputInterface $output, $cmd, string $error = null, callable $callback = null ): Process
106 106
     {
107
-        $process = $this->run($output, $cmd, $error, $callback);
107
+        $process = $this->run( $output, $cmd, $error, $callback );
108 108
 
109
-        if (!$process->isSuccessful()) {
110
-            throw new ProcessFailedException($process);
109
+        if ( ! $process->isSuccessful() ) {
110
+            throw new ProcessFailedException( $process );
111 111
         }
112 112
 
113 113
         return $process;
@@ -116,26 +116,26 @@  discard block
 block discarded – undo
116 116
     /**
117 117
      * Wraps a Process callback to add debugging output.
118 118
      */
119
-    public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null): callable
119
+    public function wrapCallback( OutputInterface $output, Process $process, callable $callback = null ): callable
120 120
     {
121
-        if ($output instanceof ConsoleOutputInterface) {
121
+        if ( $output instanceof ConsoleOutputInterface ) {
122 122
             $output = $output->getErrorOutput();
123 123
         }
124 124
 
125
-        $formatter = $this->getHelperSet()->get('debug_formatter');
125
+        $formatter = $this->getHelperSet()->get( 'debug_formatter' );
126 126
 
127
-        return function ($type, $buffer) use ($output, $process, $callback, $formatter) {
128
-            $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type));
127
+        return function( $type, $buffer ) use ( $output, $process, $callback, $formatter ) {
128
+            $output->write( $formatter->progress( spl_object_hash( $process ), $this->escapeString( $buffer ), Process::ERR === $type ) );
129 129
 
130
-            if (null !== $callback) {
131
-                $callback($type, $buffer);
130
+            if ( null !== $callback ) {
131
+                $callback( $type, $buffer );
132 132
             }
133 133
         };
134 134
     }
135 135
 
136
-    private function escapeString(string $str): string
136
+    private function escapeString( string $str ): string
137 137
     {
138
-        return str_replace('<', '\\<', $str);
138
+        return str_replace( '<', '\\<', $str );
139 139
     }
140 140
 
141 141
     /**
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -23,8 +23,7 @@
 block discarded – undo
23 23
  *
24 24
  * @final
25 25
  */
26
-class ProcessHelper extends Helper
27
-{
26
+class ProcessHelper extends Helper {
28 27
     /**
29 28
      * Runs an external process.
30 29
      *
Please login to merge, or discard this patch.
vendor/symfony/console/Helper/ProgressIndicator.php 3 patches
Indentation   +231 added lines, -231 removed lines patch added patch discarded remove patch
@@ -20,235 +20,235 @@
 block discarded – undo
20 20
  */
21 21
 class ProgressIndicator
22 22
 {
23
-    private $output;
24
-    private $startTime;
25
-    private $format;
26
-    private $message;
27
-    private $indicatorValues;
28
-    private $indicatorCurrent;
29
-    private $indicatorChangeInterval;
30
-    private $indicatorUpdateTime;
31
-    private $started = false;
32
-
33
-    private static $formatters;
34
-    private static $formats;
35
-
36
-    /**
37
-     * @param int        $indicatorChangeInterval Change interval in milliseconds
38
-     * @param array|null $indicatorValues         Animated indicator characters
39
-     */
40
-    public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null)
41
-    {
42
-        $this->output = $output;
43
-
44
-        if (null === $format) {
45
-            $format = $this->determineBestFormat();
46
-        }
47
-
48
-        if (null === $indicatorValues) {
49
-            $indicatorValues = ['-', '\\', '|', '/'];
50
-        }
51
-
52
-        $indicatorValues = array_values($indicatorValues);
53
-
54
-        if (2 > \count($indicatorValues)) {
55
-            throw new InvalidArgumentException('Must have at least 2 indicator value characters.');
56
-        }
57
-
58
-        $this->format = self::getFormatDefinition($format);
59
-        $this->indicatorChangeInterval = $indicatorChangeInterval;
60
-        $this->indicatorValues = $indicatorValues;
61
-        $this->startTime = time();
62
-    }
63
-
64
-    /**
65
-     * Sets the current indicator message.
66
-     */
67
-    public function setMessage(?string $message)
68
-    {
69
-        $this->message = $message;
70
-
71
-        $this->display();
72
-    }
73
-
74
-    /**
75
-     * Starts the indicator output.
76
-     */
77
-    public function start(string $message)
78
-    {
79
-        if ($this->started) {
80
-            throw new LogicException('Progress indicator already started.');
81
-        }
82
-
83
-        $this->message = $message;
84
-        $this->started = true;
85
-        $this->startTime = time();
86
-        $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval;
87
-        $this->indicatorCurrent = 0;
88
-
89
-        $this->display();
90
-    }
91
-
92
-    /**
93
-     * Advances the indicator.
94
-     */
95
-    public function advance()
96
-    {
97
-        if (!$this->started) {
98
-            throw new LogicException('Progress indicator has not yet been started.');
99
-        }
100
-
101
-        if (!$this->output->isDecorated()) {
102
-            return;
103
-        }
104
-
105
-        $currentTime = $this->getCurrentTimeInMilliseconds();
106
-
107
-        if ($currentTime < $this->indicatorUpdateTime) {
108
-            return;
109
-        }
110
-
111
-        $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval;
112
-        ++$this->indicatorCurrent;
113
-
114
-        $this->display();
115
-    }
116
-
117
-    /**
118
-     * Finish the indicator with message.
119
-     *
120
-     * @param $message
121
-     */
122
-    public function finish(string $message)
123
-    {
124
-        if (!$this->started) {
125
-            throw new LogicException('Progress indicator has not yet been started.');
126
-        }
127
-
128
-        $this->message = $message;
129
-        $this->display();
130
-        $this->output->writeln('');
131
-        $this->started = false;
132
-    }
133
-
134
-    /**
135
-     * Gets the format for a given name.
136
-     *
137
-     * @return string|null A format string
138
-     */
139
-    public static function getFormatDefinition(string $name)
140
-    {
141
-        if (!self::$formats) {
142
-            self::$formats = self::initFormats();
143
-        }
144
-
145
-        return self::$formats[$name] ?? null;
146
-    }
147
-
148
-    /**
149
-     * Sets a placeholder formatter for a given name.
150
-     *
151
-     * This method also allow you to override an existing placeholder.
152
-     */
153
-    public static function setPlaceholderFormatterDefinition(string $name, callable $callable)
154
-    {
155
-        if (!self::$formatters) {
156
-            self::$formatters = self::initPlaceholderFormatters();
157
-        }
158
-
159
-        self::$formatters[$name] = $callable;
160
-    }
161
-
162
-    /**
163
-     * Gets the placeholder formatter for a given name (including the delimiter char like %).
164
-     *
165
-     * @return callable|null A PHP callable
166
-     */
167
-    public static function getPlaceholderFormatterDefinition(string $name)
168
-    {
169
-        if (!self::$formatters) {
170
-            self::$formatters = self::initPlaceholderFormatters();
171
-        }
172
-
173
-        return self::$formatters[$name] ?? null;
174
-    }
175
-
176
-    private function display()
177
-    {
178
-        if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
179
-            return;
180
-        }
181
-
182
-        $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) {
183
-            if ($formatter = self::getPlaceholderFormatterDefinition($matches[1])) {
184
-                return $formatter($this);
185
-            }
186
-
187
-            return $matches[0];
188
-        }, $this->format ?? ''));
189
-    }
190
-
191
-    private function determineBestFormat(): string
192
-    {
193
-        switch ($this->output->getVerbosity()) {
194
-            // OutputInterface::VERBOSITY_QUIET: display is disabled anyway
195
-            case OutputInterface::VERBOSITY_VERBOSE:
196
-                return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi';
197
-            case OutputInterface::VERBOSITY_VERY_VERBOSE:
198
-            case OutputInterface::VERBOSITY_DEBUG:
199
-                return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi';
200
-            default:
201
-                return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi';
202
-        }
203
-    }
204
-
205
-    /**
206
-     * Overwrites a previous message to the output.
207
-     */
208
-    private function overwrite(string $message)
209
-    {
210
-        if ($this->output->isDecorated()) {
211
-            $this->output->write("\x0D\x1B[2K");
212
-            $this->output->write($message);
213
-        } else {
214
-            $this->output->writeln($message);
215
-        }
216
-    }
217
-
218
-    private function getCurrentTimeInMilliseconds(): float
219
-    {
220
-        return round(microtime(true) * 1000);
221
-    }
222
-
223
-    private static function initPlaceholderFormatters(): array
224
-    {
225
-        return [
226
-            'indicator' => function (self $indicator) {
227
-                return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)];
228
-            },
229
-            'message' => function (self $indicator) {
230
-                return $indicator->message;
231
-            },
232
-            'elapsed' => function (self $indicator) {
233
-                return Helper::formatTime(time() - $indicator->startTime);
234
-            },
235
-            'memory' => function () {
236
-                return Helper::formatMemory(memory_get_usage(true));
237
-            },
238
-        ];
239
-    }
240
-
241
-    private static function initFormats(): array
242
-    {
243
-        return [
244
-            'normal' => ' %indicator% %message%',
245
-            'normal_no_ansi' => ' %message%',
246
-
247
-            'verbose' => ' %indicator% %message% (%elapsed:6s%)',
248
-            'verbose_no_ansi' => ' %message% (%elapsed:6s%)',
249
-
250
-            'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)',
251
-            'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)',
252
-        ];
253
-    }
23
+	private $output;
24
+	private $startTime;
25
+	private $format;
26
+	private $message;
27
+	private $indicatorValues;
28
+	private $indicatorCurrent;
29
+	private $indicatorChangeInterval;
30
+	private $indicatorUpdateTime;
31
+	private $started = false;
32
+
33
+	private static $formatters;
34
+	private static $formats;
35
+
36
+	/**
37
+	 * @param int        $indicatorChangeInterval Change interval in milliseconds
38
+	 * @param array|null $indicatorValues         Animated indicator characters
39
+	 */
40
+	public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null)
41
+	{
42
+		$this->output = $output;
43
+
44
+		if (null === $format) {
45
+			$format = $this->determineBestFormat();
46
+		}
47
+
48
+		if (null === $indicatorValues) {
49
+			$indicatorValues = ['-', '\\', '|', '/'];
50
+		}
51
+
52
+		$indicatorValues = array_values($indicatorValues);
53
+
54
+		if (2 > \count($indicatorValues)) {
55
+			throw new InvalidArgumentException('Must have at least 2 indicator value characters.');
56
+		}
57
+
58
+		$this->format = self::getFormatDefinition($format);
59
+		$this->indicatorChangeInterval = $indicatorChangeInterval;
60
+		$this->indicatorValues = $indicatorValues;
61
+		$this->startTime = time();
62
+	}
63
+
64
+	/**
65
+	 * Sets the current indicator message.
66
+	 */
67
+	public function setMessage(?string $message)
68
+	{
69
+		$this->message = $message;
70
+
71
+		$this->display();
72
+	}
73
+
74
+	/**
75
+	 * Starts the indicator output.
76
+	 */
77
+	public function start(string $message)
78
+	{
79
+		if ($this->started) {
80
+			throw new LogicException('Progress indicator already started.');
81
+		}
82
+
83
+		$this->message = $message;
84
+		$this->started = true;
85
+		$this->startTime = time();
86
+		$this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval;
87
+		$this->indicatorCurrent = 0;
88
+
89
+		$this->display();
90
+	}
91
+
92
+	/**
93
+	 * Advances the indicator.
94
+	 */
95
+	public function advance()
96
+	{
97
+		if (!$this->started) {
98
+			throw new LogicException('Progress indicator has not yet been started.');
99
+		}
100
+
101
+		if (!$this->output->isDecorated()) {
102
+			return;
103
+		}
104
+
105
+		$currentTime = $this->getCurrentTimeInMilliseconds();
106
+
107
+		if ($currentTime < $this->indicatorUpdateTime) {
108
+			return;
109
+		}
110
+
111
+		$this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval;
112
+		++$this->indicatorCurrent;
113
+
114
+		$this->display();
115
+	}
116
+
117
+	/**
118
+	 * Finish the indicator with message.
119
+	 *
120
+	 * @param $message
121
+	 */
122
+	public function finish(string $message)
123
+	{
124
+		if (!$this->started) {
125
+			throw new LogicException('Progress indicator has not yet been started.');
126
+		}
127
+
128
+		$this->message = $message;
129
+		$this->display();
130
+		$this->output->writeln('');
131
+		$this->started = false;
132
+	}
133
+
134
+	/**
135
+	 * Gets the format for a given name.
136
+	 *
137
+	 * @return string|null A format string
138
+	 */
139
+	public static function getFormatDefinition(string $name)
140
+	{
141
+		if (!self::$formats) {
142
+			self::$formats = self::initFormats();
143
+		}
144
+
145
+		return self::$formats[$name] ?? null;
146
+	}
147
+
148
+	/**
149
+	 * Sets a placeholder formatter for a given name.
150
+	 *
151
+	 * This method also allow you to override an existing placeholder.
152
+	 */
153
+	public static function setPlaceholderFormatterDefinition(string $name, callable $callable)
154
+	{
155
+		if (!self::$formatters) {
156
+			self::$formatters = self::initPlaceholderFormatters();
157
+		}
158
+
159
+		self::$formatters[$name] = $callable;
160
+	}
161
+
162
+	/**
163
+	 * Gets the placeholder formatter for a given name (including the delimiter char like %).
164
+	 *
165
+	 * @return callable|null A PHP callable
166
+	 */
167
+	public static function getPlaceholderFormatterDefinition(string $name)
168
+	{
169
+		if (!self::$formatters) {
170
+			self::$formatters = self::initPlaceholderFormatters();
171
+		}
172
+
173
+		return self::$formatters[$name] ?? null;
174
+	}
175
+
176
+	private function display()
177
+	{
178
+		if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
179
+			return;
180
+		}
181
+
182
+		$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) {
183
+			if ($formatter = self::getPlaceholderFormatterDefinition($matches[1])) {
184
+				return $formatter($this);
185
+			}
186
+
187
+			return $matches[0];
188
+		}, $this->format ?? ''));
189
+	}
190
+
191
+	private function determineBestFormat(): string
192
+	{
193
+		switch ($this->output->getVerbosity()) {
194
+			// OutputInterface::VERBOSITY_QUIET: display is disabled anyway
195
+			case OutputInterface::VERBOSITY_VERBOSE:
196
+				return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi';
197
+			case OutputInterface::VERBOSITY_VERY_VERBOSE:
198
+			case OutputInterface::VERBOSITY_DEBUG:
199
+				return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi';
200
+			default:
201
+				return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi';
202
+		}
203
+	}
204
+
205
+	/**
206
+	 * Overwrites a previous message to the output.
207
+	 */
208
+	private function overwrite(string $message)
209
+	{
210
+		if ($this->output->isDecorated()) {
211
+			$this->output->write("\x0D\x1B[2K");
212
+			$this->output->write($message);
213
+		} else {
214
+			$this->output->writeln($message);
215
+		}
216
+	}
217
+
218
+	private function getCurrentTimeInMilliseconds(): float
219
+	{
220
+		return round(microtime(true) * 1000);
221
+	}
222
+
223
+	private static function initPlaceholderFormatters(): array
224
+	{
225
+		return [
226
+			'indicator' => function (self $indicator) {
227
+				return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)];
228
+			},
229
+			'message' => function (self $indicator) {
230
+				return $indicator->message;
231
+			},
232
+			'elapsed' => function (self $indicator) {
233
+				return Helper::formatTime(time() - $indicator->startTime);
234
+			},
235
+			'memory' => function () {
236
+				return Helper::formatMemory(memory_get_usage(true));
237
+			},
238
+		];
239
+	}
240
+
241
+	private static function initFormats(): array
242
+	{
243
+		return [
244
+			'normal' => ' %indicator% %message%',
245
+			'normal_no_ansi' => ' %message%',
246
+
247
+			'verbose' => ' %indicator% %message% (%elapsed:6s%)',
248
+			'verbose_no_ansi' => ' %message% (%elapsed:6s%)',
249
+
250
+			'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)',
251
+			'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)',
252
+		];
253
+	}
254 254
 }
Please login to merge, or discard this patch.
Spacing   +49 added lines, -49 removed lines patch added patch discarded remove patch
@@ -37,25 +37,25 @@  discard block
 block discarded – undo
37 37
      * @param int        $indicatorChangeInterval Change interval in milliseconds
38 38
      * @param array|null $indicatorValues         Animated indicator characters
39 39
      */
40
-    public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null)
40
+    public function __construct( OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null )
41 41
     {
42 42
         $this->output = $output;
43 43
 
44
-        if (null === $format) {
44
+        if ( null === $format ) {
45 45
             $format = $this->determineBestFormat();
46 46
         }
47 47
 
48
-        if (null === $indicatorValues) {
49
-            $indicatorValues = ['-', '\\', '|', '/'];
48
+        if ( null === $indicatorValues ) {
49
+            $indicatorValues = [ '-', '\\', '|', '/' ];
50 50
         }
51 51
 
52
-        $indicatorValues = array_values($indicatorValues);
52
+        $indicatorValues = array_values( $indicatorValues );
53 53
 
54
-        if (2 > \count($indicatorValues)) {
55
-            throw new InvalidArgumentException('Must have at least 2 indicator value characters.');
54
+        if ( 2 > \count( $indicatorValues ) ) {
55
+            throw new InvalidArgumentException( 'Must have at least 2 indicator value characters.' );
56 56
         }
57 57
 
58
-        $this->format = self::getFormatDefinition($format);
58
+        $this->format = self::getFormatDefinition( $format );
59 59
         $this->indicatorChangeInterval = $indicatorChangeInterval;
60 60
         $this->indicatorValues = $indicatorValues;
61 61
         $this->startTime = time();
@@ -64,7 +64,7 @@  discard block
 block discarded – undo
64 64
     /**
65 65
      * Sets the current indicator message.
66 66
      */
67
-    public function setMessage(?string $message)
67
+    public function setMessage( ?string $message )
68 68
     {
69 69
         $this->message = $message;
70 70
 
@@ -74,10 +74,10 @@  discard block
 block discarded – undo
74 74
     /**
75 75
      * Starts the indicator output.
76 76
      */
77
-    public function start(string $message)
77
+    public function start( string $message )
78 78
     {
79
-        if ($this->started) {
80
-            throw new LogicException('Progress indicator already started.');
79
+        if ( $this->started ) {
80
+            throw new LogicException( 'Progress indicator already started.' );
81 81
         }
82 82
 
83 83
         $this->message = $message;
@@ -94,17 +94,17 @@  discard block
 block discarded – undo
94 94
      */
95 95
     public function advance()
96 96
     {
97
-        if (!$this->started) {
98
-            throw new LogicException('Progress indicator has not yet been started.');
97
+        if ( ! $this->started ) {
98
+            throw new LogicException( 'Progress indicator has not yet been started.' );
99 99
         }
100 100
 
101
-        if (!$this->output->isDecorated()) {
101
+        if ( ! $this->output->isDecorated() ) {
102 102
             return;
103 103
         }
104 104
 
105 105
         $currentTime = $this->getCurrentTimeInMilliseconds();
106 106
 
107
-        if ($currentTime < $this->indicatorUpdateTime) {
107
+        if ( $currentTime < $this->indicatorUpdateTime ) {
108 108
             return;
109 109
         }
110 110
 
@@ -119,15 +119,15 @@  discard block
 block discarded – undo
119 119
      *
120 120
      * @param $message
121 121
      */
122
-    public function finish(string $message)
122
+    public function finish( string $message )
123 123
     {
124
-        if (!$this->started) {
125
-            throw new LogicException('Progress indicator has not yet been started.');
124
+        if ( ! $this->started ) {
125
+            throw new LogicException( 'Progress indicator has not yet been started.' );
126 126
         }
127 127
 
128 128
         $this->message = $message;
129 129
         $this->display();
130
-        $this->output->writeln('');
130
+        $this->output->writeln( '' );
131 131
         $this->started = false;
132 132
     }
133 133
 
@@ -136,13 +136,13 @@  discard block
 block discarded – undo
136 136
      *
137 137
      * @return string|null A format string
138 138
      */
139
-    public static function getFormatDefinition(string $name)
139
+    public static function getFormatDefinition( string $name )
140 140
     {
141
-        if (!self::$formats) {
141
+        if ( ! self::$formats ) {
142 142
             self::$formats = self::initFormats();
143 143
         }
144 144
 
145
-        return self::$formats[$name] ?? null;
145
+        return self::$formats[ $name ] ?? null;
146 146
     }
147 147
 
148 148
     /**
@@ -150,13 +150,13 @@  discard block
 block discarded – undo
150 150
      *
151 151
      * This method also allow you to override an existing placeholder.
152 152
      */
153
-    public static function setPlaceholderFormatterDefinition(string $name, callable $callable)
153
+    public static function setPlaceholderFormatterDefinition( string $name, callable $callable )
154 154
     {
155
-        if (!self::$formatters) {
155
+        if ( ! self::$formatters ) {
156 156
             self::$formatters = self::initPlaceholderFormatters();
157 157
         }
158 158
 
159
-        self::$formatters[$name] = $callable;
159
+        self::$formatters[ $name ] = $callable;
160 160
     }
161 161
 
162 162
     /**
@@ -164,33 +164,33 @@  discard block
 block discarded – undo
164 164
      *
165 165
      * @return callable|null A PHP callable
166 166
      */
167
-    public static function getPlaceholderFormatterDefinition(string $name)
167
+    public static function getPlaceholderFormatterDefinition( string $name )
168 168
     {
169
-        if (!self::$formatters) {
169
+        if ( ! self::$formatters ) {
170 170
             self::$formatters = self::initPlaceholderFormatters();
171 171
         }
172 172
 
173
-        return self::$formatters[$name] ?? null;
173
+        return self::$formatters[ $name ] ?? null;
174 174
     }
175 175
 
176 176
     private function display()
177 177
     {
178
-        if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
178
+        if ( OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity() ) {
179 179
             return;
180 180
         }
181 181
 
182
-        $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) {
183
-            if ($formatter = self::getPlaceholderFormatterDefinition($matches[1])) {
184
-                return $formatter($this);
182
+        $this->overwrite( preg_replace_callback( "{%([a-z\-_]+)(?:\:([^%]+))?%}i", function( $matches ) {
183
+            if ( $formatter = self::getPlaceholderFormatterDefinition( $matches[ 1 ] ) ) {
184
+                return $formatter( $this );
185 185
             }
186 186
 
187
-            return $matches[0];
188
-        }, $this->format ?? ''));
187
+            return $matches[ 0 ];
188
+        }, $this->format ?? '' ) );
189 189
     }
190 190
 
191 191
     private function determineBestFormat(): string
192 192
     {
193
-        switch ($this->output->getVerbosity()) {
193
+        switch ( $this->output->getVerbosity() ) {
194 194
             // OutputInterface::VERBOSITY_QUIET: display is disabled anyway
195 195
             case OutputInterface::VERBOSITY_VERBOSE:
196 196
                 return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi';
@@ -205,35 +205,35 @@  discard block
 block discarded – undo
205 205
     /**
206 206
      * Overwrites a previous message to the output.
207 207
      */
208
-    private function overwrite(string $message)
208
+    private function overwrite( string $message )
209 209
     {
210
-        if ($this->output->isDecorated()) {
211
-            $this->output->write("\x0D\x1B[2K");
212
-            $this->output->write($message);
210
+        if ( $this->output->isDecorated() ) {
211
+            $this->output->write( "\x0D\x1B[2K" );
212
+            $this->output->write( $message );
213 213
         } else {
214
-            $this->output->writeln($message);
214
+            $this->output->writeln( $message );
215 215
         }
216 216
     }
217 217
 
218 218
     private function getCurrentTimeInMilliseconds(): float
219 219
     {
220
-        return round(microtime(true) * 1000);
220
+        return round( microtime( true ) * 1000 );
221 221
     }
222 222
 
223 223
     private static function initPlaceholderFormatters(): array
224 224
     {
225 225
         return [
226
-            'indicator' => function (self $indicator) {
227
-                return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)];
226
+            'indicator' => function( self $indicator ) {
227
+                return $indicator->indicatorValues[ $indicator->indicatorCurrent % \count( $indicator->indicatorValues ) ];
228 228
             },
229
-            'message' => function (self $indicator) {
229
+            'message' => function( self $indicator ) {
230 230
                 return $indicator->message;
231 231
             },
232
-            'elapsed' => function (self $indicator) {
233
-                return Helper::formatTime(time() - $indicator->startTime);
232
+            'elapsed' => function( self $indicator ) {
233
+                return Helper::formatTime( time() - $indicator->startTime );
234 234
             },
235
-            'memory' => function () {
236
-                return Helper::formatMemory(memory_get_usage(true));
235
+            'memory' => function() {
236
+                return Helper::formatMemory( memory_get_usage( true ) );
237 237
             },
238 238
         ];
239 239
     }
Please login to merge, or discard this patch.
Braces   +11 added lines, -22 removed lines patch added patch discarded remove patch
@@ -18,8 +18,7 @@  discard block
 block discarded – undo
18 18
 /**
19 19
  * @author Kevin Bond <[email protected]>
20 20
  */
21
-class ProgressIndicator
22
-{
21
+class ProgressIndicator {
23 22
     private $output;
24 23
     private $startTime;
25 24
     private $format;
@@ -37,8 +36,7 @@  discard block
 block discarded – undo
37 36
      * @param int        $indicatorChangeInterval Change interval in milliseconds
38 37
      * @param array|null $indicatorValues         Animated indicator characters
39 38
      */
40
-    public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null)
41
-    {
39
+    public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null) {
42 40
         $this->output = $output;
43 41
 
44 42
         if (null === $format) {
@@ -64,8 +62,7 @@  discard block
 block discarded – undo
64 62
     /**
65 63
      * Sets the current indicator message.
66 64
      */
67
-    public function setMessage(?string $message)
68
-    {
65
+    public function setMessage(?string $message) {
69 66
         $this->message = $message;
70 67
 
71 68
         $this->display();
@@ -74,8 +71,7 @@  discard block
 block discarded – undo
74 71
     /**
75 72
      * Starts the indicator output.
76 73
      */
77
-    public function start(string $message)
78
-    {
74
+    public function start(string $message) {
79 75
         if ($this->started) {
80 76
             throw new LogicException('Progress indicator already started.');
81 77
         }
@@ -92,8 +88,7 @@  discard block
 block discarded – undo
92 88
     /**
93 89
      * Advances the indicator.
94 90
      */
95
-    public function advance()
96
-    {
91
+    public function advance() {
97 92
         if (!$this->started) {
98 93
             throw new LogicException('Progress indicator has not yet been started.');
99 94
         }
@@ -119,8 +114,7 @@  discard block
 block discarded – undo
119 114
      *
120 115
      * @param $message
121 116
      */
122
-    public function finish(string $message)
123
-    {
117
+    public function finish(string $message) {
124 118
         if (!$this->started) {
125 119
             throw new LogicException('Progress indicator has not yet been started.');
126 120
         }
@@ -136,8 +130,7 @@  discard block
 block discarded – undo
136 130
      *
137 131
      * @return string|null A format string
138 132
      */
139
-    public static function getFormatDefinition(string $name)
140
-    {
133
+    public static function getFormatDefinition(string $name) {
141 134
         if (!self::$formats) {
142 135
             self::$formats = self::initFormats();
143 136
         }
@@ -150,8 +143,7 @@  discard block
 block discarded – undo
150 143
      *
151 144
      * This method also allow you to override an existing placeholder.
152 145
      */
153
-    public static function setPlaceholderFormatterDefinition(string $name, callable $callable)
154
-    {
146
+    public static function setPlaceholderFormatterDefinition(string $name, callable $callable) {
155 147
         if (!self::$formatters) {
156 148
             self::$formatters = self::initPlaceholderFormatters();
157 149
         }
@@ -164,8 +156,7 @@  discard block
 block discarded – undo
164 156
      *
165 157
      * @return callable|null A PHP callable
166 158
      */
167
-    public static function getPlaceholderFormatterDefinition(string $name)
168
-    {
159
+    public static function getPlaceholderFormatterDefinition(string $name) {
169 160
         if (!self::$formatters) {
170 161
             self::$formatters = self::initPlaceholderFormatters();
171 162
         }
@@ -173,8 +164,7 @@  discard block
 block discarded – undo
173 164
         return self::$formatters[$name] ?? null;
174 165
     }
175 166
 
176
-    private function display()
177
-    {
167
+    private function display() {
178 168
         if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
179 169
             return;
180 170
         }
@@ -205,8 +195,7 @@  discard block
 block discarded – undo
205 195
     /**
206 196
      * Overwrites a previous message to the output.
207 197
      */
208
-    private function overwrite(string $message)
209
-    {
198
+    private function overwrite(string $message) {
210 199
         if ($this->output->isDecorated()) {
211 200
             $this->output->write("\x0D\x1B[2K");
212 201
             $this->output->write($message);
Please login to merge, or discard this patch.
vendor/symfony/console/Helper/DescriptorHelper.php 3 patches
Indentation   +51 added lines, -51 removed lines patch added patch discarded remove patch
@@ -26,62 +26,62 @@
 block discarded – undo
26 26
  */
27 27
 class DescriptorHelper extends Helper
28 28
 {
29
-    /**
30
-     * @var DescriptorInterface[]
31
-     */
32
-    private $descriptors = [];
29
+	/**
30
+	 * @var DescriptorInterface[]
31
+	 */
32
+	private $descriptors = [];
33 33
 
34
-    public function __construct()
35
-    {
36
-        $this
37
-            ->register('txt', new TextDescriptor())
38
-            ->register('xml', new XmlDescriptor())
39
-            ->register('json', new JsonDescriptor())
40
-            ->register('md', new MarkdownDescriptor())
41
-        ;
42
-    }
34
+	public function __construct()
35
+	{
36
+		$this
37
+			->register('txt', new TextDescriptor())
38
+			->register('xml', new XmlDescriptor())
39
+			->register('json', new JsonDescriptor())
40
+			->register('md', new MarkdownDescriptor())
41
+		;
42
+	}
43 43
 
44
-    /**
45
-     * Describes an object if supported.
46
-     *
47
-     * Available options are:
48
-     * * format: string, the output format name
49
-     * * raw_text: boolean, sets output type as raw
50
-     *
51
-     * @throws InvalidArgumentException when the given format is not supported
52
-     */
53
-    public function describe(OutputInterface $output, ?object $object, array $options = [])
54
-    {
55
-        $options = array_merge([
56
-            'raw_text' => false,
57
-            'format' => 'txt',
58
-        ], $options);
44
+	/**
45
+	 * Describes an object if supported.
46
+	 *
47
+	 * Available options are:
48
+	 * * format: string, the output format name
49
+	 * * raw_text: boolean, sets output type as raw
50
+	 *
51
+	 * @throws InvalidArgumentException when the given format is not supported
52
+	 */
53
+	public function describe(OutputInterface $output, ?object $object, array $options = [])
54
+	{
55
+		$options = array_merge([
56
+			'raw_text' => false,
57
+			'format' => 'txt',
58
+		], $options);
59 59
 
60
-        if (!isset($this->descriptors[$options['format']])) {
61
-            throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
62
-        }
60
+		if (!isset($this->descriptors[$options['format']])) {
61
+			throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
62
+		}
63 63
 
64
-        $descriptor = $this->descriptors[$options['format']];
65
-        $descriptor->describe($output, $object, $options);
66
-    }
64
+		$descriptor = $this->descriptors[$options['format']];
65
+		$descriptor->describe($output, $object, $options);
66
+	}
67 67
 
68
-    /**
69
-     * Registers a descriptor.
70
-     *
71
-     * @return $this
72
-     */
73
-    public function register(string $format, DescriptorInterface $descriptor)
74
-    {
75
-        $this->descriptors[$format] = $descriptor;
68
+	/**
69
+	 * Registers a descriptor.
70
+	 *
71
+	 * @return $this
72
+	 */
73
+	public function register(string $format, DescriptorInterface $descriptor)
74
+	{
75
+		$this->descriptors[$format] = $descriptor;
76 76
 
77
-        return $this;
78
-    }
77
+		return $this;
78
+	}
79 79
 
80
-    /**
81
-     * {@inheritdoc}
82
-     */
83
-    public function getName()
84
-    {
85
-        return 'descriptor';
86
-    }
80
+	/**
81
+	 * {@inheritdoc}
82
+	 */
83
+	public function getName()
84
+	{
85
+		return 'descriptor';
86
+	}
87 87
 }
Please login to merge, or discard this patch.
Spacing   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -29,15 +29,15 @@  discard block
 block discarded – undo
29 29
     /**
30 30
      * @var DescriptorInterface[]
31 31
      */
32
-    private $descriptors = [];
32
+    private $descriptors = [ ];
33 33
 
34 34
     public function __construct()
35 35
     {
36 36
         $this
37
-            ->register('txt', new TextDescriptor())
38
-            ->register('xml', new XmlDescriptor())
39
-            ->register('json', new JsonDescriptor())
40
-            ->register('md', new MarkdownDescriptor())
37
+            ->register( 'txt', new TextDescriptor() )
38
+            ->register( 'xml', new XmlDescriptor() )
39
+            ->register( 'json', new JsonDescriptor() )
40
+            ->register( 'md', new MarkdownDescriptor() )
41 41
         ;
42 42
     }
43 43
 
@@ -50,19 +50,19 @@  discard block
 block discarded – undo
50 50
      *
51 51
      * @throws InvalidArgumentException when the given format is not supported
52 52
      */
53
-    public function describe(OutputInterface $output, ?object $object, array $options = [])
53
+    public function describe( OutputInterface $output, ?object $object, array $options = [ ] )
54 54
     {
55
-        $options = array_merge([
55
+        $options = array_merge( [
56 56
             'raw_text' => false,
57 57
             'format' => 'txt',
58
-        ], $options);
58
+        ], $options );
59 59
 
60
-        if (!isset($this->descriptors[$options['format']])) {
61
-            throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
60
+        if ( ! isset( $this->descriptors[ $options[ 'format' ] ] ) ) {
61
+            throw new InvalidArgumentException( sprintf( 'Unsupported format "%s".', $options[ 'format' ] ) );
62 62
         }
63 63
 
64
-        $descriptor = $this->descriptors[$options['format']];
65
-        $descriptor->describe($output, $object, $options);
64
+        $descriptor = $this->descriptors[ $options[ 'format' ] ];
65
+        $descriptor->describe( $output, $object, $options );
66 66
     }
67 67
 
68 68
     /**
@@ -70,9 +70,9 @@  discard block
 block discarded – undo
70 70
      *
71 71
      * @return $this
72 72
      */
73
-    public function register(string $format, DescriptorInterface $descriptor)
73
+    public function register( string $format, DescriptorInterface $descriptor )
74 74
     {
75
-        $this->descriptors[$format] = $descriptor;
75
+        $this->descriptors[ $format ] = $descriptor;
76 76
 
77 77
         return $this;
78 78
     }
Please login to merge, or discard this patch.
Braces   +5 added lines, -10 removed lines patch added patch discarded remove patch
@@ -24,15 +24,13 @@  discard block
 block discarded – undo
24 24
  *
25 25
  * @author Jean-François Simon <[email protected]>
26 26
  */
27
-class DescriptorHelper extends Helper
28
-{
27
+class DescriptorHelper extends Helper {
29 28
     /**
30 29
      * @var DescriptorInterface[]
31 30
      */
32 31
     private $descriptors = [];
33 32
 
34
-    public function __construct()
35
-    {
33
+    public function __construct() {
36 34
         $this
37 35
             ->register('txt', new TextDescriptor())
38 36
             ->register('xml', new XmlDescriptor())
@@ -50,8 +48,7 @@  discard block
 block discarded – undo
50 48
      *
51 49
      * @throws InvalidArgumentException when the given format is not supported
52 50
      */
53
-    public function describe(OutputInterface $output, ?object $object, array $options = [])
54
-    {
51
+    public function describe(OutputInterface $output, ?object $object, array $options = []) {
55 52
         $options = array_merge([
56 53
             'raw_text' => false,
57 54
             'format' => 'txt',
@@ -70,8 +67,7 @@  discard block
 block discarded – undo
70 67
      *
71 68
      * @return $this
72 69
      */
73
-    public function register(string $format, DescriptorInterface $descriptor)
74
-    {
70
+    public function register(string $format, DescriptorInterface $descriptor) {
75 71
         $this->descriptors[$format] = $descriptor;
76 72
 
77 73
         return $this;
@@ -80,8 +76,7 @@  discard block
 block discarded – undo
80 76
     /**
81 77
      * {@inheritdoc}
82 78
      */
83
-    public function getName()
84
-    {
79
+    public function getName() {
85 80
         return 'descriptor';
86 81
     }
87 82
 }
Please login to merge, or discard this patch.
vendor/symfony/console/Helper/Dumper.php 3 patches
Indentation   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -21,44 +21,44 @@
 block discarded – undo
21 21
  */
22 22
 final class Dumper
23 23
 {
24
-    private $output;
25
-    private $dumper;
26
-    private $cloner;
27
-    private $handler;
24
+	private $output;
25
+	private $dumper;
26
+	private $cloner;
27
+	private $handler;
28 28
 
29
-    public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null)
30
-    {
31
-        $this->output = $output;
32
-        $this->dumper = $dumper;
33
-        $this->cloner = $cloner;
29
+	public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null)
30
+	{
31
+		$this->output = $output;
32
+		$this->dumper = $dumper;
33
+		$this->cloner = $cloner;
34 34
 
35
-        if (class_exists(CliDumper::class)) {
36
-            $this->handler = function ($var): string {
37
-                $dumper = $this->dumper ?? $this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
38
-                $dumper->setColors($this->output->isDecorated());
35
+		if (class_exists(CliDumper::class)) {
36
+			$this->handler = function ($var): string {
37
+				$dumper = $this->dumper ?? $this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
38
+				$dumper->setColors($this->output->isDecorated());
39 39
 
40
-                return rtrim($dumper->dump(($this->cloner ?? $this->cloner = new VarCloner())->cloneVar($var)->withRefHandles(false), true));
41
-            };
42
-        } else {
43
-            $this->handler = function ($var): string {
44
-                switch (true) {
45
-                    case null === $var:
46
-                        return 'null';
47
-                    case true === $var:
48
-                        return 'true';
49
-                    case false === $var:
50
-                        return 'false';
51
-                    case \is_string($var):
52
-                        return '"'.$var.'"';
53
-                    default:
54
-                        return rtrim(print_r($var, true));
55
-                }
56
-            };
57
-        }
58
-    }
40
+				return rtrim($dumper->dump(($this->cloner ?? $this->cloner = new VarCloner())->cloneVar($var)->withRefHandles(false), true));
41
+			};
42
+		} else {
43
+			$this->handler = function ($var): string {
44
+				switch (true) {
45
+					case null === $var:
46
+						return 'null';
47
+					case true === $var:
48
+						return 'true';
49
+					case false === $var:
50
+						return 'false';
51
+					case \is_string($var):
52
+						return '"'.$var.'"';
53
+					default:
54
+						return rtrim(print_r($var, true));
55
+				}
56
+			};
57
+		}
58
+	}
59 59
 
60
-    public function __invoke($var): string
61
-    {
62
-        return ($this->handler)($var);
63
-    }
60
+	public function __invoke($var): string
61
+	{
62
+		return ($this->handler)($var);
63
+	}
64 64
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -26,39 +26,39 @@
 block discarded – undo
26 26
     private $cloner;
27 27
     private $handler;
28 28
 
29
-    public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null)
29
+    public function __construct( OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null )
30 30
     {
31 31
         $this->output = $output;
32 32
         $this->dumper = $dumper;
33 33
         $this->cloner = $cloner;
34 34
 
35
-        if (class_exists(CliDumper::class)) {
36
-            $this->handler = function ($var): string {
37
-                $dumper = $this->dumper ?? $this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
38
-                $dumper->setColors($this->output->isDecorated());
35
+        if ( class_exists( CliDumper::class ) ) {
36
+            $this->handler = function( $var ): string {
37
+                $dumper = $this->dumper ?? $this->dumper = new CliDumper( null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR );
38
+                $dumper->setColors( $this->output->isDecorated() );
39 39
 
40
-                return rtrim($dumper->dump(($this->cloner ?? $this->cloner = new VarCloner())->cloneVar($var)->withRefHandles(false), true));
40
+                return rtrim( $dumper->dump( ( $this->cloner ?? $this->cloner = new VarCloner() )->cloneVar( $var )->withRefHandles( false ), true ) );
41 41
             };
42 42
         } else {
43
-            $this->handler = function ($var): string {
44
-                switch (true) {
43
+            $this->handler = function( $var ): string {
44
+                switch ( true ) {
45 45
                     case null === $var:
46 46
                         return 'null';
47 47
                     case true === $var:
48 48
                         return 'true';
49 49
                     case false === $var:
50 50
                         return 'false';
51
-                    case \is_string($var):
52
-                        return '"'.$var.'"';
51
+                    case \is_string( $var ):
52
+                        return '"' . $var . '"';
53 53
                     default:
54
-                        return rtrim(print_r($var, true));
54
+                        return rtrim( print_r( $var, true ) );
55 55
                 }
56 56
             };
57 57
         }
58 58
     }
59 59
 
60
-    public function __invoke($var): string
60
+    public function __invoke( $var ): string
61 61
     {
62
-        return ($this->handler)($var);
62
+        return ( $this->handler )( $var );
63 63
     }
64 64
 }
Please login to merge, or discard this patch.
Braces   +2 added lines, -4 removed lines patch added patch discarded remove patch
@@ -19,15 +19,13 @@
 block discarded – undo
19 19
 /**
20 20
  * @author Roland Franssen <[email protected]>
21 21
  */
22
-final class Dumper
23
-{
22
+final class Dumper {
24 23
     private $output;
25 24
     private $dumper;
26 25
     private $cloner;
27 26
     private $handler;
28 27
 
29
-    public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null)
30
-    {
28
+    public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null) {
31 29
         $this->output = $output;
32 30
         $this->dumper = $dumper;
33 31
         $this->cloner = $cloner;
Please login to merge, or discard this patch.
vendor/symfony/console/Helper/QuestionHelper.php 3 patches
Indentation   +569 added lines, -569 removed lines patch added patch discarded remove patch
@@ -33,573 +33,573 @@
 block discarded – undo
33 33
  */
34 34
 class QuestionHelper extends Helper
35 35
 {
36
-    private $inputStream;
37
-    private static $shell;
38
-    private static $stty = true;
39
-    private static $stdinIsInteractive;
40
-
41
-    /**
42
-     * Asks a question to the user.
43
-     *
44
-     * @return mixed The user answer
45
-     *
46
-     * @throws RuntimeException If there is no data to read in the input stream
47
-     */
48
-    public function ask(InputInterface $input, OutputInterface $output, Question $question)
49
-    {
50
-        if ($output instanceof ConsoleOutputInterface) {
51
-            $output = $output->getErrorOutput();
52
-        }
53
-
54
-        if (!$input->isInteractive()) {
55
-            return $this->getDefaultAnswer($question);
56
-        }
57
-
58
-        if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) {
59
-            $this->inputStream = $stream;
60
-        }
61
-
62
-        try {
63
-            if (!$question->getValidator()) {
64
-                return $this->doAsk($output, $question);
65
-            }
66
-
67
-            $interviewer = function () use ($output, $question) {
68
-                return $this->doAsk($output, $question);
69
-            };
70
-
71
-            return $this->validateAttempts($interviewer, $output, $question);
72
-        } catch (MissingInputException $exception) {
73
-            $input->setInteractive(false);
74
-
75
-            if (null === $fallbackOutput = $this->getDefaultAnswer($question)) {
76
-                throw $exception;
77
-            }
78
-
79
-            return $fallbackOutput;
80
-        }
81
-    }
82
-
83
-    /**
84
-     * {@inheritdoc}
85
-     */
86
-    public function getName()
87
-    {
88
-        return 'question';
89
-    }
90
-
91
-    /**
92
-     * Prevents usage of stty.
93
-     */
94
-    public static function disableStty()
95
-    {
96
-        self::$stty = false;
97
-    }
98
-
99
-    /**
100
-     * Asks the question to the user.
101
-     *
102
-     * @return mixed
103
-     *
104
-     * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
105
-     */
106
-    private function doAsk(OutputInterface $output, Question $question)
107
-    {
108
-        $this->writePrompt($output, $question);
109
-
110
-        $inputStream = $this->inputStream ?: \STDIN;
111
-        $autocomplete = $question->getAutocompleterCallback();
112
-
113
-        if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) {
114
-            $ret = false;
115
-            if ($question->isHidden()) {
116
-                try {
117
-                    $hiddenResponse = $this->getHiddenResponse($output, $inputStream, $question->isTrimmable());
118
-                    $ret = $question->isTrimmable() ? trim($hiddenResponse) : $hiddenResponse;
119
-                } catch (RuntimeException $e) {
120
-                    if (!$question->isHiddenFallback()) {
121
-                        throw $e;
122
-                    }
123
-                }
124
-            }
125
-
126
-            if (false === $ret) {
127
-                $ret = $this->readInput($inputStream, $question);
128
-                if (false === $ret) {
129
-                    throw new MissingInputException('Aborted.');
130
-                }
131
-                if ($question->isTrimmable()) {
132
-                    $ret = trim($ret);
133
-                }
134
-            }
135
-        } else {
136
-            $autocomplete = $this->autocomplete($output, $question, $inputStream, $autocomplete);
137
-            $ret = $question->isTrimmable() ? trim($autocomplete) : $autocomplete;
138
-        }
139
-
140
-        if ($output instanceof ConsoleSectionOutput) {
141
-            $output->addContent($ret);
142
-        }
143
-
144
-        $ret = \strlen($ret) > 0 ? $ret : $question->getDefault();
145
-
146
-        if ($normalizer = $question->getNormalizer()) {
147
-            return $normalizer($ret);
148
-        }
149
-
150
-        return $ret;
151
-    }
152
-
153
-    /**
154
-     * @return mixed
155
-     */
156
-    private function getDefaultAnswer(Question $question)
157
-    {
158
-        $default = $question->getDefault();
159
-
160
-        if (null === $default) {
161
-            return $default;
162
-        }
163
-
164
-        if ($validator = $question->getValidator()) {
165
-            return \call_user_func($question->getValidator(), $default);
166
-        } elseif ($question instanceof ChoiceQuestion) {
167
-            $choices = $question->getChoices();
168
-
169
-            if (!$question->isMultiselect()) {
170
-                return $choices[$default] ?? $default;
171
-            }
172
-
173
-            $default = explode(',', $default);
174
-            foreach ($default as $k => $v) {
175
-                $v = $question->isTrimmable() ? trim($v) : $v;
176
-                $default[$k] = $choices[$v] ?? $v;
177
-            }
178
-        }
179
-
180
-        return $default;
181
-    }
182
-
183
-    /**
184
-     * Outputs the question prompt.
185
-     */
186
-    protected function writePrompt(OutputInterface $output, Question $question)
187
-    {
188
-        $message = $question->getQuestion();
189
-
190
-        if ($question instanceof ChoiceQuestion) {
191
-            $output->writeln(array_merge([
192
-                $question->getQuestion(),
193
-            ], $this->formatChoiceQuestionChoices($question, 'info')));
194
-
195
-            $message = $question->getPrompt();
196
-        }
197
-
198
-        $output->write($message);
199
-    }
200
-
201
-    /**
202
-     * @return string[]
203
-     */
204
-    protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag)
205
-    {
206
-        $messages = [];
207
-
208
-        $maxWidth = max(array_map('self::width', array_keys($choices = $question->getChoices())));
209
-
210
-        foreach ($choices as $key => $value) {
211
-            $padding = str_repeat(' ', $maxWidth - self::width($key));
212
-
213
-            $messages[] = sprintf("  [<$tag>%s$padding</$tag>] %s", $key, $value);
214
-        }
215
-
216
-        return $messages;
217
-    }
218
-
219
-    /**
220
-     * Outputs an error message.
221
-     */
222
-    protected function writeError(OutputInterface $output, \Exception $error)
223
-    {
224
-        if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
225
-            $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
226
-        } else {
227
-            $message = '<error>'.$error->getMessage().'</error>';
228
-        }
229
-
230
-        $output->writeln($message);
231
-    }
232
-
233
-    /**
234
-     * Autocompletes a question.
235
-     *
236
-     * @param resource $inputStream
237
-     */
238
-    private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string
239
-    {
240
-        $cursor = new Cursor($output, $inputStream);
241
-
242
-        $fullChoice = '';
243
-        $ret = '';
244
-
245
-        $i = 0;
246
-        $ofs = -1;
247
-        $matches = $autocomplete($ret);
248
-        $numMatches = \count($matches);
249
-
250
-        $sttyMode = shell_exec('stty -g');
251
-
252
-        // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
253
-        shell_exec('stty -icanon -echo');
254
-
255
-        // Add highlighted text style
256
-        $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
257
-
258
-        // Read a keypress
259
-        while (!feof($inputStream)) {
260
-            $c = fread($inputStream, 1);
261
-
262
-            // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false.
263
-            if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
264
-                shell_exec(sprintf('stty %s', $sttyMode));
265
-                throw new MissingInputException('Aborted.');
266
-            } elseif ("\177" === $c) { // Backspace Character
267
-                if (0 === $numMatches && 0 !== $i) {
268
-                    --$i;
269
-                    $cursor->moveLeft(s($fullChoice)->slice(-1)->width(false));
270
-
271
-                    $fullChoice = self::substr($fullChoice, 0, $i);
272
-                }
273
-
274
-                if (0 === $i) {
275
-                    $ofs = -1;
276
-                    $matches = $autocomplete($ret);
277
-                    $numMatches = \count($matches);
278
-                } else {
279
-                    $numMatches = 0;
280
-                }
281
-
282
-                // Pop the last character off the end of our string
283
-                $ret = self::substr($ret, 0, $i);
284
-            } elseif ("\033" === $c) {
285
-                // Did we read an escape sequence?
286
-                $c .= fread($inputStream, 2);
287
-
288
-                // A = Up Arrow. B = Down Arrow
289
-                if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
290
-                    if ('A' === $c[2] && -1 === $ofs) {
291
-                        $ofs = 0;
292
-                    }
293
-
294
-                    if (0 === $numMatches) {
295
-                        continue;
296
-                    }
297
-
298
-                    $ofs += ('A' === $c[2]) ? -1 : 1;
299
-                    $ofs = ($numMatches + $ofs) % $numMatches;
300
-                }
301
-            } elseif (\ord($c) < 32) {
302
-                if ("\t" === $c || "\n" === $c) {
303
-                    if ($numMatches > 0 && -1 !== $ofs) {
304
-                        $ret = (string) $matches[$ofs];
305
-                        // Echo out remaining chars for current match
306
-                        $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
307
-                        $output->write($remainingCharacters);
308
-                        $fullChoice .= $remainingCharacters;
309
-                        $i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding);
310
-
311
-                        $matches = array_filter(
312
-                            $autocomplete($ret),
313
-                            function ($match) use ($ret) {
314
-                                return '' === $ret || str_starts_with($match, $ret);
315
-                            }
316
-                        );
317
-                        $numMatches = \count($matches);
318
-                        $ofs = -1;
319
-                    }
320
-
321
-                    if ("\n" === $c) {
322
-                        $output->write($c);
323
-                        break;
324
-                    }
325
-
326
-                    $numMatches = 0;
327
-                }
328
-
329
-                continue;
330
-            } else {
331
-                if ("\x80" <= $c) {
332
-                    $c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]);
333
-                }
334
-
335
-                $output->write($c);
336
-                $ret .= $c;
337
-                $fullChoice .= $c;
338
-                ++$i;
339
-
340
-                $tempRet = $ret;
341
-
342
-                if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
343
-                    $tempRet = $this->mostRecentlyEnteredValue($fullChoice);
344
-                }
345
-
346
-                $numMatches = 0;
347
-                $ofs = 0;
348
-
349
-                foreach ($autocomplete($ret) as $value) {
350
-                    // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
351
-                    if (str_starts_with($value, $tempRet)) {
352
-                        $matches[$numMatches++] = $value;
353
-                    }
354
-                }
355
-            }
356
-
357
-            $cursor->clearLineAfter();
358
-
359
-            if ($numMatches > 0 && -1 !== $ofs) {
360
-                $cursor->savePosition();
361
-                // Write highlighted text, complete the partially entered response
362
-                $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
363
-                $output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
364
-                $cursor->restorePosition();
365
-            }
366
-        }
367
-
368
-        // Reset stty so it behaves normally again
369
-        shell_exec(sprintf('stty %s', $sttyMode));
370
-
371
-        return $fullChoice;
372
-    }
373
-
374
-    private function mostRecentlyEnteredValue(string $entered): string
375
-    {
376
-        // Determine the most recent value that the user entered
377
-        if (!str_contains($entered, ',')) {
378
-            return $entered;
379
-        }
380
-
381
-        $choices = explode(',', $entered);
382
-        if ('' !== $lastChoice = trim($choices[\count($choices) - 1])) {
383
-            return $lastChoice;
384
-        }
385
-
386
-        return $entered;
387
-    }
388
-
389
-    /**
390
-     * Gets a hidden response from user.
391
-     *
392
-     * @param resource $inputStream The handler resource
393
-     * @param bool     $trimmable   Is the answer trimmable
394
-     *
395
-     * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
396
-     */
397
-    private function getHiddenResponse(OutputInterface $output, $inputStream, bool $trimmable = true): string
398
-    {
399
-        if ('\\' === \DIRECTORY_SEPARATOR) {
400
-            $exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
401
-
402
-            // handle code running from a phar
403
-            if ('phar:' === substr(__FILE__, 0, 5)) {
404
-                $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
405
-                copy($exe, $tmpExe);
406
-                $exe = $tmpExe;
407
-            }
408
-
409
-            $sExec = shell_exec('"'.$exe.'"');
410
-            $value = $trimmable ? rtrim($sExec) : $sExec;
411
-            $output->writeln('');
412
-
413
-            if (isset($tmpExe)) {
414
-                unlink($tmpExe);
415
-            }
416
-
417
-            return $value;
418
-        }
419
-
420
-        if (self::$stty && Terminal::hasSttyAvailable()) {
421
-            $sttyMode = shell_exec('stty -g');
422
-            shell_exec('stty -echo');
423
-        } elseif ($this->isInteractiveInput($inputStream)) {
424
-            throw new RuntimeException('Unable to hide the response.');
425
-        }
426
-
427
-        $value = fgets($inputStream, 4096);
428
-
429
-        if (self::$stty && Terminal::hasSttyAvailable()) {
430
-            shell_exec(sprintf('stty %s', $sttyMode));
431
-        }
432
-
433
-        if (false === $value) {
434
-            throw new MissingInputException('Aborted.');
435
-        }
436
-        if ($trimmable) {
437
-            $value = trim($value);
438
-        }
439
-        $output->writeln('');
440
-
441
-        return $value;
442
-    }
443
-
444
-    /**
445
-     * Validates an attempt.
446
-     *
447
-     * @param callable $interviewer A callable that will ask for a question and return the result
448
-     *
449
-     * @return mixed The validated response
450
-     *
451
-     * @throws \Exception In case the max number of attempts has been reached and no valid response has been given
452
-     */
453
-    private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question)
454
-    {
455
-        $error = null;
456
-        $attempts = $question->getMaxAttempts();
457
-
458
-        while (null === $attempts || $attempts--) {
459
-            if (null !== $error) {
460
-                $this->writeError($output, $error);
461
-            }
462
-
463
-            try {
464
-                return $question->getValidator()($interviewer());
465
-            } catch (RuntimeException $e) {
466
-                throw $e;
467
-            } catch (\Exception $error) {
468
-            }
469
-        }
470
-
471
-        throw $error;
472
-    }
473
-
474
-    private function isInteractiveInput($inputStream): bool
475
-    {
476
-        if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) {
477
-            return false;
478
-        }
479
-
480
-        if (null !== self::$stdinIsInteractive) {
481
-            return self::$stdinIsInteractive;
482
-        }
483
-
484
-        if (\function_exists('stream_isatty')) {
485
-            return self::$stdinIsInteractive = stream_isatty(fopen('php://stdin', 'r'));
486
-        }
487
-
488
-        if (\function_exists('posix_isatty')) {
489
-            return self::$stdinIsInteractive = posix_isatty(fopen('php://stdin', 'r'));
490
-        }
491
-
492
-        if (!\function_exists('exec')) {
493
-            return self::$stdinIsInteractive = true;
494
-        }
495
-
496
-        exec('stty 2> /dev/null', $output, $status);
497
-
498
-        return self::$stdinIsInteractive = 1 !== $status;
499
-    }
500
-
501
-    /**
502
-     * Reads one or more lines of input and returns what is read.
503
-     *
504
-     * @param resource $inputStream The handler resource
505
-     * @param Question $question    The question being asked
506
-     *
507
-     * @return string|false The input received, false in case input could not be read
508
-     */
509
-    private function readInput($inputStream, Question $question)
510
-    {
511
-        if (!$question->isMultiline()) {
512
-            $cp = $this->setIOCodepage();
513
-            $ret = fgets($inputStream, 4096);
514
-
515
-            return $this->resetIOCodepage($cp, $ret);
516
-        }
517
-
518
-        $multiLineStreamReader = $this->cloneInputStream($inputStream);
519
-        if (null === $multiLineStreamReader) {
520
-            return false;
521
-        }
522
-
523
-        $ret = '';
524
-        $cp = $this->setIOCodepage();
525
-        while (false !== ($char = fgetc($multiLineStreamReader))) {
526
-            if (\PHP_EOL === "{$ret}{$char}") {
527
-                break;
528
-            }
529
-            $ret .= $char;
530
-        }
531
-
532
-        return $this->resetIOCodepage($cp, $ret);
533
-    }
534
-
535
-    /**
536
-     * Sets console I/O to the host code page.
537
-     *
538
-     * @return int Previous code page in IBM/EBCDIC format
539
-     */
540
-    private function setIOCodepage(): int
541
-    {
542
-        if (\function_exists('sapi_windows_cp_set')) {
543
-            $cp = sapi_windows_cp_get();
544
-            sapi_windows_cp_set(sapi_windows_cp_get('oem'));
545
-
546
-            return $cp;
547
-        }
548
-
549
-        return 0;
550
-    }
551
-
552
-    /**
553
-     * Sets console I/O to the specified code page and converts the user input.
554
-     *
555
-     * @param string|false $input
556
-     *
557
-     * @return string|false
558
-     */
559
-    private function resetIOCodepage(int $cp, $input)
560
-    {
561
-        if (0 !== $cp) {
562
-            sapi_windows_cp_set($cp);
563
-
564
-            if (false !== $input && '' !== $input) {
565
-                $input = sapi_windows_cp_conv(sapi_windows_cp_get('oem'), $cp, $input);
566
-            }
567
-        }
568
-
569
-        return $input;
570
-    }
571
-
572
-    /**
573
-     * Clones an input stream in order to act on one instance of the same
574
-     * stream without affecting the other instance.
575
-     *
576
-     * @param resource $inputStream The handler resource
577
-     *
578
-     * @return resource|null The cloned resource, null in case it could not be cloned
579
-     */
580
-    private function cloneInputStream($inputStream)
581
-    {
582
-        $streamMetaData = stream_get_meta_data($inputStream);
583
-        $seekable = $streamMetaData['seekable'] ?? false;
584
-        $mode = $streamMetaData['mode'] ?? 'rb';
585
-        $uri = $streamMetaData['uri'] ?? null;
586
-
587
-        if (null === $uri) {
588
-            return null;
589
-        }
590
-
591
-        $cloneStream = fopen($uri, $mode);
592
-
593
-        // For seekable and writable streams, add all the same data to the
594
-        // cloned stream and then seek to the same offset.
595
-        if (true === $seekable && !\in_array($mode, ['r', 'rb', 'rt'])) {
596
-            $offset = ftell($inputStream);
597
-            rewind($inputStream);
598
-            stream_copy_to_stream($inputStream, $cloneStream);
599
-            fseek($inputStream, $offset);
600
-            fseek($cloneStream, $offset);
601
-        }
602
-
603
-        return $cloneStream;
604
-    }
36
+	private $inputStream;
37
+	private static $shell;
38
+	private static $stty = true;
39
+	private static $stdinIsInteractive;
40
+
41
+	/**
42
+	 * Asks a question to the user.
43
+	 *
44
+	 * @return mixed The user answer
45
+	 *
46
+	 * @throws RuntimeException If there is no data to read in the input stream
47
+	 */
48
+	public function ask(InputInterface $input, OutputInterface $output, Question $question)
49
+	{
50
+		if ($output instanceof ConsoleOutputInterface) {
51
+			$output = $output->getErrorOutput();
52
+		}
53
+
54
+		if (!$input->isInteractive()) {
55
+			return $this->getDefaultAnswer($question);
56
+		}
57
+
58
+		if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) {
59
+			$this->inputStream = $stream;
60
+		}
61
+
62
+		try {
63
+			if (!$question->getValidator()) {
64
+				return $this->doAsk($output, $question);
65
+			}
66
+
67
+			$interviewer = function () use ($output, $question) {
68
+				return $this->doAsk($output, $question);
69
+			};
70
+
71
+			return $this->validateAttempts($interviewer, $output, $question);
72
+		} catch (MissingInputException $exception) {
73
+			$input->setInteractive(false);
74
+
75
+			if (null === $fallbackOutput = $this->getDefaultAnswer($question)) {
76
+				throw $exception;
77
+			}
78
+
79
+			return $fallbackOutput;
80
+		}
81
+	}
82
+
83
+	/**
84
+	 * {@inheritdoc}
85
+	 */
86
+	public function getName()
87
+	{
88
+		return 'question';
89
+	}
90
+
91
+	/**
92
+	 * Prevents usage of stty.
93
+	 */
94
+	public static function disableStty()
95
+	{
96
+		self::$stty = false;
97
+	}
98
+
99
+	/**
100
+	 * Asks the question to the user.
101
+	 *
102
+	 * @return mixed
103
+	 *
104
+	 * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
105
+	 */
106
+	private function doAsk(OutputInterface $output, Question $question)
107
+	{
108
+		$this->writePrompt($output, $question);
109
+
110
+		$inputStream = $this->inputStream ?: \STDIN;
111
+		$autocomplete = $question->getAutocompleterCallback();
112
+
113
+		if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) {
114
+			$ret = false;
115
+			if ($question->isHidden()) {
116
+				try {
117
+					$hiddenResponse = $this->getHiddenResponse($output, $inputStream, $question->isTrimmable());
118
+					$ret = $question->isTrimmable() ? trim($hiddenResponse) : $hiddenResponse;
119
+				} catch (RuntimeException $e) {
120
+					if (!$question->isHiddenFallback()) {
121
+						throw $e;
122
+					}
123
+				}
124
+			}
125
+
126
+			if (false === $ret) {
127
+				$ret = $this->readInput($inputStream, $question);
128
+				if (false === $ret) {
129
+					throw new MissingInputException('Aborted.');
130
+				}
131
+				if ($question->isTrimmable()) {
132
+					$ret = trim($ret);
133
+				}
134
+			}
135
+		} else {
136
+			$autocomplete = $this->autocomplete($output, $question, $inputStream, $autocomplete);
137
+			$ret = $question->isTrimmable() ? trim($autocomplete) : $autocomplete;
138
+		}
139
+
140
+		if ($output instanceof ConsoleSectionOutput) {
141
+			$output->addContent($ret);
142
+		}
143
+
144
+		$ret = \strlen($ret) > 0 ? $ret : $question->getDefault();
145
+
146
+		if ($normalizer = $question->getNormalizer()) {
147
+			return $normalizer($ret);
148
+		}
149
+
150
+		return $ret;
151
+	}
152
+
153
+	/**
154
+	 * @return mixed
155
+	 */
156
+	private function getDefaultAnswer(Question $question)
157
+	{
158
+		$default = $question->getDefault();
159
+
160
+		if (null === $default) {
161
+			return $default;
162
+		}
163
+
164
+		if ($validator = $question->getValidator()) {
165
+			return \call_user_func($question->getValidator(), $default);
166
+		} elseif ($question instanceof ChoiceQuestion) {
167
+			$choices = $question->getChoices();
168
+
169
+			if (!$question->isMultiselect()) {
170
+				return $choices[$default] ?? $default;
171
+			}
172
+
173
+			$default = explode(',', $default);
174
+			foreach ($default as $k => $v) {
175
+				$v = $question->isTrimmable() ? trim($v) : $v;
176
+				$default[$k] = $choices[$v] ?? $v;
177
+			}
178
+		}
179
+
180
+		return $default;
181
+	}
182
+
183
+	/**
184
+	 * Outputs the question prompt.
185
+	 */
186
+	protected function writePrompt(OutputInterface $output, Question $question)
187
+	{
188
+		$message = $question->getQuestion();
189
+
190
+		if ($question instanceof ChoiceQuestion) {
191
+			$output->writeln(array_merge([
192
+				$question->getQuestion(),
193
+			], $this->formatChoiceQuestionChoices($question, 'info')));
194
+
195
+			$message = $question->getPrompt();
196
+		}
197
+
198
+		$output->write($message);
199
+	}
200
+
201
+	/**
202
+	 * @return string[]
203
+	 */
204
+	protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag)
205
+	{
206
+		$messages = [];
207
+
208
+		$maxWidth = max(array_map('self::width', array_keys($choices = $question->getChoices())));
209
+
210
+		foreach ($choices as $key => $value) {
211
+			$padding = str_repeat(' ', $maxWidth - self::width($key));
212
+
213
+			$messages[] = sprintf("  [<$tag>%s$padding</$tag>] %s", $key, $value);
214
+		}
215
+
216
+		return $messages;
217
+	}
218
+
219
+	/**
220
+	 * Outputs an error message.
221
+	 */
222
+	protected function writeError(OutputInterface $output, \Exception $error)
223
+	{
224
+		if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
225
+			$message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
226
+		} else {
227
+			$message = '<error>'.$error->getMessage().'</error>';
228
+		}
229
+
230
+		$output->writeln($message);
231
+	}
232
+
233
+	/**
234
+	 * Autocompletes a question.
235
+	 *
236
+	 * @param resource $inputStream
237
+	 */
238
+	private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string
239
+	{
240
+		$cursor = new Cursor($output, $inputStream);
241
+
242
+		$fullChoice = '';
243
+		$ret = '';
244
+
245
+		$i = 0;
246
+		$ofs = -1;
247
+		$matches = $autocomplete($ret);
248
+		$numMatches = \count($matches);
249
+
250
+		$sttyMode = shell_exec('stty -g');
251
+
252
+		// Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
253
+		shell_exec('stty -icanon -echo');
254
+
255
+		// Add highlighted text style
256
+		$output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
257
+
258
+		// Read a keypress
259
+		while (!feof($inputStream)) {
260
+			$c = fread($inputStream, 1);
261
+
262
+			// as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false.
263
+			if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
264
+				shell_exec(sprintf('stty %s', $sttyMode));
265
+				throw new MissingInputException('Aborted.');
266
+			} elseif ("\177" === $c) { // Backspace Character
267
+				if (0 === $numMatches && 0 !== $i) {
268
+					--$i;
269
+					$cursor->moveLeft(s($fullChoice)->slice(-1)->width(false));
270
+
271
+					$fullChoice = self::substr($fullChoice, 0, $i);
272
+				}
273
+
274
+				if (0 === $i) {
275
+					$ofs = -1;
276
+					$matches = $autocomplete($ret);
277
+					$numMatches = \count($matches);
278
+				} else {
279
+					$numMatches = 0;
280
+				}
281
+
282
+				// Pop the last character off the end of our string
283
+				$ret = self::substr($ret, 0, $i);
284
+			} elseif ("\033" === $c) {
285
+				// Did we read an escape sequence?
286
+				$c .= fread($inputStream, 2);
287
+
288
+				// A = Up Arrow. B = Down Arrow
289
+				if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
290
+					if ('A' === $c[2] && -1 === $ofs) {
291
+						$ofs = 0;
292
+					}
293
+
294
+					if (0 === $numMatches) {
295
+						continue;
296
+					}
297
+
298
+					$ofs += ('A' === $c[2]) ? -1 : 1;
299
+					$ofs = ($numMatches + $ofs) % $numMatches;
300
+				}
301
+			} elseif (\ord($c) < 32) {
302
+				if ("\t" === $c || "\n" === $c) {
303
+					if ($numMatches > 0 && -1 !== $ofs) {
304
+						$ret = (string) $matches[$ofs];
305
+						// Echo out remaining chars for current match
306
+						$remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
307
+						$output->write($remainingCharacters);
308
+						$fullChoice .= $remainingCharacters;
309
+						$i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding);
310
+
311
+						$matches = array_filter(
312
+							$autocomplete($ret),
313
+							function ($match) use ($ret) {
314
+								return '' === $ret || str_starts_with($match, $ret);
315
+							}
316
+						);
317
+						$numMatches = \count($matches);
318
+						$ofs = -1;
319
+					}
320
+
321
+					if ("\n" === $c) {
322
+						$output->write($c);
323
+						break;
324
+					}
325
+
326
+					$numMatches = 0;
327
+				}
328
+
329
+				continue;
330
+			} else {
331
+				if ("\x80" <= $c) {
332
+					$c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]);
333
+				}
334
+
335
+				$output->write($c);
336
+				$ret .= $c;
337
+				$fullChoice .= $c;
338
+				++$i;
339
+
340
+				$tempRet = $ret;
341
+
342
+				if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
343
+					$tempRet = $this->mostRecentlyEnteredValue($fullChoice);
344
+				}
345
+
346
+				$numMatches = 0;
347
+				$ofs = 0;
348
+
349
+				foreach ($autocomplete($ret) as $value) {
350
+					// If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
351
+					if (str_starts_with($value, $tempRet)) {
352
+						$matches[$numMatches++] = $value;
353
+					}
354
+				}
355
+			}
356
+
357
+			$cursor->clearLineAfter();
358
+
359
+			if ($numMatches > 0 && -1 !== $ofs) {
360
+				$cursor->savePosition();
361
+				// Write highlighted text, complete the partially entered response
362
+				$charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
363
+				$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
364
+				$cursor->restorePosition();
365
+			}
366
+		}
367
+
368
+		// Reset stty so it behaves normally again
369
+		shell_exec(sprintf('stty %s', $sttyMode));
370
+
371
+		return $fullChoice;
372
+	}
373
+
374
+	private function mostRecentlyEnteredValue(string $entered): string
375
+	{
376
+		// Determine the most recent value that the user entered
377
+		if (!str_contains($entered, ',')) {
378
+			return $entered;
379
+		}
380
+
381
+		$choices = explode(',', $entered);
382
+		if ('' !== $lastChoice = trim($choices[\count($choices) - 1])) {
383
+			return $lastChoice;
384
+		}
385
+
386
+		return $entered;
387
+	}
388
+
389
+	/**
390
+	 * Gets a hidden response from user.
391
+	 *
392
+	 * @param resource $inputStream The handler resource
393
+	 * @param bool     $trimmable   Is the answer trimmable
394
+	 *
395
+	 * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
396
+	 */
397
+	private function getHiddenResponse(OutputInterface $output, $inputStream, bool $trimmable = true): string
398
+	{
399
+		if ('\\' === \DIRECTORY_SEPARATOR) {
400
+			$exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
401
+
402
+			// handle code running from a phar
403
+			if ('phar:' === substr(__FILE__, 0, 5)) {
404
+				$tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
405
+				copy($exe, $tmpExe);
406
+				$exe = $tmpExe;
407
+			}
408
+
409
+			$sExec = shell_exec('"'.$exe.'"');
410
+			$value = $trimmable ? rtrim($sExec) : $sExec;
411
+			$output->writeln('');
412
+
413
+			if (isset($tmpExe)) {
414
+				unlink($tmpExe);
415
+			}
416
+
417
+			return $value;
418
+		}
419
+
420
+		if (self::$stty && Terminal::hasSttyAvailable()) {
421
+			$sttyMode = shell_exec('stty -g');
422
+			shell_exec('stty -echo');
423
+		} elseif ($this->isInteractiveInput($inputStream)) {
424
+			throw new RuntimeException('Unable to hide the response.');
425
+		}
426
+
427
+		$value = fgets($inputStream, 4096);
428
+
429
+		if (self::$stty && Terminal::hasSttyAvailable()) {
430
+			shell_exec(sprintf('stty %s', $sttyMode));
431
+		}
432
+
433
+		if (false === $value) {
434
+			throw new MissingInputException('Aborted.');
435
+		}
436
+		if ($trimmable) {
437
+			$value = trim($value);
438
+		}
439
+		$output->writeln('');
440
+
441
+		return $value;
442
+	}
443
+
444
+	/**
445
+	 * Validates an attempt.
446
+	 *
447
+	 * @param callable $interviewer A callable that will ask for a question and return the result
448
+	 *
449
+	 * @return mixed The validated response
450
+	 *
451
+	 * @throws \Exception In case the max number of attempts has been reached and no valid response has been given
452
+	 */
453
+	private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question)
454
+	{
455
+		$error = null;
456
+		$attempts = $question->getMaxAttempts();
457
+
458
+		while (null === $attempts || $attempts--) {
459
+			if (null !== $error) {
460
+				$this->writeError($output, $error);
461
+			}
462
+
463
+			try {
464
+				return $question->getValidator()($interviewer());
465
+			} catch (RuntimeException $e) {
466
+				throw $e;
467
+			} catch (\Exception $error) {
468
+			}
469
+		}
470
+
471
+		throw $error;
472
+	}
473
+
474
+	private function isInteractiveInput($inputStream): bool
475
+	{
476
+		if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) {
477
+			return false;
478
+		}
479
+
480
+		if (null !== self::$stdinIsInteractive) {
481
+			return self::$stdinIsInteractive;
482
+		}
483
+
484
+		if (\function_exists('stream_isatty')) {
485
+			return self::$stdinIsInteractive = stream_isatty(fopen('php://stdin', 'r'));
486
+		}
487
+
488
+		if (\function_exists('posix_isatty')) {
489
+			return self::$stdinIsInteractive = posix_isatty(fopen('php://stdin', 'r'));
490
+		}
491
+
492
+		if (!\function_exists('exec')) {
493
+			return self::$stdinIsInteractive = true;
494
+		}
495
+
496
+		exec('stty 2> /dev/null', $output, $status);
497
+
498
+		return self::$stdinIsInteractive = 1 !== $status;
499
+	}
500
+
501
+	/**
502
+	 * Reads one or more lines of input and returns what is read.
503
+	 *
504
+	 * @param resource $inputStream The handler resource
505
+	 * @param Question $question    The question being asked
506
+	 *
507
+	 * @return string|false The input received, false in case input could not be read
508
+	 */
509
+	private function readInput($inputStream, Question $question)
510
+	{
511
+		if (!$question->isMultiline()) {
512
+			$cp = $this->setIOCodepage();
513
+			$ret = fgets($inputStream, 4096);
514
+
515
+			return $this->resetIOCodepage($cp, $ret);
516
+		}
517
+
518
+		$multiLineStreamReader = $this->cloneInputStream($inputStream);
519
+		if (null === $multiLineStreamReader) {
520
+			return false;
521
+		}
522
+
523
+		$ret = '';
524
+		$cp = $this->setIOCodepage();
525
+		while (false !== ($char = fgetc($multiLineStreamReader))) {
526
+			if (\PHP_EOL === "{$ret}{$char}") {
527
+				break;
528
+			}
529
+			$ret .= $char;
530
+		}
531
+
532
+		return $this->resetIOCodepage($cp, $ret);
533
+	}
534
+
535
+	/**
536
+	 * Sets console I/O to the host code page.
537
+	 *
538
+	 * @return int Previous code page in IBM/EBCDIC format
539
+	 */
540
+	private function setIOCodepage(): int
541
+	{
542
+		if (\function_exists('sapi_windows_cp_set')) {
543
+			$cp = sapi_windows_cp_get();
544
+			sapi_windows_cp_set(sapi_windows_cp_get('oem'));
545
+
546
+			return $cp;
547
+		}
548
+
549
+		return 0;
550
+	}
551
+
552
+	/**
553
+	 * Sets console I/O to the specified code page and converts the user input.
554
+	 *
555
+	 * @param string|false $input
556
+	 *
557
+	 * @return string|false
558
+	 */
559
+	private function resetIOCodepage(int $cp, $input)
560
+	{
561
+		if (0 !== $cp) {
562
+			sapi_windows_cp_set($cp);
563
+
564
+			if (false !== $input && '' !== $input) {
565
+				$input = sapi_windows_cp_conv(sapi_windows_cp_get('oem'), $cp, $input);
566
+			}
567
+		}
568
+
569
+		return $input;
570
+	}
571
+
572
+	/**
573
+	 * Clones an input stream in order to act on one instance of the same
574
+	 * stream without affecting the other instance.
575
+	 *
576
+	 * @param resource $inputStream The handler resource
577
+	 *
578
+	 * @return resource|null The cloned resource, null in case it could not be cloned
579
+	 */
580
+	private function cloneInputStream($inputStream)
581
+	{
582
+		$streamMetaData = stream_get_meta_data($inputStream);
583
+		$seekable = $streamMetaData['seekable'] ?? false;
584
+		$mode = $streamMetaData['mode'] ?? 'rb';
585
+		$uri = $streamMetaData['uri'] ?? null;
586
+
587
+		if (null === $uri) {
588
+			return null;
589
+		}
590
+
591
+		$cloneStream = fopen($uri, $mode);
592
+
593
+		// For seekable and writable streams, add all the same data to the
594
+		// cloned stream and then seek to the same offset.
595
+		if (true === $seekable && !\in_array($mode, ['r', 'rb', 'rt'])) {
596
+			$offset = ftell($inputStream);
597
+			rewind($inputStream);
598
+			stream_copy_to_stream($inputStream, $cloneStream);
599
+			fseek($inputStream, $offset);
600
+			fseek($cloneStream, $offset);
601
+		}
602
+
603
+		return $cloneStream;
604
+	}
605 605
 }
Please login to merge, or discard this patch.
Spacing   +186 added lines, -186 removed lines patch added patch discarded remove patch
@@ -45,34 +45,34 @@  discard block
 block discarded – undo
45 45
      *
46 46
      * @throws RuntimeException If there is no data to read in the input stream
47 47
      */
48
-    public function ask(InputInterface $input, OutputInterface $output, Question $question)
48
+    public function ask( InputInterface $input, OutputInterface $output, Question $question )
49 49
     {
50
-        if ($output instanceof ConsoleOutputInterface) {
50
+        if ( $output instanceof ConsoleOutputInterface ) {
51 51
             $output = $output->getErrorOutput();
52 52
         }
53 53
 
54
-        if (!$input->isInteractive()) {
55
-            return $this->getDefaultAnswer($question);
54
+        if ( ! $input->isInteractive() ) {
55
+            return $this->getDefaultAnswer( $question );
56 56
         }
57 57
 
58
-        if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) {
58
+        if ( $input instanceof StreamableInputInterface && $stream = $input->getStream() ) {
59 59
             $this->inputStream = $stream;
60 60
         }
61 61
 
62 62
         try {
63
-            if (!$question->getValidator()) {
64
-                return $this->doAsk($output, $question);
63
+            if ( ! $question->getValidator() ) {
64
+                return $this->doAsk( $output, $question );
65 65
             }
66 66
 
67
-            $interviewer = function () use ($output, $question) {
68
-                return $this->doAsk($output, $question);
67
+            $interviewer = function() use ( $output, $question ) {
68
+                return $this->doAsk( $output, $question );
69 69
             };
70 70
 
71
-            return $this->validateAttempts($interviewer, $output, $question);
72
-        } catch (MissingInputException $exception) {
73
-            $input->setInteractive(false);
71
+            return $this->validateAttempts( $interviewer, $output, $question );
72
+        } catch ( MissingInputException $exception ) {
73
+            $input->setInteractive( false );
74 74
 
75
-            if (null === $fallbackOutput = $this->getDefaultAnswer($question)) {
75
+            if ( null === $fallbackOutput = $this->getDefaultAnswer( $question ) ) {
76 76
                 throw $exception;
77 77
             }
78 78
 
@@ -103,48 +103,48 @@  discard block
 block discarded – undo
103 103
      *
104 104
      * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
105 105
      */
106
-    private function doAsk(OutputInterface $output, Question $question)
106
+    private function doAsk( OutputInterface $output, Question $question )
107 107
     {
108
-        $this->writePrompt($output, $question);
108
+        $this->writePrompt( $output, $question );
109 109
 
110 110
         $inputStream = $this->inputStream ?: \STDIN;
111 111
         $autocomplete = $question->getAutocompleterCallback();
112 112
 
113
-        if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) {
113
+        if ( null === $autocomplete || ! self::$stty || ! Terminal::hasSttyAvailable() ) {
114 114
             $ret = false;
115
-            if ($question->isHidden()) {
115
+            if ( $question->isHidden() ) {
116 116
                 try {
117
-                    $hiddenResponse = $this->getHiddenResponse($output, $inputStream, $question->isTrimmable());
118
-                    $ret = $question->isTrimmable() ? trim($hiddenResponse) : $hiddenResponse;
119
-                } catch (RuntimeException $e) {
120
-                    if (!$question->isHiddenFallback()) {
117
+                    $hiddenResponse = $this->getHiddenResponse( $output, $inputStream, $question->isTrimmable() );
118
+                    $ret = $question->isTrimmable() ? trim( $hiddenResponse ) : $hiddenResponse;
119
+                } catch ( RuntimeException $e ) {
120
+                    if ( ! $question->isHiddenFallback() ) {
121 121
                         throw $e;
122 122
                     }
123 123
                 }
124 124
             }
125 125
 
126
-            if (false === $ret) {
127
-                $ret = $this->readInput($inputStream, $question);
128
-                if (false === $ret) {
129
-                    throw new MissingInputException('Aborted.');
126
+            if ( false === $ret ) {
127
+                $ret = $this->readInput( $inputStream, $question );
128
+                if ( false === $ret ) {
129
+                    throw new MissingInputException( 'Aborted.' );
130 130
                 }
131
-                if ($question->isTrimmable()) {
132
-                    $ret = trim($ret);
131
+                if ( $question->isTrimmable() ) {
132
+                    $ret = trim( $ret );
133 133
                 }
134 134
             }
135 135
         } else {
136
-            $autocomplete = $this->autocomplete($output, $question, $inputStream, $autocomplete);
137
-            $ret = $question->isTrimmable() ? trim($autocomplete) : $autocomplete;
136
+            $autocomplete = $this->autocomplete( $output, $question, $inputStream, $autocomplete );
137
+            $ret = $question->isTrimmable() ? trim( $autocomplete ) : $autocomplete;
138 138
         }
139 139
 
140
-        if ($output instanceof ConsoleSectionOutput) {
141
-            $output->addContent($ret);
140
+        if ( $output instanceof ConsoleSectionOutput ) {
141
+            $output->addContent( $ret );
142 142
         }
143 143
 
144
-        $ret = \strlen($ret) > 0 ? $ret : $question->getDefault();
144
+        $ret = \strlen( $ret ) > 0 ? $ret : $question->getDefault();
145 145
 
146
-        if ($normalizer = $question->getNormalizer()) {
147
-            return $normalizer($ret);
146
+        if ( $normalizer = $question->getNormalizer() ) {
147
+            return $normalizer( $ret );
148 148
         }
149 149
 
150 150
         return $ret;
@@ -153,27 +153,27 @@  discard block
 block discarded – undo
153 153
     /**
154 154
      * @return mixed
155 155
      */
156
-    private function getDefaultAnswer(Question $question)
156
+    private function getDefaultAnswer( Question $question )
157 157
     {
158 158
         $default = $question->getDefault();
159 159
 
160
-        if (null === $default) {
160
+        if ( null === $default ) {
161 161
             return $default;
162 162
         }
163 163
 
164
-        if ($validator = $question->getValidator()) {
165
-            return \call_user_func($question->getValidator(), $default);
166
-        } elseif ($question instanceof ChoiceQuestion) {
164
+        if ( $validator = $question->getValidator() ) {
165
+            return \call_user_func( $question->getValidator(), $default );
166
+        } elseif ( $question instanceof ChoiceQuestion ) {
167 167
             $choices = $question->getChoices();
168 168
 
169
-            if (!$question->isMultiselect()) {
170
-                return $choices[$default] ?? $default;
169
+            if ( ! $question->isMultiselect() ) {
170
+                return $choices[ $default ] ?? $default;
171 171
             }
172 172
 
173
-            $default = explode(',', $default);
174
-            foreach ($default as $k => $v) {
175
-                $v = $question->isTrimmable() ? trim($v) : $v;
176
-                $default[$k] = $choices[$v] ?? $v;
173
+            $default = explode( ',', $default );
174
+            foreach ( $default as $k => $v ) {
175
+                $v = $question->isTrimmable() ? trim( $v ) : $v;
176
+                $default[ $k ] = $choices[ $v ] ?? $v;
177 177
             }
178 178
         }
179 179
 
@@ -183,34 +183,34 @@  discard block
 block discarded – undo
183 183
     /**
184 184
      * Outputs the question prompt.
185 185
      */
186
-    protected function writePrompt(OutputInterface $output, Question $question)
186
+    protected function writePrompt( OutputInterface $output, Question $question )
187 187
     {
188 188
         $message = $question->getQuestion();
189 189
 
190
-        if ($question instanceof ChoiceQuestion) {
191
-            $output->writeln(array_merge([
190
+        if ( $question instanceof ChoiceQuestion ) {
191
+            $output->writeln( array_merge( [
192 192
                 $question->getQuestion(),
193
-            ], $this->formatChoiceQuestionChoices($question, 'info')));
193
+            ], $this->formatChoiceQuestionChoices( $question, 'info' ) ) );
194 194
 
195 195
             $message = $question->getPrompt();
196 196
         }
197 197
 
198
-        $output->write($message);
198
+        $output->write( $message );
199 199
     }
200 200
 
201 201
     /**
202 202
      * @return string[]
203 203
      */
204
-    protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag)
204
+    protected function formatChoiceQuestionChoices( ChoiceQuestion $question, string $tag )
205 205
     {
206
-        $messages = [];
206
+        $messages = [ ];
207 207
 
208
-        $maxWidth = max(array_map('self::width', array_keys($choices = $question->getChoices())));
208
+        $maxWidth = max( array_map( 'self::width', array_keys( $choices = $question->getChoices() ) ) );
209 209
 
210
-        foreach ($choices as $key => $value) {
211
-            $padding = str_repeat(' ', $maxWidth - self::width($key));
210
+        foreach ( $choices as $key => $value ) {
211
+            $padding = str_repeat( ' ', $maxWidth - self::width( $key ) );
212 212
 
213
-            $messages[] = sprintf("  [<$tag>%s$padding</$tag>] %s", $key, $value);
213
+            $messages[ ] = sprintf( "  [<$tag>%s$padding</$tag>] %s", $key, $value );
214 214
         }
215 215
 
216 216
         return $messages;
@@ -219,15 +219,15 @@  discard block
 block discarded – undo
219 219
     /**
220 220
      * Outputs an error message.
221 221
      */
222
-    protected function writeError(OutputInterface $output, \Exception $error)
222
+    protected function writeError( OutputInterface $output, \Exception $error )
223 223
     {
224
-        if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
225
-            $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
224
+        if ( null !== $this->getHelperSet() && $this->getHelperSet()->has( 'formatter' ) ) {
225
+            $message = $this->getHelperSet()->get( 'formatter' )->formatBlock( $error->getMessage(), 'error' );
226 226
         } else {
227
-            $message = '<error>'.$error->getMessage().'</error>';
227
+            $message = '<error>' . $error->getMessage() . '</error>';
228 228
         }
229 229
 
230
-        $output->writeln($message);
230
+        $output->writeln( $message );
231 231
     }
232 232
 
233 233
     /**
@@ -235,91 +235,91 @@  discard block
 block discarded – undo
235 235
      *
236 236
      * @param resource $inputStream
237 237
      */
238
-    private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string
238
+    private function autocomplete( OutputInterface $output, Question $question, $inputStream, callable $autocomplete ): string
239 239
     {
240
-        $cursor = new Cursor($output, $inputStream);
240
+        $cursor = new Cursor( $output, $inputStream );
241 241
 
242 242
         $fullChoice = '';
243 243
         $ret = '';
244 244
 
245 245
         $i = 0;
246 246
         $ofs = -1;
247
-        $matches = $autocomplete($ret);
248
-        $numMatches = \count($matches);
247
+        $matches = $autocomplete( $ret );
248
+        $numMatches = \count( $matches );
249 249
 
250
-        $sttyMode = shell_exec('stty -g');
250
+        $sttyMode = shell_exec( 'stty -g' );
251 251
 
252 252
         // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
253
-        shell_exec('stty -icanon -echo');
253
+        shell_exec( 'stty -icanon -echo' );
254 254
 
255 255
         // Add highlighted text style
256
-        $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
256
+        $output->getFormatter()->setStyle( 'hl', new OutputFormatterStyle( 'black', 'white' ) );
257 257
 
258 258
         // Read a keypress
259
-        while (!feof($inputStream)) {
260
-            $c = fread($inputStream, 1);
259
+        while ( ! feof( $inputStream ) ) {
260
+            $c = fread( $inputStream, 1 );
261 261
 
262 262
             // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false.
263
-            if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
264
-                shell_exec(sprintf('stty %s', $sttyMode));
265
-                throw new MissingInputException('Aborted.');
266
-            } elseif ("\177" === $c) { // Backspace Character
267
-                if (0 === $numMatches && 0 !== $i) {
263
+            if ( false === $c || ( '' === $ret && '' === $c && null === $question->getDefault() ) ) {
264
+                shell_exec( sprintf( 'stty %s', $sttyMode ) );
265
+                throw new MissingInputException( 'Aborted.' );
266
+            } elseif ( "\177" === $c ) { // Backspace Character
267
+                if ( 0 === $numMatches && 0 !== $i ) {
268 268
                     --$i;
269
-                    $cursor->moveLeft(s($fullChoice)->slice(-1)->width(false));
269
+                    $cursor->moveLeft( s( $fullChoice )->slice(-1)->width( false ) );
270 270
 
271
-                    $fullChoice = self::substr($fullChoice, 0, $i);
271
+                    $fullChoice = self::substr( $fullChoice, 0, $i );
272 272
                 }
273 273
 
274
-                if (0 === $i) {
274
+                if ( 0 === $i ) {
275 275
                     $ofs = -1;
276
-                    $matches = $autocomplete($ret);
277
-                    $numMatches = \count($matches);
276
+                    $matches = $autocomplete( $ret );
277
+                    $numMatches = \count( $matches );
278 278
                 } else {
279 279
                     $numMatches = 0;
280 280
                 }
281 281
 
282 282
                 // Pop the last character off the end of our string
283
-                $ret = self::substr($ret, 0, $i);
284
-            } elseif ("\033" === $c) {
283
+                $ret = self::substr( $ret, 0, $i );
284
+            } elseif ( "\033" === $c ) {
285 285
                 // Did we read an escape sequence?
286
-                $c .= fread($inputStream, 2);
286
+                $c .= fread( $inputStream, 2 );
287 287
 
288 288
                 // A = Up Arrow. B = Down Arrow
289
-                if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
290
-                    if ('A' === $c[2] && -1 === $ofs) {
289
+                if ( isset( $c[ 2 ] ) && ( 'A' === $c[ 2 ] || 'B' === $c[ 2 ] ) ) {
290
+                    if ( 'A' === $c[ 2 ] && -1 === $ofs ) {
291 291
                         $ofs = 0;
292 292
                     }
293 293
 
294
-                    if (0 === $numMatches) {
294
+                    if ( 0 === $numMatches ) {
295 295
                         continue;
296 296
                     }
297 297
 
298
-                    $ofs += ('A' === $c[2]) ? -1 : 1;
299
-                    $ofs = ($numMatches + $ofs) % $numMatches;
298
+                    $ofs += ( 'A' === $c[ 2 ] ) ? -1 : 1;
299
+                    $ofs = ( $numMatches + $ofs ) % $numMatches;
300 300
                 }
301
-            } elseif (\ord($c) < 32) {
302
-                if ("\t" === $c || "\n" === $c) {
303
-                    if ($numMatches > 0 && -1 !== $ofs) {
304
-                        $ret = (string) $matches[$ofs];
301
+            } elseif ( \ord( $c ) < 32 ) {
302
+                if ( "\t" === $c || "\n" === $c ) {
303
+                    if ( $numMatches > 0 && -1 !== $ofs ) {
304
+                        $ret = (string)$matches[ $ofs ];
305 305
                         // Echo out remaining chars for current match
306
-                        $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
307
-                        $output->write($remainingCharacters);
306
+                        $remainingCharacters = substr( $ret, \strlen( trim( $this->mostRecentlyEnteredValue( $fullChoice ) ) ) );
307
+                        $output->write( $remainingCharacters );
308 308
                         $fullChoice .= $remainingCharacters;
309
-                        $i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding);
309
+                        $i = ( false === $encoding = mb_detect_encoding( $fullChoice, null, true ) ) ? \strlen( $fullChoice ) : mb_strlen( $fullChoice, $encoding );
310 310
 
311 311
                         $matches = array_filter(
312
-                            $autocomplete($ret),
313
-                            function ($match) use ($ret) {
314
-                                return '' === $ret || str_starts_with($match, $ret);
312
+                            $autocomplete( $ret ),
313
+                            function( $match ) use ( $ret ) {
314
+                                return '' === $ret || str_starts_with( $match, $ret );
315 315
                             }
316 316
                         );
317
-                        $numMatches = \count($matches);
317
+                        $numMatches = \count( $matches );
318 318
                         $ofs = -1;
319 319
                     }
320 320
 
321
-                    if ("\n" === $c) {
322
-                        $output->write($c);
321
+                    if ( "\n" === $c ) {
322
+                        $output->write( $c );
323 323
                         break;
324 324
                     }
325 325
 
@@ -328,58 +328,58 @@  discard block
 block discarded – undo
328 328
 
329 329
                 continue;
330 330
             } else {
331
-                if ("\x80" <= $c) {
332
-                    $c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]);
331
+                if ( "\x80" <= $c ) {
332
+                    $c .= fread( $inputStream, [ "\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3 ][ $c & "\xF0" ] );
333 333
                 }
334 334
 
335
-                $output->write($c);
335
+                $output->write( $c );
336 336
                 $ret .= $c;
337 337
                 $fullChoice .= $c;
338 338
                 ++$i;
339 339
 
340 340
                 $tempRet = $ret;
341 341
 
342
-                if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
343
-                    $tempRet = $this->mostRecentlyEnteredValue($fullChoice);
342
+                if ( $question instanceof ChoiceQuestion && $question->isMultiselect() ) {
343
+                    $tempRet = $this->mostRecentlyEnteredValue( $fullChoice );
344 344
                 }
345 345
 
346 346
                 $numMatches = 0;
347 347
                 $ofs = 0;
348 348
 
349
-                foreach ($autocomplete($ret) as $value) {
349
+                foreach ( $autocomplete( $ret ) as $value ) {
350 350
                     // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
351
-                    if (str_starts_with($value, $tempRet)) {
352
-                        $matches[$numMatches++] = $value;
351
+                    if ( str_starts_with( $value, $tempRet ) ) {
352
+                        $matches[ $numMatches++ ] = $value;
353 353
                     }
354 354
                 }
355 355
             }
356 356
 
357 357
             $cursor->clearLineAfter();
358 358
 
359
-            if ($numMatches > 0 && -1 !== $ofs) {
359
+            if ( $numMatches > 0 && -1 !== $ofs ) {
360 360
                 $cursor->savePosition();
361 361
                 // Write highlighted text, complete the partially entered response
362
-                $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
363
-                $output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
362
+                $charactersEntered = \strlen( trim( $this->mostRecentlyEnteredValue( $fullChoice ) ) );
363
+                $output->write( '<hl>' . OutputFormatter::escapeTrailingBackslash( substr( $matches[ $ofs ], $charactersEntered ) ) . '</hl>' );
364 364
                 $cursor->restorePosition();
365 365
             }
366 366
         }
367 367
 
368 368
         // Reset stty so it behaves normally again
369
-        shell_exec(sprintf('stty %s', $sttyMode));
369
+        shell_exec( sprintf( 'stty %s', $sttyMode ) );
370 370
 
371 371
         return $fullChoice;
372 372
     }
373 373
 
374
-    private function mostRecentlyEnteredValue(string $entered): string
374
+    private function mostRecentlyEnteredValue( string $entered ): string
375 375
     {
376 376
         // Determine the most recent value that the user entered
377
-        if (!str_contains($entered, ',')) {
377
+        if ( ! str_contains( $entered, ',' ) ) {
378 378
             return $entered;
379 379
         }
380 380
 
381
-        $choices = explode(',', $entered);
382
-        if ('' !== $lastChoice = trim($choices[\count($choices) - 1])) {
381
+        $choices = explode( ',', $entered );
382
+        if ( '' !== $lastChoice = trim( $choices[ \count( $choices ) - 1 ] ) ) {
383 383
             return $lastChoice;
384 384
         }
385 385
 
@@ -394,49 +394,49 @@  discard block
 block discarded – undo
394 394
      *
395 395
      * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
396 396
      */
397
-    private function getHiddenResponse(OutputInterface $output, $inputStream, bool $trimmable = true): string
397
+    private function getHiddenResponse( OutputInterface $output, $inputStream, bool $trimmable = true ): string
398 398
     {
399
-        if ('\\' === \DIRECTORY_SEPARATOR) {
400
-            $exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
399
+        if ( '\\' === \DIRECTORY_SEPARATOR ) {
400
+            $exe = __DIR__ . '/../Resources/bin/hiddeninput.exe';
401 401
 
402 402
             // handle code running from a phar
403
-            if ('phar:' === substr(__FILE__, 0, 5)) {
404
-                $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
405
-                copy($exe, $tmpExe);
403
+            if ( 'phar:' === substr( __FILE__, 0, 5 ) ) {
404
+                $tmpExe = sys_get_temp_dir() . '/hiddeninput.exe';
405
+                copy( $exe, $tmpExe );
406 406
                 $exe = $tmpExe;
407 407
             }
408 408
 
409
-            $sExec = shell_exec('"'.$exe.'"');
410
-            $value = $trimmable ? rtrim($sExec) : $sExec;
411
-            $output->writeln('');
409
+            $sExec = shell_exec( '"' . $exe . '"' );
410
+            $value = $trimmable ? rtrim( $sExec ) : $sExec;
411
+            $output->writeln( '' );
412 412
 
413
-            if (isset($tmpExe)) {
414
-                unlink($tmpExe);
413
+            if ( isset( $tmpExe ) ) {
414
+                unlink( $tmpExe );
415 415
             }
416 416
 
417 417
             return $value;
418 418
         }
419 419
 
420
-        if (self::$stty && Terminal::hasSttyAvailable()) {
421
-            $sttyMode = shell_exec('stty -g');
422
-            shell_exec('stty -echo');
423
-        } elseif ($this->isInteractiveInput($inputStream)) {
424
-            throw new RuntimeException('Unable to hide the response.');
420
+        if ( self::$stty && Terminal::hasSttyAvailable() ) {
421
+            $sttyMode = shell_exec( 'stty -g' );
422
+            shell_exec( 'stty -echo' );
423
+        } elseif ( $this->isInteractiveInput( $inputStream ) ) {
424
+            throw new RuntimeException( 'Unable to hide the response.' );
425 425
         }
426 426
 
427
-        $value = fgets($inputStream, 4096);
427
+        $value = fgets( $inputStream, 4096 );
428 428
 
429
-        if (self::$stty && Terminal::hasSttyAvailable()) {
430
-            shell_exec(sprintf('stty %s', $sttyMode));
429
+        if ( self::$stty && Terminal::hasSttyAvailable() ) {
430
+            shell_exec( sprintf( 'stty %s', $sttyMode ) );
431 431
         }
432 432
 
433
-        if (false === $value) {
434
-            throw new MissingInputException('Aborted.');
433
+        if ( false === $value ) {
434
+            throw new MissingInputException( 'Aborted.' );
435 435
         }
436
-        if ($trimmable) {
437
-            $value = trim($value);
436
+        if ( $trimmable ) {
437
+            $value = trim( $value );
438 438
         }
439
-        $output->writeln('');
439
+        $output->writeln( '' );
440 440
 
441 441
         return $value;
442 442
     }
@@ -450,50 +450,50 @@  discard block
 block discarded – undo
450 450
      *
451 451
      * @throws \Exception In case the max number of attempts has been reached and no valid response has been given
452 452
      */
453
-    private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question)
453
+    private function validateAttempts( callable $interviewer, OutputInterface $output, Question $question )
454 454
     {
455 455
         $error = null;
456 456
         $attempts = $question->getMaxAttempts();
457 457
 
458
-        while (null === $attempts || $attempts--) {
459
-            if (null !== $error) {
460
-                $this->writeError($output, $error);
458
+        while ( null === $attempts || $attempts-- ) {
459
+            if ( null !== $error ) {
460
+                $this->writeError( $output, $error );
461 461
             }
462 462
 
463 463
             try {
464
-                return $question->getValidator()($interviewer());
465
-            } catch (RuntimeException $e) {
464
+                return $question->getValidator()( $interviewer() );
465
+            } catch ( RuntimeException $e ) {
466 466
                 throw $e;
467
-            } catch (\Exception $error) {
467
+            } catch ( \Exception $error ) {
468 468
             }
469 469
         }
470 470
 
471 471
         throw $error;
472 472
     }
473 473
 
474
-    private function isInteractiveInput($inputStream): bool
474
+    private function isInteractiveInput( $inputStream ): bool
475 475
     {
476
-        if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) {
476
+        if ( 'php://stdin' !== ( stream_get_meta_data( $inputStream )[ 'uri' ] ?? null ) ) {
477 477
             return false;
478 478
         }
479 479
 
480
-        if (null !== self::$stdinIsInteractive) {
480
+        if ( null !== self::$stdinIsInteractive ) {
481 481
             return self::$stdinIsInteractive;
482 482
         }
483 483
 
484
-        if (\function_exists('stream_isatty')) {
485
-            return self::$stdinIsInteractive = stream_isatty(fopen('php://stdin', 'r'));
484
+        if ( \function_exists( 'stream_isatty' ) ) {
485
+            return self::$stdinIsInteractive = stream_isatty( fopen( 'php://stdin', 'r' ) );
486 486
         }
487 487
 
488
-        if (\function_exists('posix_isatty')) {
489
-            return self::$stdinIsInteractive = posix_isatty(fopen('php://stdin', 'r'));
488
+        if ( \function_exists( 'posix_isatty' ) ) {
489
+            return self::$stdinIsInteractive = posix_isatty( fopen( 'php://stdin', 'r' ) );
490 490
         }
491 491
 
492
-        if (!\function_exists('exec')) {
492
+        if ( ! \function_exists( 'exec' ) ) {
493 493
             return self::$stdinIsInteractive = true;
494 494
         }
495 495
 
496
-        exec('stty 2> /dev/null', $output, $status);
496
+        exec( 'stty 2> /dev/null', $output, $status );
497 497
 
498 498
         return self::$stdinIsInteractive = 1 !== $status;
499 499
     }
@@ -506,30 +506,30 @@  discard block
 block discarded – undo
506 506
      *
507 507
      * @return string|false The input received, false in case input could not be read
508 508
      */
509
-    private function readInput($inputStream, Question $question)
509
+    private function readInput( $inputStream, Question $question )
510 510
     {
511
-        if (!$question->isMultiline()) {
511
+        if ( ! $question->isMultiline() ) {
512 512
             $cp = $this->setIOCodepage();
513
-            $ret = fgets($inputStream, 4096);
513
+            $ret = fgets( $inputStream, 4096 );
514 514
 
515
-            return $this->resetIOCodepage($cp, $ret);
515
+            return $this->resetIOCodepage( $cp, $ret );
516 516
         }
517 517
 
518
-        $multiLineStreamReader = $this->cloneInputStream($inputStream);
519
-        if (null === $multiLineStreamReader) {
518
+        $multiLineStreamReader = $this->cloneInputStream( $inputStream );
519
+        if ( null === $multiLineStreamReader ) {
520 520
             return false;
521 521
         }
522 522
 
523 523
         $ret = '';
524 524
         $cp = $this->setIOCodepage();
525
-        while (false !== ($char = fgetc($multiLineStreamReader))) {
526
-            if (\PHP_EOL === "{$ret}{$char}") {
525
+        while ( false !== ( $char = fgetc( $multiLineStreamReader ) ) ) {
526
+            if ( \PHP_EOL === "{$ret}{$char}" ) {
527 527
                 break;
528 528
             }
529 529
             $ret .= $char;
530 530
         }
531 531
 
532
-        return $this->resetIOCodepage($cp, $ret);
532
+        return $this->resetIOCodepage( $cp, $ret );
533 533
     }
534 534
 
535 535
     /**
@@ -539,9 +539,9 @@  discard block
 block discarded – undo
539 539
      */
540 540
     private function setIOCodepage(): int
541 541
     {
542
-        if (\function_exists('sapi_windows_cp_set')) {
542
+        if ( \function_exists( 'sapi_windows_cp_set' ) ) {
543 543
             $cp = sapi_windows_cp_get();
544
-            sapi_windows_cp_set(sapi_windows_cp_get('oem'));
544
+            sapi_windows_cp_set( sapi_windows_cp_get( 'oem' ) );
545 545
 
546 546
             return $cp;
547 547
         }
@@ -556,13 +556,13 @@  discard block
 block discarded – undo
556 556
      *
557 557
      * @return string|false
558 558
      */
559
-    private function resetIOCodepage(int $cp, $input)
559
+    private function resetIOCodepage( int $cp, $input )
560 560
     {
561
-        if (0 !== $cp) {
562
-            sapi_windows_cp_set($cp);
561
+        if ( 0 !== $cp ) {
562
+            sapi_windows_cp_set( $cp );
563 563
 
564
-            if (false !== $input && '' !== $input) {
565
-                $input = sapi_windows_cp_conv(sapi_windows_cp_get('oem'), $cp, $input);
564
+            if ( false !== $input && '' !== $input ) {
565
+                $input = sapi_windows_cp_conv( sapi_windows_cp_get( 'oem' ), $cp, $input );
566 566
             }
567 567
         }
568 568
 
@@ -577,27 +577,27 @@  discard block
 block discarded – undo
577 577
      *
578 578
      * @return resource|null The cloned resource, null in case it could not be cloned
579 579
      */
580
-    private function cloneInputStream($inputStream)
580
+    private function cloneInputStream( $inputStream )
581 581
     {
582
-        $streamMetaData = stream_get_meta_data($inputStream);
583
-        $seekable = $streamMetaData['seekable'] ?? false;
584
-        $mode = $streamMetaData['mode'] ?? 'rb';
585
-        $uri = $streamMetaData['uri'] ?? null;
582
+        $streamMetaData = stream_get_meta_data( $inputStream );
583
+        $seekable = $streamMetaData[ 'seekable' ] ?? false;
584
+        $mode = $streamMetaData[ 'mode' ] ?? 'rb';
585
+        $uri = $streamMetaData[ 'uri' ] ?? null;
586 586
 
587
-        if (null === $uri) {
587
+        if ( null === $uri ) {
588 588
             return null;
589 589
         }
590 590
 
591
-        $cloneStream = fopen($uri, $mode);
591
+        $cloneStream = fopen( $uri, $mode );
592 592
 
593 593
         // For seekable and writable streams, add all the same data to the
594 594
         // cloned stream and then seek to the same offset.
595
-        if (true === $seekable && !\in_array($mode, ['r', 'rb', 'rt'])) {
596
-            $offset = ftell($inputStream);
597
-            rewind($inputStream);
598
-            stream_copy_to_stream($inputStream, $cloneStream);
599
-            fseek($inputStream, $offset);
600
-            fseek($cloneStream, $offset);
595
+        if ( true === $seekable && ! \in_array( $mode, [ 'r', 'rb', 'rt' ] ) ) {
596
+            $offset = ftell( $inputStream );
597
+            rewind( $inputStream );
598
+            stream_copy_to_stream( $inputStream, $cloneStream );
599
+            fseek( $inputStream, $offset );
600
+            fseek( $cloneStream, $offset );
601 601
         }
602 602
 
603 603
         return $cloneStream;
Please login to merge, or discard this patch.
Braces   +15 added lines, -27 removed lines patch added patch discarded remove patch
@@ -31,8 +31,7 @@  discard block
 block discarded – undo
31 31
  *
32 32
  * @author Fabien Potencier <[email protected]>
33 33
  */
34
-class QuestionHelper extends Helper
35
-{
34
+class QuestionHelper extends Helper {
36 35
     private $inputStream;
37 36
     private static $shell;
38 37
     private static $stty = true;
@@ -45,8 +44,7 @@  discard block
 block discarded – undo
45 44
      *
46 45
      * @throws RuntimeException If there is no data to read in the input stream
47 46
      */
48
-    public function ask(InputInterface $input, OutputInterface $output, Question $question)
49
-    {
47
+    public function ask(InputInterface $input, OutputInterface $output, Question $question) {
50 48
         if ($output instanceof ConsoleOutputInterface) {
51 49
             $output = $output->getErrorOutput();
52 50
         }
@@ -83,16 +81,14 @@  discard block
 block discarded – undo
83 81
     /**
84 82
      * {@inheritdoc}
85 83
      */
86
-    public function getName()
87
-    {
84
+    public function getName() {
88 85
         return 'question';
89 86
     }
90 87
 
91 88
     /**
92 89
      * Prevents usage of stty.
93 90
      */
94
-    public static function disableStty()
95
-    {
91
+    public static function disableStty() {
96 92
         self::$stty = false;
97 93
     }
98 94
 
@@ -103,8 +99,7 @@  discard block
 block discarded – undo
103 99
      *
104 100
      * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
105 101
      */
106
-    private function doAsk(OutputInterface $output, Question $question)
107
-    {
102
+    private function doAsk(OutputInterface $output, Question $question) {
108 103
         $this->writePrompt($output, $question);
109 104
 
110 105
         $inputStream = $this->inputStream ?: \STDIN;
@@ -153,8 +148,7 @@  discard block
 block discarded – undo
153 148
     /**
154 149
      * @return mixed
155 150
      */
156
-    private function getDefaultAnswer(Question $question)
157
-    {
151
+    private function getDefaultAnswer(Question $question) {
158 152
         $default = $question->getDefault();
159 153
 
160 154
         if (null === $default) {
@@ -183,8 +177,7 @@  discard block
 block discarded – undo
183 177
     /**
184 178
      * Outputs the question prompt.
185 179
      */
186
-    protected function writePrompt(OutputInterface $output, Question $question)
187
-    {
180
+    protected function writePrompt(OutputInterface $output, Question $question) {
188 181
         $message = $question->getQuestion();
189 182
 
190 183
         if ($question instanceof ChoiceQuestion) {
@@ -201,8 +194,7 @@  discard block
 block discarded – undo
201 194
     /**
202 195
      * @return string[]
203 196
      */
204
-    protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag)
205
-    {
197
+    protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag) {
206 198
         $messages = [];
207 199
 
208 200
         $maxWidth = max(array_map('self::width', array_keys($choices = $question->getChoices())));
@@ -219,8 +211,7 @@  discard block
 block discarded – undo
219 211
     /**
220 212
      * Outputs an error message.
221 213
      */
222
-    protected function writeError(OutputInterface $output, \Exception $error)
223
-    {
214
+    protected function writeError(OutputInterface $output, \Exception $error) {
224 215
         if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
225 216
             $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
226 217
         } else {
@@ -263,7 +254,8 @@  discard block
 block discarded – undo
263 254
             if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
264 255
                 shell_exec(sprintf('stty %s', $sttyMode));
265 256
                 throw new MissingInputException('Aborted.');
266
-            } elseif ("\177" === $c) { // Backspace Character
257
+            } elseif ("\177" === $c) {
258
+// Backspace Character
267 259
                 if (0 === $numMatches && 0 !== $i) {
268 260
                     --$i;
269 261
                     $cursor->moveLeft(s($fullChoice)->slice(-1)->width(false));
@@ -450,8 +442,7 @@  discard block
 block discarded – undo
450 442
      *
451 443
      * @throws \Exception In case the max number of attempts has been reached and no valid response has been given
452 444
      */
453
-    private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question)
454
-    {
445
+    private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question) {
455 446
         $error = null;
456 447
         $attempts = $question->getMaxAttempts();
457 448
 
@@ -506,8 +497,7 @@  discard block
 block discarded – undo
506 497
      *
507 498
      * @return string|false The input received, false in case input could not be read
508 499
      */
509
-    private function readInput($inputStream, Question $question)
510
-    {
500
+    private function readInput($inputStream, Question $question) {
511 501
         if (!$question->isMultiline()) {
512 502
             $cp = $this->setIOCodepage();
513 503
             $ret = fgets($inputStream, 4096);
@@ -556,8 +546,7 @@  discard block
 block discarded – undo
556 546
      *
557 547
      * @return string|false
558 548
      */
559
-    private function resetIOCodepage(int $cp, $input)
560
-    {
549
+    private function resetIOCodepage(int $cp, $input) {
561 550
         if (0 !== $cp) {
562 551
             sapi_windows_cp_set($cp);
563 552
 
@@ -577,8 +566,7 @@  discard block
 block discarded – undo
577 566
      *
578 567
      * @return resource|null The cloned resource, null in case it could not be cloned
579 568
      */
580
-    private function cloneInputStream($inputStream)
581
-    {
569
+    private function cloneInputStream($inputStream) {
582 570
         $streamMetaData = stream_get_meta_data($inputStream);
583 571
         $seekable = $streamMetaData['seekable'] ?? false;
584 572
         $mode = $streamMetaData['mode'] ?? 'rb';
Please login to merge, or discard this patch.
vendor/symfony/console/Helper/Table.php 3 patches
Indentation   +833 added lines, -833 removed lines patch added patch discarded remove patch
@@ -29,837 +29,837 @@
 block discarded – undo
29 29
  */
30 30
 class Table
31 31
 {
32
-    private const SEPARATOR_TOP = 0;
33
-    private const SEPARATOR_TOP_BOTTOM = 1;
34
-    private const SEPARATOR_MID = 2;
35
-    private const SEPARATOR_BOTTOM = 3;
36
-    private const BORDER_OUTSIDE = 0;
37
-    private const BORDER_INSIDE = 1;
38
-
39
-    private $headerTitle;
40
-    private $footerTitle;
41
-
42
-    /**
43
-     * Table headers.
44
-     */
45
-    private $headers = [];
46
-
47
-    /**
48
-     * Table rows.
49
-     */
50
-    private $rows = [];
51
-    private $horizontal = false;
52
-
53
-    /**
54
-     * Column widths cache.
55
-     */
56
-    private $effectiveColumnWidths = [];
57
-
58
-    /**
59
-     * Number of columns cache.
60
-     *
61
-     * @var int
62
-     */
63
-    private $numberOfColumns;
64
-
65
-    /**
66
-     * @var OutputInterface
67
-     */
68
-    private $output;
69
-
70
-    /**
71
-     * @var TableStyle
72
-     */
73
-    private $style;
74
-
75
-    /**
76
-     * @var array
77
-     */
78
-    private $columnStyles = [];
79
-
80
-    /**
81
-     * User set column widths.
82
-     *
83
-     * @var array
84
-     */
85
-    private $columnWidths = [];
86
-    private $columnMaxWidths = [];
87
-
88
-    private static $styles;
89
-
90
-    private $rendered = false;
91
-
92
-    public function __construct(OutputInterface $output)
93
-    {
94
-        $this->output = $output;
95
-
96
-        if (!self::$styles) {
97
-            self::$styles = self::initStyles();
98
-        }
99
-
100
-        $this->setStyle('default');
101
-    }
102
-
103
-    /**
104
-     * Sets a style definition.
105
-     */
106
-    public static function setStyleDefinition(string $name, TableStyle $style)
107
-    {
108
-        if (!self::$styles) {
109
-            self::$styles = self::initStyles();
110
-        }
111
-
112
-        self::$styles[$name] = $style;
113
-    }
114
-
115
-    /**
116
-     * Gets a style definition by name.
117
-     *
118
-     * @return TableStyle
119
-     */
120
-    public static function getStyleDefinition(string $name)
121
-    {
122
-        if (!self::$styles) {
123
-            self::$styles = self::initStyles();
124
-        }
125
-
126
-        if (isset(self::$styles[$name])) {
127
-            return self::$styles[$name];
128
-        }
129
-
130
-        throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
131
-    }
132
-
133
-    /**
134
-     * Sets table style.
135
-     *
136
-     * @param TableStyle|string $name The style name or a TableStyle instance
137
-     *
138
-     * @return $this
139
-     */
140
-    public function setStyle($name)
141
-    {
142
-        $this->style = $this->resolveStyle($name);
143
-
144
-        return $this;
145
-    }
146
-
147
-    /**
148
-     * Gets the current table style.
149
-     *
150
-     * @return TableStyle
151
-     */
152
-    public function getStyle()
153
-    {
154
-        return $this->style;
155
-    }
156
-
157
-    /**
158
-     * Sets table column style.
159
-     *
160
-     * @param TableStyle|string $name The style name or a TableStyle instance
161
-     *
162
-     * @return $this
163
-     */
164
-    public function setColumnStyle(int $columnIndex, $name)
165
-    {
166
-        $this->columnStyles[$columnIndex] = $this->resolveStyle($name);
167
-
168
-        return $this;
169
-    }
170
-
171
-    /**
172
-     * Gets the current style for a column.
173
-     *
174
-     * If style was not set, it returns the global table style.
175
-     *
176
-     * @return TableStyle
177
-     */
178
-    public function getColumnStyle(int $columnIndex)
179
-    {
180
-        return $this->columnStyles[$columnIndex] ?? $this->getStyle();
181
-    }
182
-
183
-    /**
184
-     * Sets the minimum width of a column.
185
-     *
186
-     * @return $this
187
-     */
188
-    public function setColumnWidth(int $columnIndex, int $width)
189
-    {
190
-        $this->columnWidths[$columnIndex] = $width;
191
-
192
-        return $this;
193
-    }
194
-
195
-    /**
196
-     * Sets the minimum width of all columns.
197
-     *
198
-     * @return $this
199
-     */
200
-    public function setColumnWidths(array $widths)
201
-    {
202
-        $this->columnWidths = [];
203
-        foreach ($widths as $index => $width) {
204
-            $this->setColumnWidth($index, $width);
205
-        }
206
-
207
-        return $this;
208
-    }
209
-
210
-    /**
211
-     * Sets the maximum width of a column.
212
-     *
213
-     * Any cell within this column which contents exceeds the specified width will be wrapped into multiple lines, while
214
-     * formatted strings are preserved.
215
-     *
216
-     * @return $this
217
-     */
218
-    public function setColumnMaxWidth(int $columnIndex, int $width): self
219
-    {
220
-        if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) {
221
-            throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type($this->output->getFormatter())));
222
-        }
223
-
224
-        $this->columnMaxWidths[$columnIndex] = $width;
225
-
226
-        return $this;
227
-    }
228
-
229
-    public function setHeaders(array $headers)
230
-    {
231
-        $headers = array_values($headers);
232
-        if (!empty($headers) && !\is_array($headers[0])) {
233
-            $headers = [$headers];
234
-        }
235
-
236
-        $this->headers = $headers;
237
-
238
-        return $this;
239
-    }
240
-
241
-    public function setRows(array $rows)
242
-    {
243
-        $this->rows = [];
244
-
245
-        return $this->addRows($rows);
246
-    }
247
-
248
-    public function addRows(array $rows)
249
-    {
250
-        foreach ($rows as $row) {
251
-            $this->addRow($row);
252
-        }
253
-
254
-        return $this;
255
-    }
256
-
257
-    public function addRow($row)
258
-    {
259
-        if ($row instanceof TableSeparator) {
260
-            $this->rows[] = $row;
261
-
262
-            return $this;
263
-        }
264
-
265
-        if (!\is_array($row)) {
266
-            throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.');
267
-        }
268
-
269
-        $this->rows[] = array_values($row);
270
-
271
-        return $this;
272
-    }
273
-
274
-    /**
275
-     * Adds a row to the table, and re-renders the table.
276
-     */
277
-    public function appendRow($row): self
278
-    {
279
-        if (!$this->output instanceof ConsoleSectionOutput) {
280
-            throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__));
281
-        }
282
-
283
-        if ($this->rendered) {
284
-            $this->output->clear($this->calculateRowCount());
285
-        }
286
-
287
-        $this->addRow($row);
288
-        $this->render();
289
-
290
-        return $this;
291
-    }
292
-
293
-    public function setRow($column, array $row)
294
-    {
295
-        $this->rows[$column] = $row;
296
-
297
-        return $this;
298
-    }
299
-
300
-    public function setHeaderTitle(?string $title): self
301
-    {
302
-        $this->headerTitle = $title;
303
-
304
-        return $this;
305
-    }
306
-
307
-    public function setFooterTitle(?string $title): self
308
-    {
309
-        $this->footerTitle = $title;
310
-
311
-        return $this;
312
-    }
313
-
314
-    public function setHorizontal(bool $horizontal = true): self
315
-    {
316
-        $this->horizontal = $horizontal;
317
-
318
-        return $this;
319
-    }
320
-
321
-    /**
322
-     * Renders table to output.
323
-     *
324
-     * Example:
325
-     *
326
-     *     +---------------+-----------------------+------------------+
327
-     *     | ISBN          | Title                 | Author           |
328
-     *     +---------------+-----------------------+------------------+
329
-     *     | 99921-58-10-7 | Divine Comedy         | Dante Alighieri  |
330
-     *     | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
331
-     *     | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
332
-     *     +---------------+-----------------------+------------------+
333
-     */
334
-    public function render()
335
-    {
336
-        $divider = new TableSeparator();
337
-        if ($this->horizontal) {
338
-            $rows = [];
339
-            foreach ($this->headers[0] ?? [] as $i => $header) {
340
-                $rows[$i] = [$header];
341
-                foreach ($this->rows as $row) {
342
-                    if ($row instanceof TableSeparator) {
343
-                        continue;
344
-                    }
345
-                    if (isset($row[$i])) {
346
-                        $rows[$i][] = $row[$i];
347
-                    } elseif ($rows[$i][0] instanceof TableCell && $rows[$i][0]->getColspan() >= 2) {
348
-                        // Noop, there is a "title"
349
-                    } else {
350
-                        $rows[$i][] = null;
351
-                    }
352
-                }
353
-            }
354
-        } else {
355
-            $rows = array_merge($this->headers, [$divider], $this->rows);
356
-        }
357
-
358
-        $this->calculateNumberOfColumns($rows);
359
-
360
-        $rows = $this->buildTableRows($rows);
361
-        $this->calculateColumnsWidth($rows);
362
-
363
-        $isHeader = !$this->horizontal;
364
-        $isFirstRow = $this->horizontal;
365
-        $hasTitle = (bool) $this->headerTitle;
366
-        foreach ($rows as $row) {
367
-            if ($divider === $row) {
368
-                $isHeader = false;
369
-                $isFirstRow = true;
370
-
371
-                continue;
372
-            }
373
-            if ($row instanceof TableSeparator) {
374
-                $this->renderRowSeparator();
375
-
376
-                continue;
377
-            }
378
-            if (!$row) {
379
-                continue;
380
-            }
381
-
382
-            if ($isHeader || $isFirstRow) {
383
-                $this->renderRowSeparator(
384
-                    $isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM,
385
-                    $hasTitle ? $this->headerTitle : null,
386
-                    $hasTitle ? $this->style->getHeaderTitleFormat() : null
387
-                );
388
-                $isFirstRow = false;
389
-                $hasTitle = false;
390
-            }
391
-            if ($this->horizontal) {
392
-                $this->renderRow($row, $this->style->getCellRowFormat(), $this->style->getCellHeaderFormat());
393
-            } else {
394
-                $this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat());
395
-            }
396
-        }
397
-        $this->renderRowSeparator(self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat());
398
-
399
-        $this->cleanup();
400
-        $this->rendered = true;
401
-    }
402
-
403
-    /**
404
-     * Renders horizontal header separator.
405
-     *
406
-     * Example:
407
-     *
408
-     *     +-----+-----------+-------+
409
-     */
410
-    private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null)
411
-    {
412
-        if (0 === $count = $this->numberOfColumns) {
413
-            return;
414
-        }
415
-
416
-        $borders = $this->style->getBorderChars();
417
-        if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) {
418
-            return;
419
-        }
420
-
421
-        $crossings = $this->style->getCrossingChars();
422
-        if (self::SEPARATOR_MID === $type) {
423
-            [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]];
424
-        } elseif (self::SEPARATOR_TOP === $type) {
425
-            [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]];
426
-        } elseif (self::SEPARATOR_TOP_BOTTOM === $type) {
427
-            [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]];
428
-        } else {
429
-            [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[7], $crossings[6], $crossings[5]];
430
-        }
431
-
432
-        $markup = $leftChar;
433
-        for ($column = 0; $column < $count; ++$column) {
434
-            $markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]);
435
-            $markup .= $column === $count - 1 ? $rightChar : $midChar;
436
-        }
437
-
438
-        if (null !== $title) {
439
-            $titleLength = Helper::width(Helper::removeDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title)));
440
-            $markupLength = Helper::width($markup);
441
-            if ($titleLength > $limit = $markupLength - 4) {
442
-                $titleLength = $limit;
443
-                $formatLength = Helper::width(Helper::removeDecoration($formatter, sprintf($titleFormat, '')));
444
-                $formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...');
445
-            }
446
-
447
-            $titleStart = intdiv($markupLength - $titleLength, 2);
448
-            if (false === mb_detect_encoding($markup, null, true)) {
449
-                $markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength);
450
-            } else {
451
-                $markup = mb_substr($markup, 0, $titleStart).$formattedTitle.mb_substr($markup, $titleStart + $titleLength);
452
-            }
453
-        }
454
-
455
-        $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
456
-    }
457
-
458
-    /**
459
-     * Renders vertical column separator.
460
-     */
461
-    private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string
462
-    {
463
-        $borders = $this->style->getBorderChars();
464
-
465
-        return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]);
466
-    }
467
-
468
-    /**
469
-     * Renders table row.
470
-     *
471
-     * Example:
472
-     *
473
-     *     | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
474
-     */
475
-    private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null)
476
-    {
477
-        $rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
478
-        $columns = $this->getRowColumns($row);
479
-        $last = \count($columns) - 1;
480
-        foreach ($columns as $i => $column) {
481
-            if ($firstCellFormat && 0 === $i) {
482
-                $rowContent .= $this->renderCell($row, $column, $firstCellFormat);
483
-            } else {
484
-                $rowContent .= $this->renderCell($row, $column, $cellFormat);
485
-            }
486
-            $rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE);
487
-        }
488
-        $this->output->writeln($rowContent);
489
-    }
490
-
491
-    /**
492
-     * Renders table cell with padding.
493
-     */
494
-    private function renderCell(array $row, int $column, string $cellFormat): string
495
-    {
496
-        $cell = $row[$column] ?? '';
497
-        $width = $this->effectiveColumnWidths[$column];
498
-        if ($cell instanceof TableCell && $cell->getColspan() > 1) {
499
-            // add the width of the following columns(numbers of colspan).
500
-            foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
501
-                $width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn];
502
-            }
503
-        }
504
-
505
-        // str_pad won't work properly with multi-byte strings, we need to fix the padding
506
-        if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
507
-            $width += \strlen($cell) - mb_strwidth($cell, $encoding);
508
-        }
509
-
510
-        $style = $this->getColumnStyle($column);
511
-
512
-        if ($cell instanceof TableSeparator) {
513
-            return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width));
514
-        }
515
-
516
-        $width += Helper::length($cell) - Helper::length(Helper::removeDecoration($this->output->getFormatter(), $cell));
517
-        $content = sprintf($style->getCellRowContentFormat(), $cell);
518
-
519
-        $padType = $style->getPadType();
520
-        if ($cell instanceof TableCell && $cell->getStyle() instanceof TableCellStyle) {
521
-            $isNotStyledByTag = !preg_match('/^<(\w+|(\w+=[\w,]+;?)*)>.+<\/(\w+|(\w+=\w+;?)*)?>$/', $cell);
522
-            if ($isNotStyledByTag) {
523
-                $cellFormat = $cell->getStyle()->getCellFormat();
524
-                if (!\is_string($cellFormat)) {
525
-                    $tag = http_build_query($cell->getStyle()->getTagOptions(), '', ';');
526
-                    $cellFormat = '<'.$tag.'>%s</>';
527
-                }
528
-
529
-                if (strstr($content, '</>')) {
530
-                    $content = str_replace('</>', '', $content);
531
-                    $width -= 3;
532
-                }
533
-                if (strstr($content, '<fg=default;bg=default>')) {
534
-                    $content = str_replace('<fg=default;bg=default>', '', $content);
535
-                    $width -= \strlen('<fg=default;bg=default>');
536
-                }
537
-            }
538
-
539
-            $padType = $cell->getStyle()->getPadByAlign();
540
-        }
541
-
542
-        return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $padType));
543
-    }
544
-
545
-    /**
546
-     * Calculate number of columns for this table.
547
-     */
548
-    private function calculateNumberOfColumns(array $rows)
549
-    {
550
-        $columns = [0];
551
-        foreach ($rows as $row) {
552
-            if ($row instanceof TableSeparator) {
553
-                continue;
554
-            }
555
-
556
-            $columns[] = $this->getNumberOfColumns($row);
557
-        }
558
-
559
-        $this->numberOfColumns = max($columns);
560
-    }
561
-
562
-    private function buildTableRows(array $rows): TableRows
563
-    {
564
-        /** @var WrappableOutputFormatterInterface $formatter */
565
-        $formatter = $this->output->getFormatter();
566
-        $unmergedRows = [];
567
-        for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) {
568
-            $rows = $this->fillNextRows($rows, $rowKey);
569
-
570
-            // Remove any new line breaks and replace it with a new line
571
-            foreach ($rows[$rowKey] as $column => $cell) {
572
-                $colspan = $cell instanceof TableCell ? $cell->getColspan() : 1;
573
-
574
-                if (isset($this->columnMaxWidths[$column]) && Helper::width(Helper::removeDecoration($formatter, $cell)) > $this->columnMaxWidths[$column]) {
575
-                    $cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan);
576
-                }
577
-                if (!strstr($cell ?? '', "\n")) {
578
-                    continue;
579
-                }
580
-                $escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell)));
581
-                $cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped;
582
-                $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
583
-                foreach ($lines as $lineKey => $line) {
584
-                    if ($colspan > 1) {
585
-                        $line = new TableCell($line, ['colspan' => $colspan]);
586
-                    }
587
-                    if (0 === $lineKey) {
588
-                        $rows[$rowKey][$column] = $line;
589
-                    } else {
590
-                        if (!\array_key_exists($rowKey, $unmergedRows) || !\array_key_exists($lineKey, $unmergedRows[$rowKey])) {
591
-                            $unmergedRows[$rowKey][$lineKey] = $this->copyRow($rows, $rowKey);
592
-                        }
593
-                        $unmergedRows[$rowKey][$lineKey][$column] = $line;
594
-                    }
595
-                }
596
-            }
597
-        }
598
-
599
-        return new TableRows(function () use ($rows, $unmergedRows): \Traversable {
600
-            foreach ($rows as $rowKey => $row) {
601
-                yield $row instanceof TableSeparator ? $row : $this->fillCells($row);
602
-
603
-                if (isset($unmergedRows[$rowKey])) {
604
-                    foreach ($unmergedRows[$rowKey] as $row) {
605
-                        yield $row instanceof TableSeparator ? $row : $this->fillCells($row);
606
-                    }
607
-                }
608
-            }
609
-        });
610
-    }
611
-
612
-    private function calculateRowCount(): int
613
-    {
614
-        $numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows))));
615
-
616
-        if ($this->headers) {
617
-            ++$numberOfRows; // Add row for header separator
618
-        }
619
-
620
-        if (\count($this->rows) > 0) {
621
-            ++$numberOfRows; // Add row for footer separator
622
-        }
623
-
624
-        return $numberOfRows;
625
-    }
626
-
627
-    /**
628
-     * fill rows that contains rowspan > 1.
629
-     *
630
-     * @throws InvalidArgumentException
631
-     */
632
-    private function fillNextRows(array $rows, int $line): array
633
-    {
634
-        $unmergedRows = [];
635
-        foreach ($rows[$line] as $column => $cell) {
636
-            if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
637
-                throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell)));
638
-            }
639
-            if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
640
-                $nbLines = $cell->getRowspan() - 1;
641
-                $lines = [$cell];
642
-                if (strstr($cell, "\n")) {
643
-                    $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
644
-                    $nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;
645
-
646
-                    $rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
647
-                    unset($lines[0]);
648
-                }
649
-
650
-                // create a two dimensional array (rowspan x colspan)
651
-                $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows);
652
-                foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
653
-                    $value = $lines[$unmergedRowKey - $line] ?? '';
654
-                    $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
655
-                    if ($nbLines === $unmergedRowKey - $line) {
656
-                        break;
657
-                    }
658
-                }
659
-            }
660
-        }
661
-
662
-        foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
663
-            // we need to know if $unmergedRow will be merged or inserted into $rows
664
-            if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) {
665
-                foreach ($unmergedRow as $cellKey => $cell) {
666
-                    // insert cell into row at cellKey position
667
-                    array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]);
668
-                }
669
-            } else {
670
-                $row = $this->copyRow($rows, $unmergedRowKey - 1);
671
-                foreach ($unmergedRow as $column => $cell) {
672
-                    if (!empty($cell)) {
673
-                        $row[$column] = $unmergedRow[$column];
674
-                    }
675
-                }
676
-                array_splice($rows, $unmergedRowKey, 0, [$row]);
677
-            }
678
-        }
679
-
680
-        return $rows;
681
-    }
682
-
683
-    /**
684
-     * fill cells for a row that contains colspan > 1.
685
-     */
686
-    private function fillCells(iterable $row)
687
-    {
688
-        $newRow = [];
689
-
690
-        foreach ($row as $column => $cell) {
691
-            $newRow[] = $cell;
692
-            if ($cell instanceof TableCell && $cell->getColspan() > 1) {
693
-                foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
694
-                    // insert empty value at column position
695
-                    $newRow[] = '';
696
-                }
697
-            }
698
-        }
699
-
700
-        return $newRow ?: $row;
701
-    }
702
-
703
-    private function copyRow(array $rows, int $line): array
704
-    {
705
-        $row = $rows[$line];
706
-        foreach ($row as $cellKey => $cellValue) {
707
-            $row[$cellKey] = '';
708
-            if ($cellValue instanceof TableCell) {
709
-                $row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]);
710
-            }
711
-        }
712
-
713
-        return $row;
714
-    }
715
-
716
-    /**
717
-     * Gets number of columns by row.
718
-     */
719
-    private function getNumberOfColumns(array $row): int
720
-    {
721
-        $columns = \count($row);
722
-        foreach ($row as $column) {
723
-            $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0;
724
-        }
725
-
726
-        return $columns;
727
-    }
728
-
729
-    /**
730
-     * Gets list of columns for the given row.
731
-     */
732
-    private function getRowColumns(array $row): array
733
-    {
734
-        $columns = range(0, $this->numberOfColumns - 1);
735
-        foreach ($row as $cellKey => $cell) {
736
-            if ($cell instanceof TableCell && $cell->getColspan() > 1) {
737
-                // exclude grouped columns.
738
-                $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1));
739
-            }
740
-        }
741
-
742
-        return $columns;
743
-    }
744
-
745
-    /**
746
-     * Calculates columns widths.
747
-     */
748
-    private function calculateColumnsWidth(iterable $rows)
749
-    {
750
-        for ($column = 0; $column < $this->numberOfColumns; ++$column) {
751
-            $lengths = [];
752
-            foreach ($rows as $row) {
753
-                if ($row instanceof TableSeparator) {
754
-                    continue;
755
-                }
756
-
757
-                foreach ($row as $i => $cell) {
758
-                    if ($cell instanceof TableCell) {
759
-                        $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
760
-                        $textLength = Helper::width($textContent);
761
-                        if ($textLength > 0) {
762
-                            $contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
763
-                            foreach ($contentColumns as $position => $content) {
764
-                                $row[$i + $position] = $content;
765
-                            }
766
-                        }
767
-                    }
768
-                }
769
-
770
-                $lengths[] = $this->getCellWidth($row, $column);
771
-            }
772
-
773
-            $this->effectiveColumnWidths[$column] = max($lengths) + Helper::width($this->style->getCellRowContentFormat()) - 2;
774
-        }
775
-    }
776
-
777
-    private function getColumnSeparatorWidth(): int
778
-    {
779
-        return Helper::width(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3]));
780
-    }
781
-
782
-    private function getCellWidth(array $row, int $column): int
783
-    {
784
-        $cellWidth = 0;
785
-
786
-        if (isset($row[$column])) {
787
-            $cell = $row[$column];
788
-            $cellWidth = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $cell));
789
-        }
790
-
791
-        $columnWidth = $this->columnWidths[$column] ?? 0;
792
-        $cellWidth = max($cellWidth, $columnWidth);
793
-
794
-        return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth;
795
-    }
796
-
797
-    /**
798
-     * Called after rendering to cleanup cache data.
799
-     */
800
-    private function cleanup()
801
-    {
802
-        $this->effectiveColumnWidths = [];
803
-        $this->numberOfColumns = null;
804
-    }
805
-
806
-    private static function initStyles(): array
807
-    {
808
-        $borderless = new TableStyle();
809
-        $borderless
810
-            ->setHorizontalBorderChars('=')
811
-            ->setVerticalBorderChars(' ')
812
-            ->setDefaultCrossingChar(' ')
813
-        ;
814
-
815
-        $compact = new TableStyle();
816
-        $compact
817
-            ->setHorizontalBorderChars('')
818
-            ->setVerticalBorderChars(' ')
819
-            ->setDefaultCrossingChar('')
820
-            ->setCellRowContentFormat('%s')
821
-        ;
822
-
823
-        $styleGuide = new TableStyle();
824
-        $styleGuide
825
-            ->setHorizontalBorderChars('-')
826
-            ->setVerticalBorderChars(' ')
827
-            ->setDefaultCrossingChar(' ')
828
-            ->setCellHeaderFormat('%s')
829
-        ;
830
-
831
-        $box = (new TableStyle())
832
-            ->setHorizontalBorderChars('─')
833
-            ->setVerticalBorderChars('│')
834
-            ->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├')
835
-        ;
836
-
837
-        $boxDouble = (new TableStyle())
838
-            ->setHorizontalBorderChars('═', '─')
839
-            ->setVerticalBorderChars('║', '│')
840
-            ->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣')
841
-        ;
842
-
843
-        return [
844
-            'default' => new TableStyle(),
845
-            'borderless' => $borderless,
846
-            'compact' => $compact,
847
-            'symfony-style-guide' => $styleGuide,
848
-            'box' => $box,
849
-            'box-double' => $boxDouble,
850
-        ];
851
-    }
852
-
853
-    private function resolveStyle($name): TableStyle
854
-    {
855
-        if ($name instanceof TableStyle) {
856
-            return $name;
857
-        }
858
-
859
-        if (isset(self::$styles[$name])) {
860
-            return self::$styles[$name];
861
-        }
862
-
863
-        throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
864
-    }
32
+	private const SEPARATOR_TOP = 0;
33
+	private const SEPARATOR_TOP_BOTTOM = 1;
34
+	private const SEPARATOR_MID = 2;
35
+	private const SEPARATOR_BOTTOM = 3;
36
+	private const BORDER_OUTSIDE = 0;
37
+	private const BORDER_INSIDE = 1;
38
+
39
+	private $headerTitle;
40
+	private $footerTitle;
41
+
42
+	/**
43
+	 * Table headers.
44
+	 */
45
+	private $headers = [];
46
+
47
+	/**
48
+	 * Table rows.
49
+	 */
50
+	private $rows = [];
51
+	private $horizontal = false;
52
+
53
+	/**
54
+	 * Column widths cache.
55
+	 */
56
+	private $effectiveColumnWidths = [];
57
+
58
+	/**
59
+	 * Number of columns cache.
60
+	 *
61
+	 * @var int
62
+	 */
63
+	private $numberOfColumns;
64
+
65
+	/**
66
+	 * @var OutputInterface
67
+	 */
68
+	private $output;
69
+
70
+	/**
71
+	 * @var TableStyle
72
+	 */
73
+	private $style;
74
+
75
+	/**
76
+	 * @var array
77
+	 */
78
+	private $columnStyles = [];
79
+
80
+	/**
81
+	 * User set column widths.
82
+	 *
83
+	 * @var array
84
+	 */
85
+	private $columnWidths = [];
86
+	private $columnMaxWidths = [];
87
+
88
+	private static $styles;
89
+
90
+	private $rendered = false;
91
+
92
+	public function __construct(OutputInterface $output)
93
+	{
94
+		$this->output = $output;
95
+
96
+		if (!self::$styles) {
97
+			self::$styles = self::initStyles();
98
+		}
99
+
100
+		$this->setStyle('default');
101
+	}
102
+
103
+	/**
104
+	 * Sets a style definition.
105
+	 */
106
+	public static function setStyleDefinition(string $name, TableStyle $style)
107
+	{
108
+		if (!self::$styles) {
109
+			self::$styles = self::initStyles();
110
+		}
111
+
112
+		self::$styles[$name] = $style;
113
+	}
114
+
115
+	/**
116
+	 * Gets a style definition by name.
117
+	 *
118
+	 * @return TableStyle
119
+	 */
120
+	public static function getStyleDefinition(string $name)
121
+	{
122
+		if (!self::$styles) {
123
+			self::$styles = self::initStyles();
124
+		}
125
+
126
+		if (isset(self::$styles[$name])) {
127
+			return self::$styles[$name];
128
+		}
129
+
130
+		throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
131
+	}
132
+
133
+	/**
134
+	 * Sets table style.
135
+	 *
136
+	 * @param TableStyle|string $name The style name or a TableStyle instance
137
+	 *
138
+	 * @return $this
139
+	 */
140
+	public function setStyle($name)
141
+	{
142
+		$this->style = $this->resolveStyle($name);
143
+
144
+		return $this;
145
+	}
146
+
147
+	/**
148
+	 * Gets the current table style.
149
+	 *
150
+	 * @return TableStyle
151
+	 */
152
+	public function getStyle()
153
+	{
154
+		return $this->style;
155
+	}
156
+
157
+	/**
158
+	 * Sets table column style.
159
+	 *
160
+	 * @param TableStyle|string $name The style name or a TableStyle instance
161
+	 *
162
+	 * @return $this
163
+	 */
164
+	public function setColumnStyle(int $columnIndex, $name)
165
+	{
166
+		$this->columnStyles[$columnIndex] = $this->resolveStyle($name);
167
+
168
+		return $this;
169
+	}
170
+
171
+	/**
172
+	 * Gets the current style for a column.
173
+	 *
174
+	 * If style was not set, it returns the global table style.
175
+	 *
176
+	 * @return TableStyle
177
+	 */
178
+	public function getColumnStyle(int $columnIndex)
179
+	{
180
+		return $this->columnStyles[$columnIndex] ?? $this->getStyle();
181
+	}
182
+
183
+	/**
184
+	 * Sets the minimum width of a column.
185
+	 *
186
+	 * @return $this
187
+	 */
188
+	public function setColumnWidth(int $columnIndex, int $width)
189
+	{
190
+		$this->columnWidths[$columnIndex] = $width;
191
+
192
+		return $this;
193
+	}
194
+
195
+	/**
196
+	 * Sets the minimum width of all columns.
197
+	 *
198
+	 * @return $this
199
+	 */
200
+	public function setColumnWidths(array $widths)
201
+	{
202
+		$this->columnWidths = [];
203
+		foreach ($widths as $index => $width) {
204
+			$this->setColumnWidth($index, $width);
205
+		}
206
+
207
+		return $this;
208
+	}
209
+
210
+	/**
211
+	 * Sets the maximum width of a column.
212
+	 *
213
+	 * Any cell within this column which contents exceeds the specified width will be wrapped into multiple lines, while
214
+	 * formatted strings are preserved.
215
+	 *
216
+	 * @return $this
217
+	 */
218
+	public function setColumnMaxWidth(int $columnIndex, int $width): self
219
+	{
220
+		if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) {
221
+			throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type($this->output->getFormatter())));
222
+		}
223
+
224
+		$this->columnMaxWidths[$columnIndex] = $width;
225
+
226
+		return $this;
227
+	}
228
+
229
+	public function setHeaders(array $headers)
230
+	{
231
+		$headers = array_values($headers);
232
+		if (!empty($headers) && !\is_array($headers[0])) {
233
+			$headers = [$headers];
234
+		}
235
+
236
+		$this->headers = $headers;
237
+
238
+		return $this;
239
+	}
240
+
241
+	public function setRows(array $rows)
242
+	{
243
+		$this->rows = [];
244
+
245
+		return $this->addRows($rows);
246
+	}
247
+
248
+	public function addRows(array $rows)
249
+	{
250
+		foreach ($rows as $row) {
251
+			$this->addRow($row);
252
+		}
253
+
254
+		return $this;
255
+	}
256
+
257
+	public function addRow($row)
258
+	{
259
+		if ($row instanceof TableSeparator) {
260
+			$this->rows[] = $row;
261
+
262
+			return $this;
263
+		}
264
+
265
+		if (!\is_array($row)) {
266
+			throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.');
267
+		}
268
+
269
+		$this->rows[] = array_values($row);
270
+
271
+		return $this;
272
+	}
273
+
274
+	/**
275
+	 * Adds a row to the table, and re-renders the table.
276
+	 */
277
+	public function appendRow($row): self
278
+	{
279
+		if (!$this->output instanceof ConsoleSectionOutput) {
280
+			throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__));
281
+		}
282
+
283
+		if ($this->rendered) {
284
+			$this->output->clear($this->calculateRowCount());
285
+		}
286
+
287
+		$this->addRow($row);
288
+		$this->render();
289
+
290
+		return $this;
291
+	}
292
+
293
+	public function setRow($column, array $row)
294
+	{
295
+		$this->rows[$column] = $row;
296
+
297
+		return $this;
298
+	}
299
+
300
+	public function setHeaderTitle(?string $title): self
301
+	{
302
+		$this->headerTitle = $title;
303
+
304
+		return $this;
305
+	}
306
+
307
+	public function setFooterTitle(?string $title): self
308
+	{
309
+		$this->footerTitle = $title;
310
+
311
+		return $this;
312
+	}
313
+
314
+	public function setHorizontal(bool $horizontal = true): self
315
+	{
316
+		$this->horizontal = $horizontal;
317
+
318
+		return $this;
319
+	}
320
+
321
+	/**
322
+	 * Renders table to output.
323
+	 *
324
+	 * Example:
325
+	 *
326
+	 *     +---------------+-----------------------+------------------+
327
+	 *     | ISBN          | Title                 | Author           |
328
+	 *     +---------------+-----------------------+------------------+
329
+	 *     | 99921-58-10-7 | Divine Comedy         | Dante Alighieri  |
330
+	 *     | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
331
+	 *     | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
332
+	 *     +---------------+-----------------------+------------------+
333
+	 */
334
+	public function render()
335
+	{
336
+		$divider = new TableSeparator();
337
+		if ($this->horizontal) {
338
+			$rows = [];
339
+			foreach ($this->headers[0] ?? [] as $i => $header) {
340
+				$rows[$i] = [$header];
341
+				foreach ($this->rows as $row) {
342
+					if ($row instanceof TableSeparator) {
343
+						continue;
344
+					}
345
+					if (isset($row[$i])) {
346
+						$rows[$i][] = $row[$i];
347
+					} elseif ($rows[$i][0] instanceof TableCell && $rows[$i][0]->getColspan() >= 2) {
348
+						// Noop, there is a "title"
349
+					} else {
350
+						$rows[$i][] = null;
351
+					}
352
+				}
353
+			}
354
+		} else {
355
+			$rows = array_merge($this->headers, [$divider], $this->rows);
356
+		}
357
+
358
+		$this->calculateNumberOfColumns($rows);
359
+
360
+		$rows = $this->buildTableRows($rows);
361
+		$this->calculateColumnsWidth($rows);
362
+
363
+		$isHeader = !$this->horizontal;
364
+		$isFirstRow = $this->horizontal;
365
+		$hasTitle = (bool) $this->headerTitle;
366
+		foreach ($rows as $row) {
367
+			if ($divider === $row) {
368
+				$isHeader = false;
369
+				$isFirstRow = true;
370
+
371
+				continue;
372
+			}
373
+			if ($row instanceof TableSeparator) {
374
+				$this->renderRowSeparator();
375
+
376
+				continue;
377
+			}
378
+			if (!$row) {
379
+				continue;
380
+			}
381
+
382
+			if ($isHeader || $isFirstRow) {
383
+				$this->renderRowSeparator(
384
+					$isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM,
385
+					$hasTitle ? $this->headerTitle : null,
386
+					$hasTitle ? $this->style->getHeaderTitleFormat() : null
387
+				);
388
+				$isFirstRow = false;
389
+				$hasTitle = false;
390
+			}
391
+			if ($this->horizontal) {
392
+				$this->renderRow($row, $this->style->getCellRowFormat(), $this->style->getCellHeaderFormat());
393
+			} else {
394
+				$this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat());
395
+			}
396
+		}
397
+		$this->renderRowSeparator(self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat());
398
+
399
+		$this->cleanup();
400
+		$this->rendered = true;
401
+	}
402
+
403
+	/**
404
+	 * Renders horizontal header separator.
405
+	 *
406
+	 * Example:
407
+	 *
408
+	 *     +-----+-----------+-------+
409
+	 */
410
+	private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null)
411
+	{
412
+		if (0 === $count = $this->numberOfColumns) {
413
+			return;
414
+		}
415
+
416
+		$borders = $this->style->getBorderChars();
417
+		if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) {
418
+			return;
419
+		}
420
+
421
+		$crossings = $this->style->getCrossingChars();
422
+		if (self::SEPARATOR_MID === $type) {
423
+			[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]];
424
+		} elseif (self::SEPARATOR_TOP === $type) {
425
+			[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]];
426
+		} elseif (self::SEPARATOR_TOP_BOTTOM === $type) {
427
+			[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]];
428
+		} else {
429
+			[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[7], $crossings[6], $crossings[5]];
430
+		}
431
+
432
+		$markup = $leftChar;
433
+		for ($column = 0; $column < $count; ++$column) {
434
+			$markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]);
435
+			$markup .= $column === $count - 1 ? $rightChar : $midChar;
436
+		}
437
+
438
+		if (null !== $title) {
439
+			$titleLength = Helper::width(Helper::removeDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title)));
440
+			$markupLength = Helper::width($markup);
441
+			if ($titleLength > $limit = $markupLength - 4) {
442
+				$titleLength = $limit;
443
+				$formatLength = Helper::width(Helper::removeDecoration($formatter, sprintf($titleFormat, '')));
444
+				$formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...');
445
+			}
446
+
447
+			$titleStart = intdiv($markupLength - $titleLength, 2);
448
+			if (false === mb_detect_encoding($markup, null, true)) {
449
+				$markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength);
450
+			} else {
451
+				$markup = mb_substr($markup, 0, $titleStart).$formattedTitle.mb_substr($markup, $titleStart + $titleLength);
452
+			}
453
+		}
454
+
455
+		$this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
456
+	}
457
+
458
+	/**
459
+	 * Renders vertical column separator.
460
+	 */
461
+	private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string
462
+	{
463
+		$borders = $this->style->getBorderChars();
464
+
465
+		return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]);
466
+	}
467
+
468
+	/**
469
+	 * Renders table row.
470
+	 *
471
+	 * Example:
472
+	 *
473
+	 *     | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
474
+	 */
475
+	private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null)
476
+	{
477
+		$rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
478
+		$columns = $this->getRowColumns($row);
479
+		$last = \count($columns) - 1;
480
+		foreach ($columns as $i => $column) {
481
+			if ($firstCellFormat && 0 === $i) {
482
+				$rowContent .= $this->renderCell($row, $column, $firstCellFormat);
483
+			} else {
484
+				$rowContent .= $this->renderCell($row, $column, $cellFormat);
485
+			}
486
+			$rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE);
487
+		}
488
+		$this->output->writeln($rowContent);
489
+	}
490
+
491
+	/**
492
+	 * Renders table cell with padding.
493
+	 */
494
+	private function renderCell(array $row, int $column, string $cellFormat): string
495
+	{
496
+		$cell = $row[$column] ?? '';
497
+		$width = $this->effectiveColumnWidths[$column];
498
+		if ($cell instanceof TableCell && $cell->getColspan() > 1) {
499
+			// add the width of the following columns(numbers of colspan).
500
+			foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
501
+				$width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn];
502
+			}
503
+		}
504
+
505
+		// str_pad won't work properly with multi-byte strings, we need to fix the padding
506
+		if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
507
+			$width += \strlen($cell) - mb_strwidth($cell, $encoding);
508
+		}
509
+
510
+		$style = $this->getColumnStyle($column);
511
+
512
+		if ($cell instanceof TableSeparator) {
513
+			return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width));
514
+		}
515
+
516
+		$width += Helper::length($cell) - Helper::length(Helper::removeDecoration($this->output->getFormatter(), $cell));
517
+		$content = sprintf($style->getCellRowContentFormat(), $cell);
518
+
519
+		$padType = $style->getPadType();
520
+		if ($cell instanceof TableCell && $cell->getStyle() instanceof TableCellStyle) {
521
+			$isNotStyledByTag = !preg_match('/^<(\w+|(\w+=[\w,]+;?)*)>.+<\/(\w+|(\w+=\w+;?)*)?>$/', $cell);
522
+			if ($isNotStyledByTag) {
523
+				$cellFormat = $cell->getStyle()->getCellFormat();
524
+				if (!\is_string($cellFormat)) {
525
+					$tag = http_build_query($cell->getStyle()->getTagOptions(), '', ';');
526
+					$cellFormat = '<'.$tag.'>%s</>';
527
+				}
528
+
529
+				if (strstr($content, '</>')) {
530
+					$content = str_replace('</>', '', $content);
531
+					$width -= 3;
532
+				}
533
+				if (strstr($content, '<fg=default;bg=default>')) {
534
+					$content = str_replace('<fg=default;bg=default>', '', $content);
535
+					$width -= \strlen('<fg=default;bg=default>');
536
+				}
537
+			}
538
+
539
+			$padType = $cell->getStyle()->getPadByAlign();
540
+		}
541
+
542
+		return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $padType));
543
+	}
544
+
545
+	/**
546
+	 * Calculate number of columns for this table.
547
+	 */
548
+	private function calculateNumberOfColumns(array $rows)
549
+	{
550
+		$columns = [0];
551
+		foreach ($rows as $row) {
552
+			if ($row instanceof TableSeparator) {
553
+				continue;
554
+			}
555
+
556
+			$columns[] = $this->getNumberOfColumns($row);
557
+		}
558
+
559
+		$this->numberOfColumns = max($columns);
560
+	}
561
+
562
+	private function buildTableRows(array $rows): TableRows
563
+	{
564
+		/** @var WrappableOutputFormatterInterface $formatter */
565
+		$formatter = $this->output->getFormatter();
566
+		$unmergedRows = [];
567
+		for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) {
568
+			$rows = $this->fillNextRows($rows, $rowKey);
569
+
570
+			// Remove any new line breaks and replace it with a new line
571
+			foreach ($rows[$rowKey] as $column => $cell) {
572
+				$colspan = $cell instanceof TableCell ? $cell->getColspan() : 1;
573
+
574
+				if (isset($this->columnMaxWidths[$column]) && Helper::width(Helper::removeDecoration($formatter, $cell)) > $this->columnMaxWidths[$column]) {
575
+					$cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan);
576
+				}
577
+				if (!strstr($cell ?? '', "\n")) {
578
+					continue;
579
+				}
580
+				$escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell)));
581
+				$cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped;
582
+				$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
583
+				foreach ($lines as $lineKey => $line) {
584
+					if ($colspan > 1) {
585
+						$line = new TableCell($line, ['colspan' => $colspan]);
586
+					}
587
+					if (0 === $lineKey) {
588
+						$rows[$rowKey][$column] = $line;
589
+					} else {
590
+						if (!\array_key_exists($rowKey, $unmergedRows) || !\array_key_exists($lineKey, $unmergedRows[$rowKey])) {
591
+							$unmergedRows[$rowKey][$lineKey] = $this->copyRow($rows, $rowKey);
592
+						}
593
+						$unmergedRows[$rowKey][$lineKey][$column] = $line;
594
+					}
595
+				}
596
+			}
597
+		}
598
+
599
+		return new TableRows(function () use ($rows, $unmergedRows): \Traversable {
600
+			foreach ($rows as $rowKey => $row) {
601
+				yield $row instanceof TableSeparator ? $row : $this->fillCells($row);
602
+
603
+				if (isset($unmergedRows[$rowKey])) {
604
+					foreach ($unmergedRows[$rowKey] as $row) {
605
+						yield $row instanceof TableSeparator ? $row : $this->fillCells($row);
606
+					}
607
+				}
608
+			}
609
+		});
610
+	}
611
+
612
+	private function calculateRowCount(): int
613
+	{
614
+		$numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows))));
615
+
616
+		if ($this->headers) {
617
+			++$numberOfRows; // Add row for header separator
618
+		}
619
+
620
+		if (\count($this->rows) > 0) {
621
+			++$numberOfRows; // Add row for footer separator
622
+		}
623
+
624
+		return $numberOfRows;
625
+	}
626
+
627
+	/**
628
+	 * fill rows that contains rowspan > 1.
629
+	 *
630
+	 * @throws InvalidArgumentException
631
+	 */
632
+	private function fillNextRows(array $rows, int $line): array
633
+	{
634
+		$unmergedRows = [];
635
+		foreach ($rows[$line] as $column => $cell) {
636
+			if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
637
+				throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell)));
638
+			}
639
+			if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
640
+				$nbLines = $cell->getRowspan() - 1;
641
+				$lines = [$cell];
642
+				if (strstr($cell, "\n")) {
643
+					$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
644
+					$nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;
645
+
646
+					$rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
647
+					unset($lines[0]);
648
+				}
649
+
650
+				// create a two dimensional array (rowspan x colspan)
651
+				$unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows);
652
+				foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
653
+					$value = $lines[$unmergedRowKey - $line] ?? '';
654
+					$unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
655
+					if ($nbLines === $unmergedRowKey - $line) {
656
+						break;
657
+					}
658
+				}
659
+			}
660
+		}
661
+
662
+		foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
663
+			// we need to know if $unmergedRow will be merged or inserted into $rows
664
+			if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) {
665
+				foreach ($unmergedRow as $cellKey => $cell) {
666
+					// insert cell into row at cellKey position
667
+					array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]);
668
+				}
669
+			} else {
670
+				$row = $this->copyRow($rows, $unmergedRowKey - 1);
671
+				foreach ($unmergedRow as $column => $cell) {
672
+					if (!empty($cell)) {
673
+						$row[$column] = $unmergedRow[$column];
674
+					}
675
+				}
676
+				array_splice($rows, $unmergedRowKey, 0, [$row]);
677
+			}
678
+		}
679
+
680
+		return $rows;
681
+	}
682
+
683
+	/**
684
+	 * fill cells for a row that contains colspan > 1.
685
+	 */
686
+	private function fillCells(iterable $row)
687
+	{
688
+		$newRow = [];
689
+
690
+		foreach ($row as $column => $cell) {
691
+			$newRow[] = $cell;
692
+			if ($cell instanceof TableCell && $cell->getColspan() > 1) {
693
+				foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
694
+					// insert empty value at column position
695
+					$newRow[] = '';
696
+				}
697
+			}
698
+		}
699
+
700
+		return $newRow ?: $row;
701
+	}
702
+
703
+	private function copyRow(array $rows, int $line): array
704
+	{
705
+		$row = $rows[$line];
706
+		foreach ($row as $cellKey => $cellValue) {
707
+			$row[$cellKey] = '';
708
+			if ($cellValue instanceof TableCell) {
709
+				$row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]);
710
+			}
711
+		}
712
+
713
+		return $row;
714
+	}
715
+
716
+	/**
717
+	 * Gets number of columns by row.
718
+	 */
719
+	private function getNumberOfColumns(array $row): int
720
+	{
721
+		$columns = \count($row);
722
+		foreach ($row as $column) {
723
+			$columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0;
724
+		}
725
+
726
+		return $columns;
727
+	}
728
+
729
+	/**
730
+	 * Gets list of columns for the given row.
731
+	 */
732
+	private function getRowColumns(array $row): array
733
+	{
734
+		$columns = range(0, $this->numberOfColumns - 1);
735
+		foreach ($row as $cellKey => $cell) {
736
+			if ($cell instanceof TableCell && $cell->getColspan() > 1) {
737
+				// exclude grouped columns.
738
+				$columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1));
739
+			}
740
+		}
741
+
742
+		return $columns;
743
+	}
744
+
745
+	/**
746
+	 * Calculates columns widths.
747
+	 */
748
+	private function calculateColumnsWidth(iterable $rows)
749
+	{
750
+		for ($column = 0; $column < $this->numberOfColumns; ++$column) {
751
+			$lengths = [];
752
+			foreach ($rows as $row) {
753
+				if ($row instanceof TableSeparator) {
754
+					continue;
755
+				}
756
+
757
+				foreach ($row as $i => $cell) {
758
+					if ($cell instanceof TableCell) {
759
+						$textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
760
+						$textLength = Helper::width($textContent);
761
+						if ($textLength > 0) {
762
+							$contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
763
+							foreach ($contentColumns as $position => $content) {
764
+								$row[$i + $position] = $content;
765
+							}
766
+						}
767
+					}
768
+				}
769
+
770
+				$lengths[] = $this->getCellWidth($row, $column);
771
+			}
772
+
773
+			$this->effectiveColumnWidths[$column] = max($lengths) + Helper::width($this->style->getCellRowContentFormat()) - 2;
774
+		}
775
+	}
776
+
777
+	private function getColumnSeparatorWidth(): int
778
+	{
779
+		return Helper::width(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3]));
780
+	}
781
+
782
+	private function getCellWidth(array $row, int $column): int
783
+	{
784
+		$cellWidth = 0;
785
+
786
+		if (isset($row[$column])) {
787
+			$cell = $row[$column];
788
+			$cellWidth = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $cell));
789
+		}
790
+
791
+		$columnWidth = $this->columnWidths[$column] ?? 0;
792
+		$cellWidth = max($cellWidth, $columnWidth);
793
+
794
+		return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth;
795
+	}
796
+
797
+	/**
798
+	 * Called after rendering to cleanup cache data.
799
+	 */
800
+	private function cleanup()
801
+	{
802
+		$this->effectiveColumnWidths = [];
803
+		$this->numberOfColumns = null;
804
+	}
805
+
806
+	private static function initStyles(): array
807
+	{
808
+		$borderless = new TableStyle();
809
+		$borderless
810
+			->setHorizontalBorderChars('=')
811
+			->setVerticalBorderChars(' ')
812
+			->setDefaultCrossingChar(' ')
813
+		;
814
+
815
+		$compact = new TableStyle();
816
+		$compact
817
+			->setHorizontalBorderChars('')
818
+			->setVerticalBorderChars(' ')
819
+			->setDefaultCrossingChar('')
820
+			->setCellRowContentFormat('%s')
821
+		;
822
+
823
+		$styleGuide = new TableStyle();
824
+		$styleGuide
825
+			->setHorizontalBorderChars('-')
826
+			->setVerticalBorderChars(' ')
827
+			->setDefaultCrossingChar(' ')
828
+			->setCellHeaderFormat('%s')
829
+		;
830
+
831
+		$box = (new TableStyle())
832
+			->setHorizontalBorderChars('─')
833
+			->setVerticalBorderChars('│')
834
+			->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├')
835
+		;
836
+
837
+		$boxDouble = (new TableStyle())
838
+			->setHorizontalBorderChars('═', '─')
839
+			->setVerticalBorderChars('║', '│')
840
+			->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣')
841
+		;
842
+
843
+		return [
844
+			'default' => new TableStyle(),
845
+			'borderless' => $borderless,
846
+			'compact' => $compact,
847
+			'symfony-style-guide' => $styleGuide,
848
+			'box' => $box,
849
+			'box-double' => $boxDouble,
850
+		];
851
+	}
852
+
853
+	private function resolveStyle($name): TableStyle
854
+	{
855
+		if ($name instanceof TableStyle) {
856
+			return $name;
857
+		}
858
+
859
+		if (isset(self::$styles[$name])) {
860
+			return self::$styles[$name];
861
+		}
862
+
863
+		throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
864
+	}
865 865
 }
Please login to merge, or discard this patch.
Spacing   +276 added lines, -276 removed lines patch added patch discarded remove patch
@@ -42,18 +42,18 @@  discard block
 block discarded – undo
42 42
     /**
43 43
      * Table headers.
44 44
      */
45
-    private $headers = [];
45
+    private $headers = [ ];
46 46
 
47 47
     /**
48 48
      * Table rows.
49 49
      */
50
-    private $rows = [];
50
+    private $rows = [ ];
51 51
     private $horizontal = false;
52 52
 
53 53
     /**
54 54
      * Column widths cache.
55 55
      */
56
-    private $effectiveColumnWidths = [];
56
+    private $effectiveColumnWidths = [ ];
57 57
 
58 58
     /**
59 59
      * Number of columns cache.
@@ -75,41 +75,41 @@  discard block
 block discarded – undo
75 75
     /**
76 76
      * @var array
77 77
      */
78
-    private $columnStyles = [];
78
+    private $columnStyles = [ ];
79 79
 
80 80
     /**
81 81
      * User set column widths.
82 82
      *
83 83
      * @var array
84 84
      */
85
-    private $columnWidths = [];
86
-    private $columnMaxWidths = [];
85
+    private $columnWidths = [ ];
86
+    private $columnMaxWidths = [ ];
87 87
 
88 88
     private static $styles;
89 89
 
90 90
     private $rendered = false;
91 91
 
92
-    public function __construct(OutputInterface $output)
92
+    public function __construct( OutputInterface $output )
93 93
     {
94 94
         $this->output = $output;
95 95
 
96
-        if (!self::$styles) {
96
+        if ( ! self::$styles ) {
97 97
             self::$styles = self::initStyles();
98 98
         }
99 99
 
100
-        $this->setStyle('default');
100
+        $this->setStyle( 'default' );
101 101
     }
102 102
 
103 103
     /**
104 104
      * Sets a style definition.
105 105
      */
106
-    public static function setStyleDefinition(string $name, TableStyle $style)
106
+    public static function setStyleDefinition( string $name, TableStyle $style )
107 107
     {
108
-        if (!self::$styles) {
108
+        if ( ! self::$styles ) {
109 109
             self::$styles = self::initStyles();
110 110
         }
111 111
 
112
-        self::$styles[$name] = $style;
112
+        self::$styles[ $name ] = $style;
113 113
     }
114 114
 
115 115
     /**
@@ -117,17 +117,17 @@  discard block
 block discarded – undo
117 117
      *
118 118
      * @return TableStyle
119 119
      */
120
-    public static function getStyleDefinition(string $name)
120
+    public static function getStyleDefinition( string $name )
121 121
     {
122
-        if (!self::$styles) {
122
+        if ( ! self::$styles ) {
123 123
             self::$styles = self::initStyles();
124 124
         }
125 125
 
126
-        if (isset(self::$styles[$name])) {
127
-            return self::$styles[$name];
126
+        if ( isset( self::$styles[ $name ] ) ) {
127
+            return self::$styles[ $name ];
128 128
         }
129 129
 
130
-        throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
130
+        throw new InvalidArgumentException( sprintf( 'Style "%s" is not defined.', $name ) );
131 131
     }
132 132
 
133 133
     /**
@@ -137,9 +137,9 @@  discard block
 block discarded – undo
137 137
      *
138 138
      * @return $this
139 139
      */
140
-    public function setStyle($name)
140
+    public function setStyle( $name )
141 141
     {
142
-        $this->style = $this->resolveStyle($name);
142
+        $this->style = $this->resolveStyle( $name );
143 143
 
144 144
         return $this;
145 145
     }
@@ -161,9 +161,9 @@  discard block
 block discarded – undo
161 161
      *
162 162
      * @return $this
163 163
      */
164
-    public function setColumnStyle(int $columnIndex, $name)
164
+    public function setColumnStyle( int $columnIndex, $name )
165 165
     {
166
-        $this->columnStyles[$columnIndex] = $this->resolveStyle($name);
166
+        $this->columnStyles[ $columnIndex ] = $this->resolveStyle( $name );
167 167
 
168 168
         return $this;
169 169
     }
@@ -175,9 +175,9 @@  discard block
 block discarded – undo
175 175
      *
176 176
      * @return TableStyle
177 177
      */
178
-    public function getColumnStyle(int $columnIndex)
178
+    public function getColumnStyle( int $columnIndex )
179 179
     {
180
-        return $this->columnStyles[$columnIndex] ?? $this->getStyle();
180
+        return $this->columnStyles[ $columnIndex ] ?? $this->getStyle();
181 181
     }
182 182
 
183 183
     /**
@@ -185,9 +185,9 @@  discard block
 block discarded – undo
185 185
      *
186 186
      * @return $this
187 187
      */
188
-    public function setColumnWidth(int $columnIndex, int $width)
188
+    public function setColumnWidth( int $columnIndex, int $width )
189 189
     {
190
-        $this->columnWidths[$columnIndex] = $width;
190
+        $this->columnWidths[ $columnIndex ] = $width;
191 191
 
192 192
         return $this;
193 193
     }
@@ -197,11 +197,11 @@  discard block
 block discarded – undo
197 197
      *
198 198
      * @return $this
199 199
      */
200
-    public function setColumnWidths(array $widths)
200
+    public function setColumnWidths( array $widths )
201 201
     {
202
-        $this->columnWidths = [];
203
-        foreach ($widths as $index => $width) {
204
-            $this->setColumnWidth($index, $width);
202
+        $this->columnWidths = [ ];
203
+        foreach ( $widths as $index => $width ) {
204
+            $this->setColumnWidth( $index, $width );
205 205
         }
206 206
 
207 207
         return $this;
@@ -215,22 +215,22 @@  discard block
 block discarded – undo
215 215
      *
216 216
      * @return $this
217 217
      */
218
-    public function setColumnMaxWidth(int $columnIndex, int $width): self
218
+    public function setColumnMaxWidth( int $columnIndex, int $width ): self
219 219
     {
220
-        if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) {
221
-            throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type($this->output->getFormatter())));
220
+        if ( ! $this->output->getFormatter() instanceof WrappableOutputFormatterInterface ) {
221
+            throw new \LogicException( sprintf( 'Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type( $this->output->getFormatter() ) ) );
222 222
         }
223 223
 
224
-        $this->columnMaxWidths[$columnIndex] = $width;
224
+        $this->columnMaxWidths[ $columnIndex ] = $width;
225 225
 
226 226
         return $this;
227 227
     }
228 228
 
229
-    public function setHeaders(array $headers)
229
+    public function setHeaders( array $headers )
230 230
     {
231
-        $headers = array_values($headers);
232
-        if (!empty($headers) && !\is_array($headers[0])) {
233
-            $headers = [$headers];
231
+        $headers = array_values( $headers );
232
+        if ( ! empty( $headers ) && ! \is_array( $headers[ 0 ] ) ) {
233
+            $headers = [ $headers ];
234 234
         }
235 235
 
236 236
         $this->headers = $headers;
@@ -238,35 +238,35 @@  discard block
 block discarded – undo
238 238
         return $this;
239 239
     }
240 240
 
241
-    public function setRows(array $rows)
241
+    public function setRows( array $rows )
242 242
     {
243
-        $this->rows = [];
243
+        $this->rows = [ ];
244 244
 
245
-        return $this->addRows($rows);
245
+        return $this->addRows( $rows );
246 246
     }
247 247
 
248
-    public function addRows(array $rows)
248
+    public function addRows( array $rows )
249 249
     {
250
-        foreach ($rows as $row) {
251
-            $this->addRow($row);
250
+        foreach ( $rows as $row ) {
251
+            $this->addRow( $row );
252 252
         }
253 253
 
254 254
         return $this;
255 255
     }
256 256
 
257
-    public function addRow($row)
257
+    public function addRow( $row )
258 258
     {
259
-        if ($row instanceof TableSeparator) {
260
-            $this->rows[] = $row;
259
+        if ( $row instanceof TableSeparator ) {
260
+            $this->rows[ ] = $row;
261 261
 
262 262
             return $this;
263 263
         }
264 264
 
265
-        if (!\is_array($row)) {
266
-            throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.');
265
+        if ( ! \is_array( $row ) ) {
266
+            throw new InvalidArgumentException( 'A row must be an array or a TableSeparator instance.' );
267 267
         }
268 268
 
269
-        $this->rows[] = array_values($row);
269
+        $this->rows[ ] = array_values( $row );
270 270
 
271 271
         return $this;
272 272
     }
@@ -274,44 +274,44 @@  discard block
 block discarded – undo
274 274
     /**
275 275
      * Adds a row to the table, and re-renders the table.
276 276
      */
277
-    public function appendRow($row): self
277
+    public function appendRow( $row ): self
278 278
     {
279
-        if (!$this->output instanceof ConsoleSectionOutput) {
280
-            throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__));
279
+        if ( ! $this->output instanceof ConsoleSectionOutput ) {
280
+            throw new RuntimeException( sprintf( 'Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__ ) );
281 281
         }
282 282
 
283
-        if ($this->rendered) {
284
-            $this->output->clear($this->calculateRowCount());
283
+        if ( $this->rendered ) {
284
+            $this->output->clear( $this->calculateRowCount() );
285 285
         }
286 286
 
287
-        $this->addRow($row);
287
+        $this->addRow( $row );
288 288
         $this->render();
289 289
 
290 290
         return $this;
291 291
     }
292 292
 
293
-    public function setRow($column, array $row)
293
+    public function setRow( $column, array $row )
294 294
     {
295
-        $this->rows[$column] = $row;
295
+        $this->rows[ $column ] = $row;
296 296
 
297 297
         return $this;
298 298
     }
299 299
 
300
-    public function setHeaderTitle(?string $title): self
300
+    public function setHeaderTitle( ?string $title ): self
301 301
     {
302 302
         $this->headerTitle = $title;
303 303
 
304 304
         return $this;
305 305
     }
306 306
 
307
-    public function setFooterTitle(?string $title): self
307
+    public function setFooterTitle( ?string $title ): self
308 308
     {
309 309
         $this->footerTitle = $title;
310 310
 
311 311
         return $this;
312 312
     }
313 313
 
314
-    public function setHorizontal(bool $horizontal = true): self
314
+    public function setHorizontal( bool $horizontal = true ): self
315 315
     {
316 316
         $this->horizontal = $horizontal;
317 317
 
@@ -334,52 +334,52 @@  discard block
 block discarded – undo
334 334
     public function render()
335 335
     {
336 336
         $divider = new TableSeparator();
337
-        if ($this->horizontal) {
338
-            $rows = [];
339
-            foreach ($this->headers[0] ?? [] as $i => $header) {
340
-                $rows[$i] = [$header];
341
-                foreach ($this->rows as $row) {
342
-                    if ($row instanceof TableSeparator) {
337
+        if ( $this->horizontal ) {
338
+            $rows = [ ];
339
+            foreach ( $this->headers[ 0 ] ?? [ ] as $i => $header ) {
340
+                $rows[ $i ] = [ $header ];
341
+                foreach ( $this->rows as $row ) {
342
+                    if ( $row instanceof TableSeparator ) {
343 343
                         continue;
344 344
                     }
345
-                    if (isset($row[$i])) {
346
-                        $rows[$i][] = $row[$i];
347
-                    } elseif ($rows[$i][0] instanceof TableCell && $rows[$i][0]->getColspan() >= 2) {
345
+                    if ( isset( $row[ $i ] ) ) {
346
+                        $rows[ $i ][ ] = $row[ $i ];
347
+                    } elseif ( $rows[ $i ][ 0 ] instanceof TableCell && $rows[ $i ][ 0 ]->getColspan() >= 2 ) {
348 348
                         // Noop, there is a "title"
349 349
                     } else {
350
-                        $rows[$i][] = null;
350
+                        $rows[ $i ][ ] = null;
351 351
                     }
352 352
                 }
353 353
             }
354 354
         } else {
355
-            $rows = array_merge($this->headers, [$divider], $this->rows);
355
+            $rows = array_merge( $this->headers, [ $divider ], $this->rows );
356 356
         }
357 357
 
358
-        $this->calculateNumberOfColumns($rows);
358
+        $this->calculateNumberOfColumns( $rows );
359 359
 
360
-        $rows = $this->buildTableRows($rows);
361
-        $this->calculateColumnsWidth($rows);
360
+        $rows = $this->buildTableRows( $rows );
361
+        $this->calculateColumnsWidth( $rows );
362 362
 
363
-        $isHeader = !$this->horizontal;
363
+        $isHeader = ! $this->horizontal;
364 364
         $isFirstRow = $this->horizontal;
365
-        $hasTitle = (bool) $this->headerTitle;
366
-        foreach ($rows as $row) {
367
-            if ($divider === $row) {
365
+        $hasTitle = (bool)$this->headerTitle;
366
+        foreach ( $rows as $row ) {
367
+            if ( $divider === $row ) {
368 368
                 $isHeader = false;
369 369
                 $isFirstRow = true;
370 370
 
371 371
                 continue;
372 372
             }
373
-            if ($row instanceof TableSeparator) {
373
+            if ( $row instanceof TableSeparator ) {
374 374
                 $this->renderRowSeparator();
375 375
 
376 376
                 continue;
377 377
             }
378
-            if (!$row) {
378
+            if ( ! $row ) {
379 379
                 continue;
380 380
             }
381 381
 
382
-            if ($isHeader || $isFirstRow) {
382
+            if ( $isHeader || $isFirstRow ) {
383 383
                 $this->renderRowSeparator(
384 384
                     $isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM,
385 385
                     $hasTitle ? $this->headerTitle : null,
@@ -388,13 +388,13 @@  discard block
 block discarded – undo
388 388
                 $isFirstRow = false;
389 389
                 $hasTitle = false;
390 390
             }
391
-            if ($this->horizontal) {
392
-                $this->renderRow($row, $this->style->getCellRowFormat(), $this->style->getCellHeaderFormat());
391
+            if ( $this->horizontal ) {
392
+                $this->renderRow( $row, $this->style->getCellRowFormat(), $this->style->getCellHeaderFormat() );
393 393
             } else {
394
-                $this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat());
394
+                $this->renderRow( $row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat() );
395 395
             }
396 396
         }
397
-        $this->renderRowSeparator(self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat());
397
+        $this->renderRowSeparator( self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat() );
398 398
 
399 399
         $this->cleanup();
400 400
         $this->rendered = true;
@@ -407,62 +407,62 @@  discard block
 block discarded – undo
407 407
      *
408 408
      *     +-----+-----------+-------+
409 409
      */
410
-    private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null)
410
+    private function renderRowSeparator( int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null )
411 411
     {
412
-        if (0 === $count = $this->numberOfColumns) {
412
+        if ( 0 === $count = $this->numberOfColumns ) {
413 413
             return;
414 414
         }
415 415
 
416 416
         $borders = $this->style->getBorderChars();
417
-        if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) {
417
+        if ( ! $borders[ 0 ] && ! $borders[ 2 ] && ! $this->style->getCrossingChar() ) {
418 418
             return;
419 419
         }
420 420
 
421 421
         $crossings = $this->style->getCrossingChars();
422
-        if (self::SEPARATOR_MID === $type) {
423
-            [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]];
424
-        } elseif (self::SEPARATOR_TOP === $type) {
425
-            [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]];
426
-        } elseif (self::SEPARATOR_TOP_BOTTOM === $type) {
427
-            [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]];
422
+        if ( self::SEPARATOR_MID === $type ) {
423
+            [ $horizontal, $leftChar, $midChar, $rightChar ] = [ $borders[ 2 ], $crossings[ 8 ], $crossings[ 0 ], $crossings[ 4 ] ];
424
+        } elseif ( self::SEPARATOR_TOP === $type ) {
425
+            [ $horizontal, $leftChar, $midChar, $rightChar ] = [ $borders[ 0 ], $crossings[ 1 ], $crossings[ 2 ], $crossings[ 3 ] ];
426
+        } elseif ( self::SEPARATOR_TOP_BOTTOM === $type ) {
427
+            [ $horizontal, $leftChar, $midChar, $rightChar ] = [ $borders[ 0 ], $crossings[ 9 ], $crossings[ 10 ], $crossings[ 11 ] ];
428 428
         } else {
429
-            [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[7], $crossings[6], $crossings[5]];
429
+            [ $horizontal, $leftChar, $midChar, $rightChar ] = [ $borders[ 0 ], $crossings[ 7 ], $crossings[ 6 ], $crossings[ 5 ] ];
430 430
         }
431 431
 
432 432
         $markup = $leftChar;
433
-        for ($column = 0; $column < $count; ++$column) {
434
-            $markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]);
433
+        for ( $column = 0; $column < $count; ++$column ) {
434
+            $markup .= str_repeat( $horizontal, $this->effectiveColumnWidths[ $column ] );
435 435
             $markup .= $column === $count - 1 ? $rightChar : $midChar;
436 436
         }
437 437
 
438
-        if (null !== $title) {
439
-            $titleLength = Helper::width(Helper::removeDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title)));
440
-            $markupLength = Helper::width($markup);
441
-            if ($titleLength > $limit = $markupLength - 4) {
438
+        if ( null !== $title ) {
439
+            $titleLength = Helper::width( Helper::removeDecoration( $formatter = $this->output->getFormatter(), $formattedTitle = sprintf( $titleFormat, $title ) ) );
440
+            $markupLength = Helper::width( $markup );
441
+            if ( $titleLength > $limit = $markupLength - 4 ) {
442 442
                 $titleLength = $limit;
443
-                $formatLength = Helper::width(Helper::removeDecoration($formatter, sprintf($titleFormat, '')));
444
-                $formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...');
443
+                $formatLength = Helper::width( Helper::removeDecoration( $formatter, sprintf( $titleFormat, '' ) ) );
444
+                $formattedTitle = sprintf( $titleFormat, Helper::substr( $title, 0, $limit - $formatLength - 3 ) . '...' );
445 445
             }
446 446
 
447
-            $titleStart = intdiv($markupLength - $titleLength, 2);
448
-            if (false === mb_detect_encoding($markup, null, true)) {
449
-                $markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength);
447
+            $titleStart = intdiv( $markupLength - $titleLength, 2 );
448
+            if ( false === mb_detect_encoding( $markup, null, true ) ) {
449
+                $markup = substr_replace( $markup, $formattedTitle, $titleStart, $titleLength );
450 450
             } else {
451
-                $markup = mb_substr($markup, 0, $titleStart).$formattedTitle.mb_substr($markup, $titleStart + $titleLength);
451
+                $markup = mb_substr( $markup, 0, $titleStart ) . $formattedTitle . mb_substr( $markup, $titleStart + $titleLength );
452 452
             }
453 453
         }
454 454
 
455
-        $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
455
+        $this->output->writeln( sprintf( $this->style->getBorderFormat(), $markup ) );
456 456
     }
457 457
 
458 458
     /**
459 459
      * Renders vertical column separator.
460 460
      */
461
-    private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string
461
+    private function renderColumnSeparator( int $type = self::BORDER_OUTSIDE ): string
462 462
     {
463 463
         $borders = $this->style->getBorderChars();
464 464
 
465
-        return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]);
465
+        return sprintf( $this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[ 1 ] : $borders[ 3 ] );
466 466
     }
467 467
 
468 468
     /**
@@ -472,137 +472,137 @@  discard block
 block discarded – undo
472 472
      *
473 473
      *     | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
474 474
      */
475
-    private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null)
476
-    {
477
-        $rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
478
-        $columns = $this->getRowColumns($row);
479
-        $last = \count($columns) - 1;
480
-        foreach ($columns as $i => $column) {
481
-            if ($firstCellFormat && 0 === $i) {
482
-                $rowContent .= $this->renderCell($row, $column, $firstCellFormat);
475
+    private function renderRow( array $row, string $cellFormat, string $firstCellFormat = null )
476
+    {
477
+        $rowContent = $this->renderColumnSeparator( self::BORDER_OUTSIDE );
478
+        $columns = $this->getRowColumns( $row );
479
+        $last = \count( $columns ) - 1;
480
+        foreach ( $columns as $i => $column ) {
481
+            if ( $firstCellFormat && 0 === $i ) {
482
+                $rowContent .= $this->renderCell( $row, $column, $firstCellFormat );
483 483
             } else {
484
-                $rowContent .= $this->renderCell($row, $column, $cellFormat);
484
+                $rowContent .= $this->renderCell( $row, $column, $cellFormat );
485 485
             }
486
-            $rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE);
486
+            $rowContent .= $this->renderColumnSeparator( $last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE );
487 487
         }
488
-        $this->output->writeln($rowContent);
488
+        $this->output->writeln( $rowContent );
489 489
     }
490 490
 
491 491
     /**
492 492
      * Renders table cell with padding.
493 493
      */
494
-    private function renderCell(array $row, int $column, string $cellFormat): string
494
+    private function renderCell( array $row, int $column, string $cellFormat ): string
495 495
     {
496
-        $cell = $row[$column] ?? '';
497
-        $width = $this->effectiveColumnWidths[$column];
498
-        if ($cell instanceof TableCell && $cell->getColspan() > 1) {
496
+        $cell = $row[ $column ] ?? '';
497
+        $width = $this->effectiveColumnWidths[ $column ];
498
+        if ( $cell instanceof TableCell && $cell->getColspan() > 1 ) {
499 499
             // add the width of the following columns(numbers of colspan).
500
-            foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
501
-                $width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn];
500
+            foreach ( range( $column + 1, $column + $cell->getColspan() - 1 ) as $nextColumn ) {
501
+                $width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[ $nextColumn ];
502 502
             }
503 503
         }
504 504
 
505 505
         // str_pad won't work properly with multi-byte strings, we need to fix the padding
506
-        if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
507
-            $width += \strlen($cell) - mb_strwidth($cell, $encoding);
506
+        if ( false !== $encoding = mb_detect_encoding( $cell, null, true ) ) {
507
+            $width += \strlen( $cell ) - mb_strwidth( $cell, $encoding );
508 508
         }
509 509
 
510
-        $style = $this->getColumnStyle($column);
510
+        $style = $this->getColumnStyle( $column );
511 511
 
512
-        if ($cell instanceof TableSeparator) {
513
-            return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width));
512
+        if ( $cell instanceof TableSeparator ) {
513
+            return sprintf( $style->getBorderFormat(), str_repeat( $style->getBorderChars()[ 2 ], $width ) );
514 514
         }
515 515
 
516
-        $width += Helper::length($cell) - Helper::length(Helper::removeDecoration($this->output->getFormatter(), $cell));
517
-        $content = sprintf($style->getCellRowContentFormat(), $cell);
516
+        $width += Helper::length( $cell ) - Helper::length( Helper::removeDecoration( $this->output->getFormatter(), $cell ) );
517
+        $content = sprintf( $style->getCellRowContentFormat(), $cell );
518 518
 
519 519
         $padType = $style->getPadType();
520
-        if ($cell instanceof TableCell && $cell->getStyle() instanceof TableCellStyle) {
521
-            $isNotStyledByTag = !preg_match('/^<(\w+|(\w+=[\w,]+;?)*)>.+<\/(\w+|(\w+=\w+;?)*)?>$/', $cell);
522
-            if ($isNotStyledByTag) {
520
+        if ( $cell instanceof TableCell && $cell->getStyle() instanceof TableCellStyle ) {
521
+            $isNotStyledByTag = ! preg_match( '/^<(\w+|(\w+=[\w,]+;?)*)>.+<\/(\w+|(\w+=\w+;?)*)?>$/', $cell );
522
+            if ( $isNotStyledByTag ) {
523 523
                 $cellFormat = $cell->getStyle()->getCellFormat();
524
-                if (!\is_string($cellFormat)) {
525
-                    $tag = http_build_query($cell->getStyle()->getTagOptions(), '', ';');
526
-                    $cellFormat = '<'.$tag.'>%s</>';
524
+                if ( ! \is_string( $cellFormat ) ) {
525
+                    $tag = http_build_query( $cell->getStyle()->getTagOptions(), '', ';' );
526
+                    $cellFormat = '<' . $tag . '>%s</>';
527 527
                 }
528 528
 
529
-                if (strstr($content, '</>')) {
530
-                    $content = str_replace('</>', '', $content);
529
+                if ( strstr( $content, '</>' ) ) {
530
+                    $content = str_replace( '</>', '', $content );
531 531
                     $width -= 3;
532 532
                 }
533
-                if (strstr($content, '<fg=default;bg=default>')) {
534
-                    $content = str_replace('<fg=default;bg=default>', '', $content);
535
-                    $width -= \strlen('<fg=default;bg=default>');
533
+                if ( strstr( $content, '<fg=default;bg=default>' ) ) {
534
+                    $content = str_replace( '<fg=default;bg=default>', '', $content );
535
+                    $width -= \strlen( '<fg=default;bg=default>' );
536 536
                 }
537 537
             }
538 538
 
539 539
             $padType = $cell->getStyle()->getPadByAlign();
540 540
         }
541 541
 
542
-        return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $padType));
542
+        return sprintf( $cellFormat, str_pad( $content, $width, $style->getPaddingChar(), $padType ) );
543 543
     }
544 544
 
545 545
     /**
546 546
      * Calculate number of columns for this table.
547 547
      */
548
-    private function calculateNumberOfColumns(array $rows)
548
+    private function calculateNumberOfColumns( array $rows )
549 549
     {
550
-        $columns = [0];
551
-        foreach ($rows as $row) {
552
-            if ($row instanceof TableSeparator) {
550
+        $columns = [ 0 ];
551
+        foreach ( $rows as $row ) {
552
+            if ( $row instanceof TableSeparator ) {
553 553
                 continue;
554 554
             }
555 555
 
556
-            $columns[] = $this->getNumberOfColumns($row);
556
+            $columns[ ] = $this->getNumberOfColumns( $row );
557 557
         }
558 558
 
559
-        $this->numberOfColumns = max($columns);
559
+        $this->numberOfColumns = max( $columns );
560 560
     }
561 561
 
562
-    private function buildTableRows(array $rows): TableRows
562
+    private function buildTableRows( array $rows ): TableRows
563 563
     {
564 564
         /** @var WrappableOutputFormatterInterface $formatter */
565 565
         $formatter = $this->output->getFormatter();
566
-        $unmergedRows = [];
567
-        for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) {
568
-            $rows = $this->fillNextRows($rows, $rowKey);
566
+        $unmergedRows = [ ];
567
+        for ( $rowKey = 0; $rowKey < \count( $rows ); ++$rowKey ) {
568
+            $rows = $this->fillNextRows( $rows, $rowKey );
569 569
 
570 570
             // Remove any new line breaks and replace it with a new line
571
-            foreach ($rows[$rowKey] as $column => $cell) {
571
+            foreach ( $rows[ $rowKey ] as $column => $cell ) {
572 572
                 $colspan = $cell instanceof TableCell ? $cell->getColspan() : 1;
573 573
 
574
-                if (isset($this->columnMaxWidths[$column]) && Helper::width(Helper::removeDecoration($formatter, $cell)) > $this->columnMaxWidths[$column]) {
575
-                    $cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan);
574
+                if ( isset( $this->columnMaxWidths[ $column ] ) && Helper::width( Helper::removeDecoration( $formatter, $cell ) ) > $this->columnMaxWidths[ $column ] ) {
575
+                    $cell = $formatter->formatAndWrap( $cell, $this->columnMaxWidths[ $column ] * $colspan );
576 576
                 }
577
-                if (!strstr($cell ?? '', "\n")) {
577
+                if ( ! strstr( $cell ?? '', "\n" ) ) {
578 578
                     continue;
579 579
                 }
580
-                $escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell)));
581
-                $cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped;
582
-                $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
583
-                foreach ($lines as $lineKey => $line) {
584
-                    if ($colspan > 1) {
585
-                        $line = new TableCell($line, ['colspan' => $colspan]);
580
+                $escaped = implode( "\n", array_map( [ OutputFormatter::class, 'escapeTrailingBackslash' ], explode( "\n", $cell ) ) );
581
+                $cell = $cell instanceof TableCell ? new TableCell( $escaped, [ 'colspan' => $cell->getColspan() ] ) : $escaped;
582
+                $lines = explode( "\n", str_replace( "\n", "<fg=default;bg=default>\n</>", $cell ) );
583
+                foreach ( $lines as $lineKey => $line ) {
584
+                    if ( $colspan > 1 ) {
585
+                        $line = new TableCell( $line, [ 'colspan' => $colspan ] );
586 586
                     }
587
-                    if (0 === $lineKey) {
588
-                        $rows[$rowKey][$column] = $line;
587
+                    if ( 0 === $lineKey ) {
588
+                        $rows[ $rowKey ][ $column ] = $line;
589 589
                     } else {
590
-                        if (!\array_key_exists($rowKey, $unmergedRows) || !\array_key_exists($lineKey, $unmergedRows[$rowKey])) {
591
-                            $unmergedRows[$rowKey][$lineKey] = $this->copyRow($rows, $rowKey);
590
+                        if ( ! \array_key_exists( $rowKey, $unmergedRows ) || ! \array_key_exists( $lineKey, $unmergedRows[ $rowKey ] ) ) {
591
+                            $unmergedRows[ $rowKey ][ $lineKey ] = $this->copyRow( $rows, $rowKey );
592 592
                         }
593
-                        $unmergedRows[$rowKey][$lineKey][$column] = $line;
593
+                        $unmergedRows[ $rowKey ][ $lineKey ][ $column ] = $line;
594 594
                     }
595 595
                 }
596 596
             }
597 597
         }
598 598
 
599
-        return new TableRows(function () use ($rows, $unmergedRows): \Traversable {
600
-            foreach ($rows as $rowKey => $row) {
601
-                yield $row instanceof TableSeparator ? $row : $this->fillCells($row);
599
+        return new TableRows( function() use ( $rows, $unmergedRows ): \Traversable {
600
+            foreach ( $rows as $rowKey => $row ) {
601
+                yield $row instanceof TableSeparator ? $row : $this->fillCells( $row );
602 602
 
603
-                if (isset($unmergedRows[$rowKey])) {
604
-                    foreach ($unmergedRows[$rowKey] as $row) {
605
-                        yield $row instanceof TableSeparator ? $row : $this->fillCells($row);
603
+                if ( isset( $unmergedRows[ $rowKey ] ) ) {
604
+                    foreach ( $unmergedRows[ $rowKey ] as $row ) {
605
+                        yield $row instanceof TableSeparator ? $row : $this->fillCells( $row );
606 606
                     }
607 607
                 }
608 608
             }
@@ -611,13 +611,13 @@  discard block
 block discarded – undo
611 611
 
612 612
     private function calculateRowCount(): int
613 613
     {
614
-        $numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows))));
614
+        $numberOfRows = \count( iterator_to_array( $this->buildTableRows( array_merge( $this->headers, [ new TableSeparator() ], $this->rows ) ) ) );
615 615
 
616
-        if ($this->headers) {
616
+        if ( $this->headers ) {
617 617
             ++$numberOfRows; // Add row for header separator
618 618
         }
619 619
 
620
-        if (\count($this->rows) > 0) {
620
+        if ( \count( $this->rows ) > 0 ) {
621 621
             ++$numberOfRows; // Add row for footer separator
622 622
         }
623 623
 
@@ -629,51 +629,51 @@  discard block
 block discarded – undo
629 629
      *
630 630
      * @throws InvalidArgumentException
631 631
      */
632
-    private function fillNextRows(array $rows, int $line): array
632
+    private function fillNextRows( array $rows, int $line ): array
633 633
     {
634
-        $unmergedRows = [];
635
-        foreach ($rows[$line] as $column => $cell) {
636
-            if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
637
-                throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell)));
634
+        $unmergedRows = [ ];
635
+        foreach ( $rows[ $line ] as $column => $cell ) {
636
+            if ( null !== $cell && ! $cell instanceof TableCell && ! is_scalar( $cell ) && ! ( \is_object( $cell ) && method_exists( $cell, '__toString' ) ) ) {
637
+                throw new InvalidArgumentException( sprintf( 'A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type( $cell ) ) );
638 638
             }
639
-            if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
639
+            if ( $cell instanceof TableCell && $cell->getRowspan() > 1 ) {
640 640
                 $nbLines = $cell->getRowspan() - 1;
641
-                $lines = [$cell];
642
-                if (strstr($cell, "\n")) {
643
-                    $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
644
-                    $nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;
641
+                $lines = [ $cell ];
642
+                if ( strstr( $cell, "\n" ) ) {
643
+                    $lines = explode( "\n", str_replace( "\n", "<fg=default;bg=default>\n</>", $cell ) );
644
+                    $nbLines = \count( $lines ) > $nbLines ? substr_count( $cell, "\n" ) : $nbLines;
645 645
 
646
-                    $rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
647
-                    unset($lines[0]);
646
+                    $rows[ $line ][ $column ] = new TableCell( $lines[ 0 ], [ 'colspan' => $cell->getColspan(), 'style' => $cell->getStyle() ] );
647
+                    unset( $lines[ 0 ] );
648 648
                 }
649 649
 
650 650
                 // create a two dimensional array (rowspan x colspan)
651
-                $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows);
652
-                foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
653
-                    $value = $lines[$unmergedRowKey - $line] ?? '';
654
-                    $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
655
-                    if ($nbLines === $unmergedRowKey - $line) {
651
+                $unmergedRows = array_replace_recursive( array_fill( $line + 1, $nbLines, [ ] ), $unmergedRows );
652
+                foreach ( $unmergedRows as $unmergedRowKey => $unmergedRow ) {
653
+                    $value = $lines[ $unmergedRowKey - $line ] ?? '';
654
+                    $unmergedRows[ $unmergedRowKey ][ $column ] = new TableCell( $value, [ 'colspan' => $cell->getColspan(), 'style' => $cell->getStyle() ] );
655
+                    if ( $nbLines === $unmergedRowKey - $line ) {
656 656
                         break;
657 657
                     }
658 658
                 }
659 659
             }
660 660
         }
661 661
 
662
-        foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
662
+        foreach ( $unmergedRows as $unmergedRowKey => $unmergedRow ) {
663 663
             // we need to know if $unmergedRow will be merged or inserted into $rows
664
-            if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) {
665
-                foreach ($unmergedRow as $cellKey => $cell) {
664
+            if ( isset( $rows[ $unmergedRowKey ] ) && \is_array( $rows[ $unmergedRowKey ] ) && ( $this->getNumberOfColumns( $rows[ $unmergedRowKey ] ) + $this->getNumberOfColumns( $unmergedRows[ $unmergedRowKey ] ) <= $this->numberOfColumns ) ) {
665
+                foreach ( $unmergedRow as $cellKey => $cell ) {
666 666
                     // insert cell into row at cellKey position
667
-                    array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]);
667
+                    array_splice( $rows[ $unmergedRowKey ], $cellKey, 0, [ $cell ] );
668 668
                 }
669 669
             } else {
670
-                $row = $this->copyRow($rows, $unmergedRowKey - 1);
671
-                foreach ($unmergedRow as $column => $cell) {
672
-                    if (!empty($cell)) {
673
-                        $row[$column] = $unmergedRow[$column];
670
+                $row = $this->copyRow( $rows, $unmergedRowKey - 1 );
671
+                foreach ( $unmergedRow as $column => $cell ) {
672
+                    if ( ! empty( $cell ) ) {
673
+                        $row[ $column ] = $unmergedRow[ $column ];
674 674
                     }
675 675
                 }
676
-                array_splice($rows, $unmergedRowKey, 0, [$row]);
676
+                array_splice( $rows, $unmergedRowKey, 0, [ $row ] );
677 677
             }
678 678
         }
679 679
 
@@ -683,16 +683,16 @@  discard block
 block discarded – undo
683 683
     /**
684 684
      * fill cells for a row that contains colspan > 1.
685 685
      */
686
-    private function fillCells(iterable $row)
686
+    private function fillCells( iterable $row )
687 687
     {
688
-        $newRow = [];
688
+        $newRow = [ ];
689 689
 
690
-        foreach ($row as $column => $cell) {
691
-            $newRow[] = $cell;
692
-            if ($cell instanceof TableCell && $cell->getColspan() > 1) {
693
-                foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
690
+        foreach ( $row as $column => $cell ) {
691
+            $newRow[ ] = $cell;
692
+            if ( $cell instanceof TableCell && $cell->getColspan() > 1 ) {
693
+                foreach ( range( $column + 1, $column + $cell->getColspan() - 1 ) as $position ) {
694 694
                     // insert empty value at column position
695
-                    $newRow[] = '';
695
+                    $newRow[ ] = '';
696 696
                 }
697 697
             }
698 698
         }
@@ -700,13 +700,13 @@  discard block
 block discarded – undo
700 700
         return $newRow ?: $row;
701 701
     }
702 702
 
703
-    private function copyRow(array $rows, int $line): array
703
+    private function copyRow( array $rows, int $line ): array
704 704
     {
705
-        $row = $rows[$line];
706
-        foreach ($row as $cellKey => $cellValue) {
707
-            $row[$cellKey] = '';
708
-            if ($cellValue instanceof TableCell) {
709
-                $row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]);
705
+        $row = $rows[ $line ];
706
+        foreach ( $row as $cellKey => $cellValue ) {
707
+            $row[ $cellKey ] = '';
708
+            if ( $cellValue instanceof TableCell ) {
709
+                $row[ $cellKey ] = new TableCell( '', [ 'colspan' => $cellValue->getColspan() ] );
710 710
             }
711 711
         }
712 712
 
@@ -716,11 +716,11 @@  discard block
 block discarded – undo
716 716
     /**
717 717
      * Gets number of columns by row.
718 718
      */
719
-    private function getNumberOfColumns(array $row): int
719
+    private function getNumberOfColumns( array $row ): int
720 720
     {
721
-        $columns = \count($row);
722
-        foreach ($row as $column) {
723
-            $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0;
721
+        $columns = \count( $row );
722
+        foreach ( $row as $column ) {
723
+            $columns += $column instanceof TableCell ? ( $column->getColspan() - 1 ) : 0;
724 724
         }
725 725
 
726 726
         return $columns;
@@ -729,13 +729,13 @@  discard block
 block discarded – undo
729 729
     /**
730 730
      * Gets list of columns for the given row.
731 731
      */
732
-    private function getRowColumns(array $row): array
732
+    private function getRowColumns( array $row ): array
733 733
     {
734
-        $columns = range(0, $this->numberOfColumns - 1);
735
-        foreach ($row as $cellKey => $cell) {
736
-            if ($cell instanceof TableCell && $cell->getColspan() > 1) {
734
+        $columns = range( 0, $this->numberOfColumns - 1 );
735
+        foreach ( $row as $cellKey => $cell ) {
736
+            if ( $cell instanceof TableCell && $cell->getColspan() > 1 ) {
737 737
                 // exclude grouped columns.
738
-                $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1));
738
+                $columns = array_diff( $columns, range( $cellKey + 1, $cellKey + $cell->getColspan() - 1 ) );
739 739
             }
740 740
         }
741 741
 
@@ -745,53 +745,53 @@  discard block
 block discarded – undo
745 745
     /**
746 746
      * Calculates columns widths.
747 747
      */
748
-    private function calculateColumnsWidth(iterable $rows)
748
+    private function calculateColumnsWidth( iterable $rows )
749 749
     {
750
-        for ($column = 0; $column < $this->numberOfColumns; ++$column) {
751
-            $lengths = [];
752
-            foreach ($rows as $row) {
753
-                if ($row instanceof TableSeparator) {
750
+        for ( $column = 0; $column < $this->numberOfColumns; ++$column ) {
751
+            $lengths = [ ];
752
+            foreach ( $rows as $row ) {
753
+                if ( $row instanceof TableSeparator ) {
754 754
                     continue;
755 755
                 }
756 756
 
757
-                foreach ($row as $i => $cell) {
758
-                    if ($cell instanceof TableCell) {
759
-                        $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
760
-                        $textLength = Helper::width($textContent);
761
-                        if ($textLength > 0) {
762
-                            $contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
763
-                            foreach ($contentColumns as $position => $content) {
764
-                                $row[$i + $position] = $content;
757
+                foreach ( $row as $i => $cell ) {
758
+                    if ( $cell instanceof TableCell ) {
759
+                        $textContent = Helper::removeDecoration( $this->output->getFormatter(), $cell );
760
+                        $textLength = Helper::width( $textContent );
761
+                        if ( $textLength > 0 ) {
762
+                            $contentColumns = str_split( $textContent, ceil( $textLength / $cell->getColspan() ) );
763
+                            foreach ( $contentColumns as $position => $content ) {
764
+                                $row[ $i + $position ] = $content;
765 765
                             }
766 766
                         }
767 767
                     }
768 768
                 }
769 769
 
770
-                $lengths[] = $this->getCellWidth($row, $column);
770
+                $lengths[ ] = $this->getCellWidth( $row, $column );
771 771
             }
772 772
 
773
-            $this->effectiveColumnWidths[$column] = max($lengths) + Helper::width($this->style->getCellRowContentFormat()) - 2;
773
+            $this->effectiveColumnWidths[ $column ] = max( $lengths ) + Helper::width( $this->style->getCellRowContentFormat() ) - 2;
774 774
         }
775 775
     }
776 776
 
777 777
     private function getColumnSeparatorWidth(): int
778 778
     {
779
-        return Helper::width(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3]));
779
+        return Helper::width( sprintf( $this->style->getBorderFormat(), $this->style->getBorderChars()[ 3 ] ) );
780 780
     }
781 781
 
782
-    private function getCellWidth(array $row, int $column): int
782
+    private function getCellWidth( array $row, int $column ): int
783 783
     {
784 784
         $cellWidth = 0;
785 785
 
786
-        if (isset($row[$column])) {
787
-            $cell = $row[$column];
788
-            $cellWidth = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $cell));
786
+        if ( isset( $row[ $column ] ) ) {
787
+            $cell = $row[ $column ];
788
+            $cellWidth = Helper::width( Helper::removeDecoration( $this->output->getFormatter(), $cell ) );
789 789
         }
790 790
 
791
-        $columnWidth = $this->columnWidths[$column] ?? 0;
792
-        $cellWidth = max($cellWidth, $columnWidth);
791
+        $columnWidth = $this->columnWidths[ $column ] ?? 0;
792
+        $cellWidth = max( $cellWidth, $columnWidth );
793 793
 
794
-        return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth;
794
+        return isset( $this->columnMaxWidths[ $column ] ) ? min( $this->columnMaxWidths[ $column ], $cellWidth ) : $cellWidth;
795 795
     }
796 796
 
797 797
     /**
@@ -799,7 +799,7 @@  discard block
 block discarded – undo
799 799
      */
800 800
     private function cleanup()
801 801
     {
802
-        $this->effectiveColumnWidths = [];
802
+        $this->effectiveColumnWidths = [ ];
803 803
         $this->numberOfColumns = null;
804 804
     }
805 805
 
@@ -807,37 +807,37 @@  discard block
 block discarded – undo
807 807
     {
808 808
         $borderless = new TableStyle();
809 809
         $borderless
810
-            ->setHorizontalBorderChars('=')
811
-            ->setVerticalBorderChars(' ')
812
-            ->setDefaultCrossingChar(' ')
810
+            ->setHorizontalBorderChars( '=' )
811
+            ->setVerticalBorderChars( ' ' )
812
+            ->setDefaultCrossingChar( ' ' )
813 813
         ;
814 814
 
815 815
         $compact = new TableStyle();
816 816
         $compact
817
-            ->setHorizontalBorderChars('')
818
-            ->setVerticalBorderChars(' ')
819
-            ->setDefaultCrossingChar('')
820
-            ->setCellRowContentFormat('%s')
817
+            ->setHorizontalBorderChars( '' )
818
+            ->setVerticalBorderChars( ' ' )
819
+            ->setDefaultCrossingChar( '' )
820
+            ->setCellRowContentFormat( '%s' )
821 821
         ;
822 822
 
823 823
         $styleGuide = new TableStyle();
824 824
         $styleGuide
825
-            ->setHorizontalBorderChars('-')
826
-            ->setVerticalBorderChars(' ')
827
-            ->setDefaultCrossingChar(' ')
828
-            ->setCellHeaderFormat('%s')
825
+            ->setHorizontalBorderChars( '-' )
826
+            ->setVerticalBorderChars( ' ' )
827
+            ->setDefaultCrossingChar( ' ' )
828
+            ->setCellHeaderFormat( '%s' )
829 829
         ;
830 830
 
831
-        $box = (new TableStyle())
832
-            ->setHorizontalBorderChars('─')
833
-            ->setVerticalBorderChars('│')
834
-            ->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├')
831
+        $box = ( new TableStyle() )
832
+            ->setHorizontalBorderChars( '─' )
833
+            ->setVerticalBorderChars( '│' )
834
+            ->setCrossingChars( '┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├' )
835 835
         ;
836 836
 
837
-        $boxDouble = (new TableStyle())
838
-            ->setHorizontalBorderChars('═', '─')
839
-            ->setVerticalBorderChars('║', '│')
840
-            ->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣')
837
+        $boxDouble = ( new TableStyle() )
838
+            ->setHorizontalBorderChars( '═', '─' )
839
+            ->setVerticalBorderChars( '║', '│' )
840
+            ->setCrossingChars( '┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣' )
841 841
         ;
842 842
 
843 843
         return [
@@ -850,16 +850,16 @@  discard block
 block discarded – undo
850 850
         ];
851 851
     }
852 852
 
853
-    private function resolveStyle($name): TableStyle
853
+    private function resolveStyle( $name ): TableStyle
854 854
     {
855
-        if ($name instanceof TableStyle) {
855
+        if ( $name instanceof TableStyle ) {
856 856
             return $name;
857 857
         }
858 858
 
859
-        if (isset(self::$styles[$name])) {
860
-            return self::$styles[$name];
859
+        if ( isset( self::$styles[ $name ] ) ) {
860
+            return self::$styles[ $name ];
861 861
         }
862 862
 
863
-        throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
863
+        throw new InvalidArgumentException( sprintf( 'Style "%s" is not defined.', $name ) );
864 864
     }
865 865
 }
Please login to merge, or discard this patch.
Braces   +22 added lines, -44 removed lines patch added patch discarded remove patch
@@ -27,8 +27,7 @@  discard block
 block discarded – undo
27 27
  * @author Max Grigorian <[email protected]>
28 28
  * @author Dany Maillard <[email protected]>
29 29
  */
30
-class Table
31
-{
30
+class Table {
32 31
     private const SEPARATOR_TOP = 0;
33 32
     private const SEPARATOR_TOP_BOTTOM = 1;
34 33
     private const SEPARATOR_MID = 2;
@@ -89,8 +88,7 @@  discard block
 block discarded – undo
89 88
 
90 89
     private $rendered = false;
91 90
 
92
-    public function __construct(OutputInterface $output)
93
-    {
91
+    public function __construct(OutputInterface $output) {
94 92
         $this->output = $output;
95 93
 
96 94
         if (!self::$styles) {
@@ -103,8 +101,7 @@  discard block
 block discarded – undo
103 101
     /**
104 102
      * Sets a style definition.
105 103
      */
106
-    public static function setStyleDefinition(string $name, TableStyle $style)
107
-    {
104
+    public static function setStyleDefinition(string $name, TableStyle $style) {
108 105
         if (!self::$styles) {
109 106
             self::$styles = self::initStyles();
110 107
         }
@@ -117,8 +114,7 @@  discard block
 block discarded – undo
117 114
      *
118 115
      * @return TableStyle
119 116
      */
120
-    public static function getStyleDefinition(string $name)
121
-    {
117
+    public static function getStyleDefinition(string $name) {
122 118
         if (!self::$styles) {
123 119
             self::$styles = self::initStyles();
124 120
         }
@@ -137,8 +133,7 @@  discard block
 block discarded – undo
137 133
      *
138 134
      * @return $this
139 135
      */
140
-    public function setStyle($name)
141
-    {
136
+    public function setStyle($name) {
142 137
         $this->style = $this->resolveStyle($name);
143 138
 
144 139
         return $this;
@@ -149,8 +144,7 @@  discard block
 block discarded – undo
149 144
      *
150 145
      * @return TableStyle
151 146
      */
152
-    public function getStyle()
153
-    {
147
+    public function getStyle() {
154 148
         return $this->style;
155 149
     }
156 150
 
@@ -161,8 +155,7 @@  discard block
 block discarded – undo
161 155
      *
162 156
      * @return $this
163 157
      */
164
-    public function setColumnStyle(int $columnIndex, $name)
165
-    {
158
+    public function setColumnStyle(int $columnIndex, $name) {
166 159
         $this->columnStyles[$columnIndex] = $this->resolveStyle($name);
167 160
 
168 161
         return $this;
@@ -175,8 +168,7 @@  discard block
 block discarded – undo
175 168
      *
176 169
      * @return TableStyle
177 170
      */
178
-    public function getColumnStyle(int $columnIndex)
179
-    {
171
+    public function getColumnStyle(int $columnIndex) {
180 172
         return $this->columnStyles[$columnIndex] ?? $this->getStyle();
181 173
     }
182 174
 
@@ -185,8 +177,7 @@  discard block
 block discarded – undo
185 177
      *
186 178
      * @return $this
187 179
      */
188
-    public function setColumnWidth(int $columnIndex, int $width)
189
-    {
180
+    public function setColumnWidth(int $columnIndex, int $width) {
190 181
         $this->columnWidths[$columnIndex] = $width;
191 182
 
192 183
         return $this;
@@ -197,8 +188,7 @@  discard block
 block discarded – undo
197 188
      *
198 189
      * @return $this
199 190
      */
200
-    public function setColumnWidths(array $widths)
201
-    {
191
+    public function setColumnWidths(array $widths) {
202 192
         $this->columnWidths = [];
203 193
         foreach ($widths as $index => $width) {
204 194
             $this->setColumnWidth($index, $width);
@@ -226,8 +216,7 @@  discard block
 block discarded – undo
226 216
         return $this;
227 217
     }
228 218
 
229
-    public function setHeaders(array $headers)
230
-    {
219
+    public function setHeaders(array $headers) {
231 220
         $headers = array_values($headers);
232 221
         if (!empty($headers) && !\is_array($headers[0])) {
233 222
             $headers = [$headers];
@@ -238,15 +227,13 @@  discard block
 block discarded – undo
238 227
         return $this;
239 228
     }
240 229
 
241
-    public function setRows(array $rows)
242
-    {
230
+    public function setRows(array $rows) {
243 231
         $this->rows = [];
244 232
 
245 233
         return $this->addRows($rows);
246 234
     }
247 235
 
248
-    public function addRows(array $rows)
249
-    {
236
+    public function addRows(array $rows) {
250 237
         foreach ($rows as $row) {
251 238
             $this->addRow($row);
252 239
         }
@@ -254,8 +241,7 @@  discard block
 block discarded – undo
254 241
         return $this;
255 242
     }
256 243
 
257
-    public function addRow($row)
258
-    {
244
+    public function addRow($row) {
259 245
         if ($row instanceof TableSeparator) {
260 246
             $this->rows[] = $row;
261 247
 
@@ -290,8 +276,7 @@  discard block
 block discarded – undo
290 276
         return $this;
291 277
     }
292 278
 
293
-    public function setRow($column, array $row)
294
-    {
279
+    public function setRow($column, array $row) {
295 280
         $this->rows[$column] = $row;
296 281
 
297 282
         return $this;
@@ -331,8 +316,7 @@  discard block
 block discarded – undo
331 316
      *     | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
332 317
      *     +---------------+-----------------------+------------------+
333 318
      */
334
-    public function render()
335
-    {
319
+    public function render() {
336 320
         $divider = new TableSeparator();
337 321
         if ($this->horizontal) {
338 322
             $rows = [];
@@ -407,8 +391,7 @@  discard block
 block discarded – undo
407 391
      *
408 392
      *     +-----+-----------+-------+
409 393
      */
410
-    private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null)
411
-    {
394
+    private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null) {
412 395
         if (0 === $count = $this->numberOfColumns) {
413 396
             return;
414 397
         }
@@ -472,8 +455,7 @@  discard block
 block discarded – undo
472 455
      *
473 456
      *     | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
474 457
      */
475
-    private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null)
476
-    {
458
+    private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null) {
477 459
         $rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
478 460
         $columns = $this->getRowColumns($row);
479 461
         $last = \count($columns) - 1;
@@ -545,8 +527,7 @@  discard block
 block discarded – undo
545 527
     /**
546 528
      * Calculate number of columns for this table.
547 529
      */
548
-    private function calculateNumberOfColumns(array $rows)
549
-    {
530
+    private function calculateNumberOfColumns(array $rows) {
550 531
         $columns = [0];
551 532
         foreach ($rows as $row) {
552 533
             if ($row instanceof TableSeparator) {
@@ -683,8 +664,7 @@  discard block
 block discarded – undo
683 664
     /**
684 665
      * fill cells for a row that contains colspan > 1.
685 666
      */
686
-    private function fillCells(iterable $row)
687
-    {
667
+    private function fillCells(iterable $row) {
688 668
         $newRow = [];
689 669
 
690 670
         foreach ($row as $column => $cell) {
@@ -745,8 +725,7 @@  discard block
 block discarded – undo
745 725
     /**
746 726
      * Calculates columns widths.
747 727
      */
748
-    private function calculateColumnsWidth(iterable $rows)
749
-    {
728
+    private function calculateColumnsWidth(iterable $rows) {
750 729
         for ($column = 0; $column < $this->numberOfColumns; ++$column) {
751 730
             $lengths = [];
752 731
             foreach ($rows as $row) {
@@ -797,8 +776,7 @@  discard block
 block discarded – undo
797 776
     /**
798 777
      * Called after rendering to cleanup cache data.
799 778
      */
800
-    private function cleanup()
801
-    {
779
+    private function cleanup() {
802 780
         $this->effectiveColumnWidths = [];
803 781
         $this->numberOfColumns = null;
804 782
     }
Please login to merge, or discard this patch.
vendor/symfony/console/Helper/TableSeparator.php 3 patches
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -18,8 +18,8 @@
 block discarded – undo
18 18
  */
19 19
 class TableSeparator extends TableCell
20 20
 {
21
-    public function __construct(array $options = [])
22
-    {
23
-        parent::__construct('', $options);
24
-    }
21
+	public function __construct(array $options = [])
22
+	{
23
+		parent::__construct('', $options);
24
+	}
25 25
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -18,8 +18,8 @@
 block discarded – undo
18 18
  */
19 19
 class TableSeparator extends TableCell
20 20
 {
21
-    public function __construct(array $options = [])
21
+    public function __construct( array $options = [ ] )
22 22
     {
23
-        parent::__construct('', $options);
23
+        parent::__construct( '', $options );
24 24
     }
25 25
 }
Please login to merge, or discard this patch.
Braces   +2 added lines, -4 removed lines patch added patch discarded remove patch
@@ -16,10 +16,8 @@
 block discarded – undo
16 16
  *
17 17
  * @author Fabien Potencier <[email protected]>
18 18
  */
19
-class TableSeparator extends TableCell
20
-{
21
-    public function __construct(array $options = [])
22
-    {
19
+class TableSeparator extends TableCell {
20
+    public function __construct(array $options = []) {
23 21
         parent::__construct('', $options);
24 22
     }
25 23
 }
Please login to merge, or discard this patch.
vendor/symfony/console/Helper/InputAwareHelper.php 3 patches
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -21,13 +21,13 @@
 block discarded – undo
21 21
  */
22 22
 abstract class InputAwareHelper extends Helper implements InputAwareInterface
23 23
 {
24
-    protected $input;
24
+	protected $input;
25 25
 
26
-    /**
27
-     * {@inheritdoc}
28
-     */
29
-    public function setInput(InputInterface $input)
30
-    {
31
-        $this->input = $input;
32
-    }
26
+	/**
27
+	 * {@inheritdoc}
28
+	 */
29
+	public function setInput(InputInterface $input)
30
+	{
31
+		$this->input = $input;
32
+	}
33 33
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -26,7 +26,7 @@
 block discarded – undo
26 26
     /**
27 27
      * {@inheritdoc}
28 28
      */
29
-    public function setInput(InputInterface $input)
29
+    public function setInput( InputInterface $input )
30 30
     {
31 31
         $this->input = $input;
32 32
     }
Please login to merge, or discard this patch.
Braces   +2 added lines, -4 removed lines patch added patch discarded remove patch
@@ -19,15 +19,13 @@
 block discarded – undo
19 19
  *
20 20
  * @author Wouter J <[email protected]>
21 21
  */
22
-abstract class InputAwareHelper extends Helper implements InputAwareInterface
23
-{
22
+abstract class InputAwareHelper extends Helper implements InputAwareInterface {
24 23
     protected $input;
25 24
 
26 25
     /**
27 26
      * {@inheritdoc}
28 27
      */
29
-    public function setInput(InputInterface $input)
30
-    {
28
+    public function setInput(InputInterface $input) {
31 29
         $this->input = $input;
32 30
     }
33 31
 }
Please login to merge, or discard this patch.