⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content
Merged

Dev #39

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/php81.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ on:
jobs:
test:
name: Run Tests
uses: WebFiori/workflows/.github/workflows/test-php.yaml@main
uses: WebFiori/workflows/.github/workflows/test-php.yaml@v1.2.1
with:
php-version: '8.1'

code-coverage:
name: Coverage
needs: test
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@main
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@v1.2.1
with:
php-version: '8.1'
coverage-file: 'php-8.1-coverage.xml'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/php82.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ on:
jobs:
test:
name: Run Tests
uses: WebFiori/workflows/.github/workflows/test-php.yaml@main
uses: WebFiori/workflows/.github/workflows/test-php.yaml@v1.2.1
with:
php-version: '8.2'
phpunit-config: "tests/phpunit10.xml"

code-coverage:
name: Coverage
needs: test
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@main
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@v1.2.1
with:
php-version: '8.2'
coverage-file: 'php-8.2-coverage.xml'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/php83.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:

test:
name: Run Tests
uses: WebFiori/workflows/.github/workflows/test-php.yaml@main
uses: WebFiori/workflows/.github/workflows/test-php.yaml@v1.2.1
with:
php-version: '8.3'
phpunit-config: 'tests/phpunit10.xml'
Expand All @@ -21,7 +21,7 @@ jobs:
code-coverage:
name: Coverage
needs: test
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@main
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@v1.2.1
with:
php-version: '8.3'
coverage-file: 'php-8.3-coverage.xml'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/php84.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ on:
jobs:
test:
name: Run Tests
uses: WebFiori/workflows/.github/workflows/test-php.yaml@main
uses: WebFiori/workflows/.github/workflows/test-php.yaml@v1.2.1
with:
php-version: '8.4'
phpunit-config: "tests/phpunit10.xml"

code-coverage:
name: Coverage
needs: test
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@main
uses: WebFiori/workflows/.github/workflows/coverage-codecov.yaml@v1.2.1
with:
php-version: '8.4'
coverage-file: 'php-8.4-coverage.xml'
Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/php85.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Build PHP 8.5

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
name: Run Tests
uses: WebFiori/workflows/.github/workflows/[email protected]
with:
php-version: '8.5'
phpunit-config: "tests/phpunit10.xml"

code-coverage:
name: Coverage
needs: test
uses: WebFiori/workflows/.github/workflows/[email protected]
with:
php-version: '8.5'
coverage-file: 'php-8.5-coverage.xml'
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}



5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
Class library that can help in writing command line based applications with minimum dependencies using PHP.

<p align="center">
<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 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>
<a href="https://codecov.io/gh/WebFiori/cli">
<img src="https://codecov.io/gh/WebFiori/cli/branch/main/graph/badge.svg" />
Expand Down Expand Up @@ -56,6 +56,7 @@ Class library that can help in writing command line based applications with mini
| <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> |
| <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> |
| <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> |
| <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> |

## Features
* **Easy Command Creation**: Simple class-based approach to building CLI commands
Expand Down
108 changes: 107 additions & 1 deletion WebFiori/Cli/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,56 @@ public function getInput(string $prompt, ?string $default = null, ?InputValidato
return null;
}
/**
* Reads user input with characters masked by a specified character.
*
* This method is similar to getInput() but masks the input characters as the user types,
* making it suitable for sensitive information like passwords, tokens, or secrets.
* The actual input value is captured but only mask characters are displayed in the terminal.
*
* @param string $prompt The prompt message to display to the user. Must be non-empty.
*
* @param string $mask The character to display instead of the actual input characters.
* Default is '*'. Can be any single character or string.
*
* @param string|null $default An optional default value to use if the user provides
* empty input. If provided, it will be shown in the prompt.
*
* @param InputValidator|null $validator An optional validator to validate the input.
* If validation fails, the user will be prompted again.
*
* @return string|null Returns the actual input value (not masked) if valid input is provided,
* or null if the prompt is empty.
*
* @since 1.1.0
*/
public function getMaskedInput(string $prompt, string $mask = '*', ?string $default = null, ?InputValidator $validator = null): ?string {
$trimmed = trim($prompt);

if (strlen($trimmed) > 0) {
do {
$this->prints($trimmed, [
'color' => 'gray',
'bold' => true
]);

if ($default !== null) {
$this->prints(" Enter = '".$default."'", [
'color' => 'light-blue'
]);
}
$this->println();
$input = trim($this->readMaskedLine($mask));

$check = $this->getInputHelper($input, $validator, $default);

if ($check['valid']) {
return $check['value'];
}
} while (true);
}

return null;
} /**
* Returns the stream at which the command is sing to read inputs.
*
* @return null|InputStream If the stream is set, it will be returned as
Expand Down Expand Up @@ -964,7 +1014,63 @@ public function readInteger(string $prompt, ?int $default = null) : int {
public function readln() : string {
return $this->getInputStream()->readLine();
}

/**
* Reads a line from input stream with character masking.
*
* This method reads input character by character and displays mask characters
* instead of the actual input. It handles backspace for character deletion
* and ignores special keys like ESC and arrow keys.
*
* @param string $mask The character to display instead of actual input characters.
*
* @return string The actual input string (unmasked).
*
* @since 1.1.0
*/
private function readMaskedLine(string $mask = '*'): string {
$input = '';

// For testing with ArrayInputStream, read the whole line at once
if ($this->getInputStream() instanceof \WebFiori\Cli\Streams\ArrayInputStream) {
$input = $this->getInputStream()->readLine();
// Simulate masking output for testing
$this->prints(str_repeat($mask, strlen($input)));
$this->println();
return $input;
}

// Set terminal to raw mode with echo disabled for real-time character reading
$sttyMode = null;
if (function_exists('shell_exec') && PHP_OS_FAMILY !== 'Windows') {
$sttyMode = shell_exec('stty -g 2>/dev/null');
shell_exec('stty -echo -icanon 2>/dev/null');
}

try {
// For real terminal input, read character by character
while (true) {
$char = KeysMap::readAndTranslate($this->getInputStream());

if ($char === 'LF' || $char === 'CR' || $char === '') {
break;
} elseif ($char === 'BACKSPACE' && strlen($input) > 0) {
$input = substr($input, 0, -1);
$this->prints("\x08 \x08"); // Backspace, space, backspace
} elseif ($char !== 'BACKSPACE' && $char !== 'ESC' && $char !== 'DOWN' && $char !== 'UP' && $char !== 'LEFT' && $char !== 'RIGHT') {
$input .= $char === 'SPACE' ? ' ' : $char;
$this->prints($mask);
}
}
} finally {
// Restore terminal settings
if ($sttyMode !== null) {
shell_exec('stty ' . $sttyMode . ' 2>/dev/null');
}
}

$this->println();
return $input;
}
/**
* Reads a string that represents class namespace.
*
Expand Down
Loading