mirror of
https://gitee.com/hyperf/hyperf.git
synced 2024-12-02 03:37:44 +08:00
Adds hyperf/closure-command
component (#5855)
This commit is contained in:
parent
f82bc145b5
commit
8d2a243c51
@ -23,6 +23,7 @@
|
||||
- [x] Added `hyperf/polyfill-coroutine` component.
|
||||
- [#5815](https://github.com/hyperf/hyperf/pull/5815) Added alias as `mysql` for `pdo` in `hyperf/db`.
|
||||
- [#5849](https://github.com/hyperf/hyperf/pull/5849) Support for insert update and select using enums.
|
||||
- [#5855](https://github.com/hyperf/hyperf/pull/5855) Added `hyperf/closure-command` component.
|
||||
|
||||
## Optimized
|
||||
|
||||
|
2
src/closure-command/.gitattributes
vendored
Normal file
2
src/closure-command/.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/tests export-ignore
|
||||
/.github export-ignore
|
13
src/closure-command/.github/workflows/close-pull-request.yml
vendored
Normal file
13
src/closure-command/.github/workflows/close-pull-request.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
name: Close Pull Request
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: superbrothers/close-pull-request@v3
|
||||
with:
|
||||
comment: "Hi, this is a READ-ONLY repository, please submit your PR on the https://github.com/hyperf/hyperf repository.<br><br> This Pull Request will close automatically.<br><br> Thanks! "
|
25
src/closure-command/.github/workflows/release.yml
vendored
Normal file
25
src/closure-command/.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
on:
|
||||
push:
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
name: Release
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
21
src/closure-command/LICENSE
Normal file
21
src/closure-command/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Hyperf
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
45
src/closure-command/composer.json
Normal file
45
src/closure-command/composer.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "hyperf/closure-command",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"php",
|
||||
"swoole",
|
||||
"closure-command"
|
||||
],
|
||||
"description": "The Closure Command component for hyperf",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Hyperf\\ClosureCommand\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"HyperfTest\\ClosureCommand\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"hyperf/collection": "~3.1.0",
|
||||
"hyperf/command": "~3.1.0",
|
||||
"hyperf/di": "~3.1.0",
|
||||
"hyperf/event": "~3.1.0",
|
||||
"hyperf/stringable": "~3.1.0",
|
||||
"hyperf/support": "~3.1.0",
|
||||
"hyperf/tappable": "~3.1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"hyperf/di": "Required to use annotations."
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"hyperf": {
|
||||
"config": "Hyperf\\ClosureCommand\\ConfigProvider"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
}
|
||||
}
|
||||
}
|
16
src/closure-command/publish/console.php
Normal file
16
src/closure-command/publish/console.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
use Hyperf\ClosureCommand\Console;
|
||||
|
||||
Console::command('hello', function () {
|
||||
$this->comment('Hello, Hyperf!');
|
||||
})->describe('This a closure command demo.');
|
44
src/closure-command/src/ClosureCommand.php
Normal file
44
src/closure-command/src/ClosureCommand.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\ClosureCommand;
|
||||
|
||||
use Closure;
|
||||
use Hyperf\Command\Command;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class ClosureCommand extends Command
|
||||
{
|
||||
private ParameterParser $parameterParser;
|
||||
|
||||
public function __construct(ContainerInterface $container, string $signature, protected Closure $closure)
|
||||
{
|
||||
$this->signature = $signature;
|
||||
$this->parameterParser = $container->get(ParameterParser::class);
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$inputs = array_merge($this->input->getArguments(), $this->input->getOptions());
|
||||
$parameters = $this->parameterParser->parseClosureParameters($this->closure, $inputs);
|
||||
|
||||
return $this->closure->call($this, ...$parameters);
|
||||
}
|
||||
|
||||
public function describe(string $description): self
|
||||
{
|
||||
$this->setDescription($description);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
34
src/closure-command/src/ConfigProvider.php
Normal file
34
src/closure-command/src/ConfigProvider.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\ClosureCommand;
|
||||
|
||||
use Hyperf\ClosureCommand\Listener\RegisterCommandListener;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
public function __invoke(): array
|
||||
{
|
||||
return [
|
||||
'listeners' => [
|
||||
RegisterCommandListener::class,
|
||||
],
|
||||
'publish' => [
|
||||
[
|
||||
'id' => 'config',
|
||||
'description' => 'The console route file of closure-command.',
|
||||
'source' => __DIR__ . '/../publish/console.php',
|
||||
'destination' => Console::ROUTE,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
52
src/closure-command/src/Console.php
Normal file
52
src/closure-command/src/Console.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\ClosureCommand;
|
||||
|
||||
use Closure;
|
||||
use Hyperf\Context\ApplicationContext;
|
||||
use Hyperf\Contract\ApplicationInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
|
||||
use function Hyperf\Support\make;
|
||||
use function Hyperf\Tappable\tap;
|
||||
|
||||
class Console
|
||||
{
|
||||
public const ROUTE = BASE_PATH . '/config/console.php';
|
||||
|
||||
/**
|
||||
* @var ClosureCommand[]
|
||||
*/
|
||||
protected static $commands = [];
|
||||
|
||||
public static function command(string $signature, Closure $command): ClosureCommand
|
||||
{
|
||||
return tap(make(ClosureCommand::class, [
|
||||
'signature' => $signature,
|
||||
'closure' => $command,
|
||||
]), static function ($handler) {
|
||||
$handlerId = spl_object_hash($handler);
|
||||
self::$commands[$handlerId] = $handler;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClosureCommand[]
|
||||
*/
|
||||
public static function getCommands(): array
|
||||
{
|
||||
return self::$commands;
|
||||
}
|
||||
}
|
64
src/closure-command/src/Listener/RegisterCommandListener.php
Normal file
64
src/closure-command/src/Listener/RegisterCommandListener.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\ClosureCommand\Listener;
|
||||
|
||||
use Hyperf\ClosureCommand\Console;
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\Contract\StdoutLoggerInterface;
|
||||
use Hyperf\Event\Contract\ListenerInterface;
|
||||
use Hyperf\Framework\Event\BootApplication;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class RegisterCommandListener implements ListenerInterface
|
||||
{
|
||||
/**
|
||||
* @param \Hyperf\Di\Container $container
|
||||
*/
|
||||
public function __construct(private ContainerInterface $container, private ConfigInterface $config, private StdoutLoggerInterface $logger)
|
||||
{
|
||||
}
|
||||
|
||||
public function listen(): array
|
||||
{
|
||||
return [
|
||||
BootApplication::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function process(object $event): void
|
||||
{
|
||||
$this->registerClosureCommands();
|
||||
}
|
||||
|
||||
private function registerClosureCommands(): void
|
||||
{
|
||||
$route = Console::ROUTE;
|
||||
|
||||
if (! file_exists($route)) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once $route;
|
||||
|
||||
foreach (Console::getCommands() as $handlerId => $command) {
|
||||
$this->container->set($handlerId, $command);
|
||||
$this->appendConfig('commands', $handlerId);
|
||||
}
|
||||
}
|
||||
|
||||
private function appendConfig(string $key, $configValues): void
|
||||
{
|
||||
$configs = $this->config->get($key, []);
|
||||
array_push($configs, $configValues);
|
||||
$this->config->set($key, $configs);
|
||||
}
|
||||
}
|
71
src/closure-command/src/ParameterParser.php
Normal file
71
src/closure-command/src/ParameterParser.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\ClosureCommand;
|
||||
|
||||
use Closure;
|
||||
use Hyperf\Contract\NormalizerInterface;
|
||||
use Hyperf\Di\ClosureDefinitionCollectorInterface;
|
||||
use Hyperf\Stringable\Str;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class ParameterParser
|
||||
{
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
private ?ClosureDefinitionCollectorInterface $closureDefinitionCollector = null;
|
||||
|
||||
public function __construct(private ContainerInterface $container)
|
||||
{
|
||||
$this->normalizer = $this->container->get(NormalizerInterface::class);
|
||||
|
||||
if ($this->container->has(ClosureDefinitionCollectorInterface::class)) {
|
||||
$this->closureDefinitionCollector = $this->container->get(ClosureDefinitionCollectorInterface::class);
|
||||
}
|
||||
}
|
||||
|
||||
public function parseClosureParameters(Closure $closure, array $arguments): array
|
||||
{
|
||||
if (! $this->closureDefinitionCollector) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$definitions = $this->closureDefinitionCollector->getParameters($closure);
|
||||
|
||||
return $this->getInjections($definitions, 'Closure', $arguments);
|
||||
}
|
||||
|
||||
private function getInjections(array $definitions, string $callableName, array $arguments): array
|
||||
{
|
||||
$injections = [];
|
||||
|
||||
foreach ($definitions as $pos => $definition) {
|
||||
$value = $arguments[$pos] ?? $arguments[$definition->getMeta('name')] ?? $arguments[Str::snake($definition->getMeta('name'), '-')] ?? null;
|
||||
if ($value === null) {
|
||||
if ($definition->getMeta('defaultValueAvailable')) {
|
||||
$injections[] = $definition->getMeta('defaultValue');
|
||||
} elseif ($this->container->has($definition->getName())) {
|
||||
$injections[] = $this->container->get($definition->getName());
|
||||
} elseif ($definition->allowsNull()) {
|
||||
$injections[] = null;
|
||||
} else {
|
||||
throw new InvalidArgumentException("Parameter '{$definition->getMeta('name')}' "
|
||||
. "of {$callableName} should not be null");
|
||||
}
|
||||
} else {
|
||||
$injections[] = $this->normalizer->denormalize($value, $definition->getName());
|
||||
}
|
||||
}
|
||||
|
||||
return $injections;
|
||||
}
|
||||
}
|
0
src/closure-command/tests/.gitkeep
Normal file
0
src/closure-command/tests/.gitkeep
Normal file
Loading…
Reference in New Issue
Block a user