Skip to content

Commit f583d39

Browse files
Merge pull request #3 from InitPHP/2.x
Add CI workflow for PHP 7.2-8.4, setup PHP, validate composer.json, c…
2 parents 4eecf7c + c217c23 commit f583d39

39 files changed

Lines changed: 3955 additions & 1206 deletions

.github/workflows/ci.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, '*.x']
6+
pull_request:
7+
branches: [main, '*.x']
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
qa:
14+
name: PHP ${{ matrix.php }} / QA
15+
runs-on: ubuntu-latest
16+
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
21+
22+
steps:
23+
- name: Checkout
24+
uses: actions/checkout@v4
25+
26+
- name: Setup PHP
27+
uses: shivammathur/setup-php@v2
28+
with:
29+
php-version: ${{ matrix.php }}
30+
extensions: mbstring
31+
coverage: none
32+
tools: composer:v2
33+
env:
34+
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35+
36+
- name: Validate composer.json
37+
run: composer validate --strict
38+
39+
- name: Get Composer cache directory
40+
id: composer-cache
41+
run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"
42+
43+
- name: Cache Composer dependencies
44+
uses: actions/cache@v4
45+
with:
46+
path: ${{ steps.composer-cache.outputs.dir }}
47+
key: composer-${{ runner.os }}-${{ matrix.php }}-${{ hashFiles('composer.json') }}
48+
restore-keys: |
49+
composer-${{ runner.os }}-${{ matrix.php }}-
50+
51+
- name: Install dependencies
52+
run: composer update --prefer-dist --no-progress --no-interaction
53+
54+
- name: Coding standards (phpcs)
55+
run: composer cs-ci
56+
57+
- name: Static analysis (PHPStan)
58+
run: composer stan
59+
60+
- name: Unit tests (PHPUnit)
61+
run: composer test

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
/.vs/
44
/vendor/
55
/composer.lock
6+
/build/
7+
/.phpunit.cache/
8+
.phpunit.result.cache
9+
/.php-cs-fixer.cache

README.md

Lines changed: 168 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,166 @@
1-
# Console
1+
# InitPHP Console
22

3-
A simple helper library for writing console/CLI applications in PHP.
3+
A small, dependency-free helper library for writing console / CLI applications in PHP — command routing, typed arguments, coloured output, interactive questions and a styleable ANSI table renderer.
44

