Repository navigation
Expand file tree
/
Copy pathInputOutput.php
More file actions
125 lines (105 loc) · 3.79 KB
/
Copy pathInputOutput.php
File metadata and controls
125 lines (105 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<?php
declare(strict_types=1);
/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace CodeIgniter\CLI;
/**
* Input and Output for CLI.
*/
class InputOutput
{
/**
* Is the readline library on the system?
*/
private readonly bool $readlineSupport;
public function __construct()
{
// Readline is an extension for PHP that makes interactivity with PHP
// much more bash-like.
// http://www.php.net/manual/en/readline.installation.php
$this->readlineSupport = extension_loaded('readline');
}
/**
* Get input from the shell, using readline or the standard STDIN
*
* Named options must be in the following formats:
* php index.php user -v --v -name=John --name=John
*
* @param string|null $prefix You may specify a string with which to prompt the user.
*/
public function input(?string $prefix = null): string
{
// readline() can't be tested.
if ($this->readlineSupport && ENVIRONMENT !== 'testing') {
// @codeCoverageIgnoreStart
$prompt = $this->readlinePrompt($prefix, readline_info('library_version'));
if ($prompt === null) {
// The library cannot render the prompt, so write it ourselves and let readline() only read the line.
self::fwrite(STDOUT, $prefix ?? '');
}
$input = readline($prompt);
return $input === false ? '' : $input;
// @codeCoverageIgnoreEnd
}
// self:: skips MockInputOutput's fwrite override, whose filter bookkeeping must not nest inside input().
self::fwrite(STDOUT, $prefix ?? '');
$input = fgets(fopen('php://stdin', 'rb'));
if ($input === false) {
return '';
}
return $input;
}
/**
* While the library is intended for use on CLI commands,
* commands can be called from controllers and elsewhere
* so we need a way to allow them to still work.
*
* For now, just echo the content, but look into a better
* solution down the road.
*
* @param resource $handle
*/
public function fwrite($handle, string $string): void
{
if (! is_cli()) {
echo $string;
return;
}
fwrite($handle, $string);
}
/**
* Builds the prompt handed to readline(), or returns null when the caller has to write
* the prompt to STDOUT itself because the line-editing library cannot render it.
*
* GNU readline gets ANSI sequences wrapped in its non-printing markers so line-redraw
* column accounting skips them. Libedit reports "EditLine wrapper" and mangles the
* markers, so it gets the raw prompt. Windows builds use WinEditLine, which prints ANSI
* sequences literally, and readline_info() omits the version there (php-src guards it
* with #ifndef PHP_WIN32), so they get no prompt.
*
* @param mixed $libraryVersion The value of readline_info('library_version')
*/
private function readlinePrompt(?string $prefix, mixed $libraryVersion): ?string
{
if ($prefix === null || ! is_string($libraryVersion)) {
return null;
}
if (str_contains($libraryVersion, 'EditLine')) {
return $prefix;
}
return $this->markAnsiNonPrinting($prefix);
}
/**
* Wraps ANSI escape sequences in readline's non-printing markers so line-redraw column accounting skips them.
*/
private function markAnsiNonPrinting(string $prefix): string
{
return preg_replace('/(\e\[[0-9;]*m)/', "\x01\$1\x02", $prefix);
}
}