Skip to content

Commit b278081

Browse files
authored
chore: Merge pull request #39 from WebFiori/dev
Release-As: v2.1.0
2 parents d7772d9 + 69e05c0 commit b278081

File tree

31 files changed

+1874
-14
lines changed

31 files changed

+1874
-14
lines changed

.github/workflows/php81.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ on:
88
jobs:
99
test:
1010
name: Run Tests
11-
uses: WebFiori/workflows/.github/workflows/test-php.yaml@main
11+
uses: WebFiori/workflows/.github/workflows/test-php.yaml@v1.2.1
1212
with:
1313
php-version: '8.1'
1414

1515
code-coverage:
1616
name: Coverage
1717
needs: test
18-
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@main
18+
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@v1.2.1
1919
with:
2020
php-version: '8.1'
2121
coverage-file: 'php-8.1-coverage.xml'

.github/workflows/php82.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ on:
88
jobs:
99
test:
1010
name: Run Tests
11-
uses: WebFiori/workflows/.github/workflows/test-php.yaml@main
11+
uses: WebFiori/workflows/.github/workflows/test-php.yaml@v1.2.1
1212
with:
1313
php-version: '8.2'
1414
phpunit-config: "tests/phpunit10.xml"
1515

1616
code-coverage:
1717
name: Coverage
1818
needs: test
19-
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@main
19+
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@v1.2.1
2020
with:
2121
php-version: '8.2'
2222
coverage-file: 'php-8.2-coverage.xml'

.github/workflows/php83.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212

1313
test:
1414
name: Run Tests
15-
uses: WebFiori/workflows/.github/workflows/test-php.yaml@main
15+
uses: WebFiori/workflows/.github/workflows/test-php.yaml@v1.2.1
1616
with:
1717
php-version: '8.3'
1818
phpunit-config: 'tests/phpunit10.xml'
@@ -21,7 +21,7 @@ jobs:
2121
code-coverage:
2222
name: Coverage
2323
needs: test
24-
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@main
24+
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@v1.2.1
2525
with:
2626
php-version: '8.3'
2727
coverage-file: 'php-8.3-coverage.xml'

.github/workflows/php84.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ on:
88
jobs:
99
test:
1010
name: Run Tests
11-
uses: WebFiori/workflows/.github/workflows/test-php.yaml@main
11+
uses: WebFiori/workflows/.github/workflows/test-php.yaml@v1.2.1
1212
with:
1313
php-version: '8.4'
1414
phpunit-config: "tests/phpunit10.xml"
1515

1616
code-coverage:
1717
name: Coverage
1818
needs: test
19-
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@main
19+
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@v1.2.1
2020
with:
2121
php-version: '8.4'
2222
coverage-file: 'php-8.4-coverage.xml'

.github/workflows/php85.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Build PHP 8.5
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
jobs:
9+
test:
10+
name: Run Tests
11+
uses: WebFiori/workflows/.github/workflows/test-php.yaml@v1.2.1
12+
with:
13+
php-version: '8.5'
14+
phpunit-config: "tests/phpunit10.xml"
15+
16+
code-coverage:
17+
name: Coverage
18+
needs: test
19+
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@v1.2.1
20+
with:
21+
php-version: '8.5'
22+
coverage-file: 'php-8.5-coverage.xml'
23+
secrets:
24+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
25+
26+
27+

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
Class library that can help in writing command line based applications with minimum dependencies using PHP.
33

44
<p align="center">
5-
<a target="_blank" href="https://github.com/WebFiori/cli/actions/workflows/php84.yaml">
6-
<img src="https://github.com/WebFiori/cli/actions/workflows/php84.yaml/badge.svg?branch=main">
5+
<a target="_blank" href="https://github.com/WebFiori/cli/actions/workflows/php85.yaml">
6+
<img src="https://github.com/WebFiori/cli/actions/workflows/php85.yaml/badge.svg?branch=main">
77
</a>
88
<a href="https://codecov.io/gh/WebFiori/cli">
99
<img src="https://codecov.io/gh/WebFiori/cli/branch/main/graph/badge.svg" />
@@ -56,6 +56,7 @@ Class library that can help in writing command line based applications with mini
5656
| <a target="_blank" href="https://github.com/WebFiori/cli/actions/workflows/php82.yaml"><img src="https://github.com/WebFiori/cli/actions/workflows/php82.yaml/badge.svg?branch=main"></a> |
5757
| <a target="_blank" href="https://github.com/WebFiori/cli/actions/workflows/php83.yaml"><img src="https://github.com/WebFiori/cli/actions/workflows/php83.yaml/badge.svg?branch=main"></a> |
5858
| <a target="_blank" href="https://github.com/WebFiori/cli/actions/workflows/php84.yaml"><img src="https://github.com/WebFiori/cli/actions/workflows/php84.yaml/badge.svg?branch=main"></a> |
59+
| <a target="_blank" href="https://github.com/WebFiori/cli/actions/workflows/php85.yaml"><img src="https://github.com/WebFiori/cli/actions/workflows/php85.yaml/badge.svg?branch=main"></a> |
5960

