Skip to content

Commit f862c22

Browse files
committed
Initial commit
1 parent c14ea2d commit f862c22

13 files changed

Lines changed: 611 additions & 0 deletions

.gitignore

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Created by .ignore support plugin (hsz.mobi)
2+
### Composer template
3+
composer.phar
4+
/vendor/
5+
6+
# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
7+
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
8+
composer.lock
9+
10+
### JetBrains template
11+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
12+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
13+
14+
# User-specific stuff:
15+
.idea
16+
17+
## File-based project format:
18+
*.iws
19+
20+
## Plugin-specific files:
21+
22+
# IntelliJ
23+
/out/
24+
25+
# mpeltonen/sbt-idea plugin
26+
.idea_modules/
27+
28+
# JIRA plugin
29+
atlassian-ide-plugin.xml
30+
31+
# Crashlytics plugin (for Android Studio and IntelliJ)
32+
com_crashlytics_export_strings.xml
33+
crashlytics.properties
34+
crashlytics-build.properties
35+
fabric.properties

OSSMETADATA

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
osslifecycle=active

README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# dot-cli
2+
3+
DotKernel component to build console applications based on laminas-cli
4+
5+
### Requirements
6+
- PHP >= 7.4
7+
- laminas/laminas-servicemanager >= 3.6,
8+
- laminas/laminas-cli >= 1.0
9+
10+
11+
### Setup
12+
#### 1. Install package
13+
Run the following command in your application's root directory:
14+
```bash
15+
$ composer require dotkernel/dot-cli
16+
```
17+
18+
#### 2. Register ConfigProvider
19+
Open your application's `config/config.php` and add `Dot\Cli\ConfigProvider::class,` under the _DK packages_ comment.
20+
21+
#### 3. Copy bootstrap file
22+
Locate in this package the following file `bin/cli.php` then copy it to your application's `bin/` directory.
23+
This is the bootstrap file you will use to execute your commands with.
24+
25+
#### 4. Copy config file
26+
Locate in this package the following file `config/autoload/cli.global.php` then copy it to your application's `config/autoload/` directory.
27+
This is the config file you will add your commands to.
28+
29+
30+
### Testing
31+
Using the command line, go to your application's root directory, then type the following command:
32+
```bash
33+
$ php /bin/cli.php
34+
```
35+
The output should look similar to this, containing information on how to start using dot-cli:
36+
```text
37+
DotKernel CLI 1.0.0
38+
39+
Usage:
40+
command [options] [arguments]
41+
42+
Options:
43+
-h, --help Display help for the given command. When no command is given display help for the list command
44+
-q, --quiet Do not output any message
45+
-V, --version Display this application version
46+
--ansi Force ANSI output
47+
--no-ansi Disable ANSI output
48+
-n, --no-interaction Do not ask any interactive question
49+
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
50+
51+
Available commands:
52+
demo-command Demo command description.
53+
help Display help for a command
54+
list List commands
55+
```
56+
As shown in `config/autoload/cli.global.php`, dot-cli includes a demo command `demo-command` that will help you understand the basics of creating a new command.
57+
For more information, see [laminas-cli documentation](https://docs.laminas.dev/laminas-cli/).
58+
59+
## License
60+
MIT

bin/cli.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Dot\Cli\Factory\ApplicationFactory;
6+
use Psr\Container\ContainerInterface;
7+
8+
chdir(dirname(__DIR__));
9+
require 'vendor/autoload.php';
10+
11+
/** @var ContainerInterface $container */
12+
$container = require 'config/container.php';
13+
14+
$applicationFactory = new ApplicationFactory();
15+
exit($applicationFactory($container)->run());

composer.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "dotkernel/dot-cli",
3+
"type": "library",
4+
"description": "DotKernel component for creating console applications based on laminas-cli",
5+
"license": "MIT",
6+
"homepage": "https://github.com/dotkernel/dot-cli",
7+
"keywords": [
8+
"cli",
9+
"console",
10+
"dotkernel",
11+
"laminas",
12+
"mezzio"
13+
],
14+
"authors": [
15+
{
16+
"name": "DotKernel Team",
17+
"email": "team@dotkernel.com"
18+
}
19+
],
20+
"require": {
21+
"php": "^7.4",
22+
"laminas/laminas-dependency-plugin": "^2.1",
23+
"laminas/laminas-cli": "^1.0",
24+
"laminas/laminas-servicemanager": "^3.6"
25+
},
26+
"require-dev": {
27+
"phpunit/phpunit": "^9.5",
28+
"squizlabs/php_codesniffer": "^3.5"
29+
},
30+
"autoload": {
31+
"psr-4": {
32+
"Dot\\Cli\\": "src"
33+
}
34+
}
35+
}