5-
Starting with **2.1**, this package also ships the ANSI-coloured table renderer that used to be distributed as the separate [`initphp/cli-table`](https://github.com/InitPHP/CLITable) package (now deprecated). See the [migration section](#migrating-from-initphpcli-table) below if you are coming from that package.
5+
[![CI](https://github.com/InitPHP/Console/actions/workflows/ci.yml/badge.svg)](https://github.com/InitPHP/Console/actions/workflows/ci.yml)
6+
[![Latest Stable Version](https://poser.pugx.org/initphp/console/v/stable)](https://packagist.org/packages/initphp/console)
7+
[![Total Downloads](https://poser.pugx.org/initphp/console/downloads)](https://packagist.org/packages/initphp/console)
8+
[![License](https://poser.pugx.org/initphp/console/license)](./LICENSE)
9+
[![PHP Version Require](https://poser.pugx.org/initphp/console/require/php)](https://packagist.org/packages/initphp/console)
10+
11+
> Starting with **2.1** this package also ships the ANSI table renderer that used to be distributed as the separate [`initphp/cli-table`](https://github.com/InitPHP/CLITable) package (now deprecated). See [Migrating from `initphp/cli-table`](#migrating-from-initphpcli-table).
12+
13+
## Features
14+
15+
- **Command routing** — register commands as closures or as classes extending `Command`.
16+
- **Grouped help** — automatic `help` / `list` overview and per-command `--help` usage.
17+
- **Typed arguments** — declare `--name` arguments with a type (`INT`, `FLOAT`, `BOOL`, …), a default and an optional/required flag; values are validated automatically.
18+
- **Input parsing** — long arguments (`--name=value`), short options (`-v`, `-abc`, `-k=value`) and bare positional segments, with automatic scalar type casting.
19+
- **Coloured output** — 16/256-colour SGR helpers, message styles (`error`, `success`, `warning`, `info`), key/value lists and a progress bar.
20+
- **Interactive prompts** — free-form `ask()` and option-constrained `question()`.
21+
- **Table rendering** — a styleable, multibyte-aware ASCII/ANSI table.
22+
- **Testable I/O** — output and input streams are injectable, so commands can be unit tested without touching `STDOUT`/`STDIN`.
623

724
## Requirements
825

9-
- PHP 7.2 or higher
26+
- PHP **7.2** or higher
27+
- `ext-mbstring` *(optional)* — improves table alignment for multibyte (UTF-8) values
1028

1129
## Installation
1230

13-
```
31+
```bash
1432
composer require initphp/console
1533
```
1634

17-
## Usage
35+
## Quick start
36+
37+
Create an entry script (e.g. `console.php`):
1838

1939
```php
2040
#!/usr/bin/env php
2141
<?php
22-
require_once __DIR__ . '/../vendor/autoload.php';
23-
use \InitPHP\Console\{Application, Input, Output};
2442

25-
$console = new Application("My Console Application", '1.0');
43+
require_once __DIR__ . '/vendor/autoload.php';
44+
45+
use InitPHP\Console\{Application, Input, Output};
2646

27-
// Register commands ...
47+
$console = new Application('My Console Application', '1.0.0');
2848

29-
// hello -name=John
49+
// A closure command: php console.php hello --name=John
3050
$console->register('hello', function (Input $input, Output $output) {
31-
if ($input->hasArgument('name')) {
32-
$output->writeln('Hello {name}', [
33-
'name' => $input->getArgument('name')
34-
]);
35-
} else {
36-
$output->writeln('Hello World!');
37-
}
51+
$output->writeln('Hello {name}!', [
52+
'name' => $input->getArgument('name', 'World'),
53+
]);
3854
}, 'Says hello.');
3955

40-
4156
$console->run();
4257
```
4358

59+
Run it:
60+
61+
```bash
62+
php console.php hello --name=John # Hello John!
63+
php console.php hello # Hello World!
64+
php console.php list # Show all registered commands
4465
```
45-
php console.php list
66+
67+
## Terminology
68+
69+
This library distinguishes three kinds of tokens that follow the command name:
70+
71+
| Token | Shape | Accessor |
72+
|------------------|--------------------------------|-----------------|
73+
| **Argument** | `--name`, `--name=value` | `getArgument()` |
74+
| **Option** | `-v`, `-abc`, `-key=value` | `getOption()` |
75+
| **Segment** | bare value, e.g. `migrate` | `getSegment()` |
76+
77+
> **Note:** here `--long` tokens are called *arguments* and `-short` tokens are called *options*. This is the opposite of some other frameworks — keep it in mind when porting code.
78+
79+
All scalar values are cast automatically: `"true"`/`"false"`/`"yes"`/`"no"``bool`, `"null"``null`, integer/decimal strings → `int`/`float`.
80+
81+
## Class-based commands
82+
83+
For anything beyond a one-liner, extend `Command`:
84+
85+
```php
86+
use InitPHP\Console\{Command, Input, InputArgument, Output};
87+
88+
class GreetCommand extends Command
89+
{
90+
public $command = 'app:greet';
91+
92+
public function definition(): string
93+
{
94+
return 'Greets a person.';
95+
}
96+
97+
public function help(): string
98+
{
99+
return 'Prints a friendly greeting to the given name.';
100+
}
101+
102+
public function arguments(): array
103+
{
104+
return [
105+
new InputArgument('name', InputArgument::STR, 'World', true, 'Who to greet.'),
106+
];
107+
}
108+
109+
public function execute(Input $input, Output $output)
110+
{
111+
$output->success('Hi ' . $input->getArgument('name'));
112+
}
113+
}
114+
115+
$console->register(GreetCommand::class);
46116
```
47117

48-
## Rendering tables
118+
Declared `arguments()` are validated *before* `execute()` runs: missing required arguments or values that do not match the declared type abort the command with an error.
49119

50-
This package ships a styleable ASCII/ANSI table renderer under `\InitPHP\Console\Utils\Table`:
120+
```bash
121+
php console.php app:greet --name=Ada
122+
php console.php app:greet --help # shows the generated usage + parameters
123+
```
124+
125+
## Output
126+
127+
```php
128+
$output->writeln('Plain line');
129+
$output->writeln('Coloured', [], [Output::COLOR_GREEN, Output::BOLD]);
130+
$output->write('No newline; {token} interpolated', ['token' => 42]);
131+
132+
$output->error('Something failed');
133+
$output->success('All good');
134+
$output->warning('Heads up');
135+
$output->info('FYI');
136+
137+
$output->list(['host' => 'localhost', 'port' => 8080]);
138+
139+
for ($i = 0; $i <= 100; $i += 10) {
140+
$output->progressBar($i, 100);
141+
usleep(50_000);
142+
}
143+
```
144+
145+
## Interactive prompts
146+
147+
```php
148+
$name = $output->ask('What is your name?');
149+
150+
use InitPHP\Console\Question;
151+
152+
$question = (new Question())
153+
->setQuestion('Continue? (yes/no)')
154+
->setOptions(['yes', 'no'])
155+
->optional()
156+
->setDefault('no');
157+
158+
$answer = $output->question($question);
159+
```
160+
161+
Typing `exit` or `quit` at any prompt terminates the application.
162+
163+
## Rendering tables
51164

52165
```php
53166
use InitPHP\Console\Utils\Table;
@@ -58,43 +171,65 @@ $table = Table::create()
58171
->setCellStyle(Table::COLOR_GREEN);
59172

60173
$table->row(['id' => 1, 'name' => 'Matthew S.', 'email' => 'matthew@example.com', 'status' => true])
61-
->row(['id' => 2, 'name' => 'Millie J.', 'email' => 'millie@example.com', 'status' => false])
62-
->row(['id' => 3, 'name' => 'Regina G.', 'email' => 'regina@example.com', 'status' => true]);
174+
->row(['id' => 2, 'name' => 'Millie J.', 'email' => 'millie@example.com', 'status' => false]);
63175

64176
echo $table; // or $table->getContent()
65177
```
66178

67-
The renderer is intentionally lightweight: it stringifies non-string cell values (`[NULL]`, `[TRUE]`, `[FALSE]`, `[CALLABLE]`, `[RESOURCE]`, class name for objects), auto-sizes columns, and emits standard SGR escape sequences. `mb_strlen()` is used when available so multibyte values align correctly.
179+
Non-string cell values are stringified (`[NULL]`, `[TRUE]`, `[FALSE]`, `[ARRAY]`, `[CALLABLE]`, `[RESOURCE]`, or the class name for objects), columns are auto-sized, and `mb_strlen()` is used when available so multibyte values align correctly.
180+
181+
## Documentation
182+
183+
In-depth, example-driven guides live in [`docs/`](./docs):
184+
185+
1. [Getting started](./docs/01-getting-started.md)
186+
2. [Commands](./docs/02-commands.md)
187+
3. [Input: arguments, options & segments](./docs/03-input.md)
188+
4. [Typed input arguments](./docs/04-input-arguments.md)
189+
5. [Output & formatting](./docs/05-output.md)
190+
6. [Interactive questions](./docs/06-questions.md)
191+
7. [Tables](./docs/07-tables.md)
192+
8. [Migrating from `initphp/cli-table`](./docs/08-migrating-from-cli-table.md)
68193

69194
## Migrating from `initphp/cli-table`
70195

71-
The standalone [`initphp/cli-table`](https://github.com/InitPHP/CLITable) package has been merged into this one starting with **2.1** and is now deprecated.
196+
The standalone [`initphp/cli-table`](https://github.com/InitPHP/CLITable) package has been merged into this one as of **2.1** and is now deprecated.
72197

73-
If your code currently uses `\InitPHP\CLITable\Table`, **no source changes are required** — this package ships a `class_alias` that keeps the old fully-qualified class name working. Just switch your dependency:
198+
If your code uses `\InitPHP\CLITable\Table`, **no source changes are required** — this package ships a `class_alias` keeping the old fully-qualified name working. Just switch the dependency:
74199

75200
```diff
76201
- "initphp/cli-table": "^1.0",
77202
+ "initphp/console": "^2.1"
78203
```
79204

80-
(`initphp/console:^2.1` declares a Composer `replace` for `initphp/cli-table`, so Composer will not install both side-by-side.)
81-
82-
When you next touch the code, prefer the new canonical namespace:
205+
(`initphp/console` declares a Composer `replace` for `initphp/cli-table`, so the two will never be installed side by side.) When you next touch the code, prefer the canonical namespace:
83206

84207
```php
85208
// Before
86209
use InitPHP\CLITable\Table;
87-
88210
// After
89211
use InitPHP\Console\Utils\Table;
90212
```
91213

92-
The alias is intended as a transition aid and may be removed in a future major release.
214+
The alias is a transition aid and may be removed in a future major release. See the [migration guide](./docs/08-migrating-from-cli-table.md) for details.
215+
216+
## Testing & quality
217+
218+
```bash
219+
composer test # PHPUnit
220+
composer cs # PHP_CodeSniffer (PSR-12)
221+
composer stan # PHPStan (level 6)
222+
composer qa # all of the above
223+
```
224+
225+
## Contributing
226+
227+
Contributions are welcome. Please run `composer qa` before opening a pull request. See the organisation [contributing guidelines](https://github.com/InitPHP/.github/blob/main/CONTRIBUTING.md).
93228

94229
## Credits
95230

96231
- [Muhammet ŞAFAK](https://www.muhammetsafak.com.tr) <<info@muhammetsafak.com.tr>>
97232

98233
## License
99234

100-
Copyright &copy; 2022 [MIT License](./LICENSE)
235+
Released under the [MIT License](./LICENSE). Copyright © 2022 InitPHP.

0 commit comments

Comments
 (0)