6061
## Features
6162
* **Easy Command Creation**: Simple class-based approach to building CLI commands

WebFiori/Cli/Command.php

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,56 @@ public function getInput(string $prompt, ?string $default = null, ?InputValidato
579579
return null;
580580
}
581581
/**
582+
* Reads user input with characters masked by a specified character.
583+
*
584+
* This method is similar to getInput() but masks the input characters as the user types,
585+
* making it suitable for sensitive information like passwords, tokens, or secrets.
586+
* The actual input value is captured but only mask characters are displayed in the terminal.
587+
*
588+
* @param string $prompt The prompt message to display to the user. Must be non-empty.
589+
*
590+
* @param string $mask The character to display instead of the actual input characters.
591+
* Default is '*'. Can be any single character or string.
592+
*
593+
* @param string|null $default An optional default value to use if the user provides
594+
* empty input. If provided, it will be shown in the prompt.
595+
*
596+
* @param InputValidator|null $validator An optional validator to validate the input.
597+
* If validation fails, the user will be prompted again.
598+
*
599+
* @return string|null Returns the actual input value (not masked) if valid input is provided,
600+
* or null if the prompt is empty.
601+
*
602+
* @since 1.1.0
603+
*/
604+
public function getMaskedInput(string $prompt, string $mask = '*', ?string $default = null, ?InputValidator $validator = null): ?string {
605+
$trimmed = trim($prompt);
606+
607+
if (strlen($trimmed) > 0) {
608+
do {
609+
$this->prints($trimmed, [
610+
'color' => 'gray',
611+
'bold' => true
612+
]);
613+
614+
if ($default !== null) {
615+
$this->prints(" Enter = '".$default."'", [
616+
'color' => 'light-blue'
617+
]);
618+
}
619+
$this->println();
620+
$input = trim($this->readMaskedLine($mask));
621+
622+
$check = $this->getInputHelper($input, $validator, $default);
623+
624+
if ($check['valid']) {
625+
return $check['value'];
626+
}
627+
} while (true);
628+
}
629+
630+
return null;
631+
} /**
582632
* Returns the stream at which the command is sing to read inputs.
583633
*
584634
* @return null|InputStream If the stream is set, it will be returned as
@@ -964,7 +1014,63 @@ public function readInteger(string $prompt, ?int $default = null) : int {
9641014
public function readln() : string {
9651015
return $this->getInputStream()->readLine();
9661016
}
967-
1017+
/**
1018+
* Reads a line from input stream with character masking.
1019+
*
1020+
* This method reads input character by character and displays mask characters
1021+
* instead of the actual input. It handles backspace for character deletion
1022+
* and ignores special keys like ESC and arrow keys.
1023+
*
1024+
* @param string $mask The character to display instead of actual input characters.
1025+
*
1026+
* @return string The actual input string (unmasked).
1027+
*
1028+
* @since 1.1.0
1029+
*/
1030+
private function readMaskedLine(string $mask = '*'): string {
1031+
$input = '';
1032+
1033+
// For testing with ArrayInputStream, read the whole line at once
1034+
if ($this->getInputStream() instanceof \WebFiori\Cli\Streams\ArrayInputStream) {
1035+
$input = $this->getInputStream()->readLine();
1036+
// Simulate masking output for testing
1037+
$this->prints(str_repeat($mask, strlen($input)));
1038+
$this->println();
1039+
return $input;
1040+
}
1041+
1042+
// Set terminal to raw mode with echo disabled for real-time character reading
1043+
$sttyMode = null;
1044+
if (function_exists('shell_exec') && PHP_OS_FAMILY !== 'Windows') {
1045+
$sttyMode = shell_exec('stty -g 2>/dev/null');
1046+
shell_exec('stty -echo -icanon 2>/dev/null');
1047+
}
1048+
1049+
try {
1050+
// For real terminal input, read character by character
1051+
while (true) {
1052+
$char = KeysMap::readAndTranslate($this->getInputStream());
1053+
1054+
if ($char === 'LF' || $char === 'CR' || $char === '') {
1055+
break;
1056+
} elseif ($char === 'BACKSPACE' && strlen($input) > 0) {
1057+
$input = substr($input, 0, -1);
1058+
$this->prints("\x08 \x08"); // Backspace, space, backspace
1059+
} elseif ($char !== 'BACKSPACE' && $char !== 'ESC' && $char !== 'DOWN' && $char !== 'UP' && $char !== 'LEFT' && $char !== 'RIGHT') {
1060+
$input .= $char === 'SPACE' ? ' ' : $char;
1061+
$this->prints($mask);
1062+
}
1063+
}
1064+
} finally {
1065+
// Restore terminal settings
1066+
if ($sttyMode !== null) {
1067+
shell_exec('stty ' . $sttyMode . ' 2>/dev/null');
1068+
}
1069+
}
1070+
1071+
$this->println();
1072+
return $input;
1073+
}
9681074
/**
9691075
* Reads a string that represents class namespace.
9701076
*

0 commit comments

Comments
 (0)