config/autoload/cli.global.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
use Dot\Cli\Command\DemoCommand;
4+
use Dot\Cli\FileLockerInterface;
5+
6+
/**
7+
* Documentation: https://docs.laminas.dev/laminas-cli/
8+
*/
9+
return [
10+
'dot_cli' => [
11+
'version' => '1.0.0',
12+
'name' => 'DotKernel CLI',
13+
'showVersion' => true,
14+
'commands' => [
15+
'demo-command' => DemoCommand::class,
16+
]
17+
],
18+
FileLockerInterface::class => [
19+
'enabled' => true,
20+
'dirPath' => getcwd() . '/data/lock',
21+
]
22+
];

src/Application.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Dot\Cli;
6+
7+
use Exception;
8+
use Symfony\Component\Console\Input\InputInterface;
9+
use Symfony\Component\Console\Output\OutputInterface;
10+
use Throwable;
11+
12+
/**
13+
* Class Application
14+
* @package Dot\Cli
15+
*/
16+
class Application extends \Symfony\Component\Console\Application
17+
{
18+
private FileLockerInterface $fileLocker;
19+
20+
/**
21+
* Application constructor.
22+
* @param FileLockerInterface $fileLocker
23+
* @param array $config
24+
*/
25+
public function __construct(FileLockerInterface $fileLocker, array $config)
26+
{
27+
parent::__construct($config['name'] ?? 'UNKNOWN', $config['version'] ?? 'UNKNOWN');
28+
29+
$this->fileLocker = $fileLocker;
30+
}
31+
32+
/**
33+
* Application destructor.
34+
*/
35+
public function __destruct()
36+
{
37+
$this->fileLocker->unlock();
38+
}
39+
40+
/**
41+
* @param InputInterface $input
42+
* @param OutputInterface $output
43+
* @return int
44+
* @throws Throwable
45+
*/
46+
public function doRun(InputInterface $input, OutputInterface $output): int
47+
{
48+
$commandName = $this->getCommandName($input);
49+
$this->fileLocker->setCommandName($commandName)->setEnabled(true);
50+
51+
try {
52+
$this->fileLocker->lock();
53+
} catch (Exception $exception) {
54+
$output->writeln($exception->getMessage());
55+
return 0;
56+
}
57+
58+
return parent::doRun($input, $output);
59+
}
60+
}

src/Command/DemoCommand.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Dot\Cli\Command;
6+
7+
use Symfony\Component\Console\Command\Command;
8+
use Symfony\Component\Console\Input\InputArgument;
9+
use Symfony\Component\Console\Input\InputInterface;
10+
use Symfony\Component\Console\Input\InputOption;
11+
use Symfony\Component\Console\Output\OutputInterface;
12+
13+
/**
14+
* Class DemoCommand
15+
* @package Dot\Cli\Command
16+
*/
17+
class DemoCommand extends Command
18+
{
19+
protected static $defaultName = 'demo-command';
20+
21+
/**
22+
* @return void
23+
*/
24+
protected function configure(): void
25+
{
26+
$this
27+
->setName(self::$defaultName)
28+
->setDescription('Demo command description.')
29+
->addOption('opt_required', 'r', InputOption::VALUE_REQUIRED, 'Required parameter')
30+
->addOption('opt_optional', 'o', InputOption::VALUE_OPTIONAL, 'Optional parameter')
31+
->addArgument('arg_required', InputArgument::REQUIRED, 'Required argument')
32+
->addArgument('arg_optional', InputArgument::OPTIONAL, 'Optional argument', 'arg')
33+
->addArgument('arg_array', InputArgument::IS_ARRAY, 'Array argument');
34+
}
35+
36+
/**
37+
* @param InputInterface $input
38+
* @param OutputInterface $output
39+
* @return int
40+
*/
41+
protected function execute(InputInterface $input, OutputInterface $output): int
42+
{
43+
$output->writeln('opt_required=' . $input->getOption('opt_required'));
44+
$output->writeln('opt_optional=' . $input->getOption('opt_optional'));
45+
$output->writeln('arg_required=' . $input->getArgument('arg_required'));
46+
$output->writeln('arg_optional=' . $input->getArgument('arg_optional'));
47+
$output->writeln('arg_array=[' . implode(', ', $input->getArgument('arg_array')) . ']');
48+
49+
return 0;
50+
}
51+
}

src/ConfigProvider.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Dot\Cli;
6+
7+
use Dot\Cli\Factory\ApplicationFactory;
8+
use Dot\Cli\Factory\FileLockerFactory;
9+
10+
/**
11+
* Class ConfigProvider
12+
* @package Dot\Cli
13+
*/
14+
class ConfigProvider
15+
{
16+
/**
17+
* @return array
18+
*/
19+
public function __invoke(): array
20+
{
21+
return [
22+
'dependencies' => $this->getDependencyConfig(),
23+
];
24+
}
25+
26+
/**
27+
* @return string[][]
28+
*/
29+
public function getDependencyConfig(): array
30+
{
31+
return [
32+
'aliases' => [
33+
FileLockerInterface::class => FileLocker::class
34+
],
35+
'factories' => [
36+
Application::class => ApplicationFactory::class,
37+
FileLocker::class => FileLockerFactory::class
38+
]
39+
];
40+
}
41+
}

src/Factory/ApplicationFactory.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Dot\Cli\Factory;
6+
7+
use Dot\Cli\FileLockerInterface;
8+
use Laminas\Cli\ContainerCommandLoader;
9+
use Laminas\Cli\Listener\TerminateListener;
10+
use PackageVersions\Versions;
11+
use Psr\Container\ContainerInterface;
12+
use Symfony\Component\Console\Application;
13+
use Symfony\Component\Console\ConsoleEvents;
14+
use Symfony\Component\EventDispatcher\EventDispatcher;
15+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
16+
use Webmozart\Assert\Assert;
17+
18+
/**
19+
* Class ApplicationFactory
20+
*/
21+
class ApplicationFactory
22+
{
23+
/**
24+
* @param ContainerInterface $container
25+
* @return Application
26+
*/
27+
public function __invoke(ContainerInterface $container): Application
28+
{
29+
$config = $container->get('config')['dot_cli'] ?? [];
30+
Assert::isMap($config);
31+
32+
/** @psalm-suppress DeprecatedClass */
33+
$version = strstr(Versions::getVersion('laminas/laminas-cli'), '@', true);
34+
Assert::string($version);
35+
36+
$commands = $config['commands'] ?? [];
37+
Assert::isMap($commands);
38+
Assert::allString($commands);
39+
40+
$eventDispatcherServiceName = __NAMESPACE__ . '\SymfonyEventDispatcher';
41+
$dispatcher = $container->has($eventDispatcherServiceName)
42+
? $container->get($eventDispatcherServiceName)
43+
: new EventDispatcher();
44+
Assert::isInstanceOf($dispatcher, EventDispatcherInterface::class);
45+
46+
$dispatcher->addListener(ConsoleEvents::TERMINATE, new TerminateListener($config));
47+
48+
$fileLocker = $container->get(FileLockerInterface::class);
49+
Assert::isInstanceOf($fileLocker, FileLockerInterface::class);
50+
51+
$application = new \Dot\Cli\Application($fileLocker, $config);
52+
// phpcs:ignore WebimpressCodingStandard.PHP.CorrectClassNameCase
53+
$application->setCommandLoader(new ContainerCommandLoader($container, $commands));
54+
$application->setDispatcher($dispatcher);
55+
$application->setAutoExit(false);
56+
57+
return $application;
58+
}
59+
}

0 commit comments

Comments
 (0)