Added option no-restart for watcher. (#2875)

This commit is contained in:
李铭昕 2020-11-25 20:23:49 +08:00 committed by GitHub
parent d026b98134
commit c360e79d7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 18 additions and 621 deletions

View File

@ -3,7 +3,7 @@
## Added
- [#2857](https://github.com/hyperf/hyperf/pull/2857) Support Consul ACL Token for Service Governance.
- [#2864](https://github.com/hyperf/hyperf/pull/2864) Added encryption component.
- [#2875](https://github.com/hyperf/hyperf/pull/2875) Added option `no-restart` for watcher.
## Fixed

View File

@ -1,37 +0,0 @@
# 加密解密
[hyperf/encryption](https://github.com/hyperf/encryption) 借鉴于 `Laravel Encryption` 组件,十分感谢 `Laravel` 开发组对 `PHP` 社区的贡献。
## 简介
`Encryption` 是基于 `OpenSSL` 实现加密解密组件。
## 配置
加密器可以根据实际情况,配置多组 `key``cipher`
```php
<?php
return [
'default' => [
'key' => 'Hyperf',
'cipher' => 'AES-128-CBC',
],
];
```
## 使用
```php
<?php
use Hyperf\Utils\ApplicationContext;
use Hyperf\Encryption\Contract\EncrypterInterface;
$input = 'Hello Word.';
$container = ApplicationContext::getContainer();
$encrypter = $container->get(EncrypterInterface::class);
$encrypt = $encrypter->encrypt($input);
$raw = $encrypter->decrypt($encrypt);
```

View File

@ -1,2 +0,0 @@
/tests export-ignore
/.github export-ignore

View File

@ -1,25 +0,0 @@
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

View File

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
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.

View File

@ -1,36 +0,0 @@
{
"name": "hyperf/encryption",
"type": "library",
"license": "MIT",
"keywords": [
"php",
"hyperf",
"encryption"
],
"description": "",
"autoload": {
"psr-4": {
"Hyperf\\Encryption\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"HyperfTest\\Encryption\\": "tests/"
}
},
"require": {
"php": ">=7.2",
"ext-json": "*",
"ext-openssl": "*",
"hyperf/contract": "~2.0.0",
"psr/container": "^1.0"
},
"config": {
"sort-packages": true
},
"extra": {
"hyperf": {
"config": "Hyperf\\Encryption\\ConfigProvider"
}
}
}

View File

@ -1,17 +0,0 @@
<?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
*/
return [
'default' => [
'key' => 'Hyperf',
'cipher' => 'AES-128-CBC',
],
];

View File

@ -1,34 +0,0 @@
<?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\Encryption;
use Hyperf\Encryption\Contract\EncrypterInterface;
class ConfigProvider
{
public function __invoke(): array
{
return [
'dependencies' => [
EncrypterInterface::class => EncrypterInvoker::class,
],
'publish' => [
[
'id' => 'config',
'description' => 'The config for encryption.',
'source' => __DIR__ . '/../publish/encryption.php',
'destination' => BASE_PATH . '/config/autoload/encryption.php',
],
],
];
}
}

View File

@ -1,33 +0,0 @@
<?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\Encryption\Contract;
use RuntimeException;
interface EncrypterInterface
{
/**
* Encrypt the given value.
*
* @param mixed $value
* @throws RuntimeException
*/
public function encrypt($value, bool $serialize = true): string;
/**
* Decrypt the given value.
*
* @throws RuntimeException
* @return mixed
*/
public function decrypt(string $payload, bool $unserialize = true);
}

View File

@ -1,229 +0,0 @@
<?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\Encryption;
use Hyperf\Encryption\Contract\EncrypterInterface;
use Hyperf\Encryption\Exception\DecryptException;
use Hyperf\Encryption\Exception\EncryptException;
use Hyperf\Encryption\Exception\InvalidArgumentException;
class Encrypter implements EncrypterInterface
{
/**
* The encryption key.
*
* @var string
*/
protected $key;
/**
* The algorithm used for encryption.
*
* @var string
*/
protected $cipher;
public function __construct(string $key, string $cipher = 'AES-128-CBC')
{
if (empty($key)) {
throw new InvalidArgumentException('Encrypter key Not Set');
}
if (! in_array(strtolower($cipher), openssl_get_cipher_methods())) {
throw new InvalidArgumentException('Encrypter ciphers Not supported');
}
$this->key = $key;
$this->cipher = $cipher;
}
/**
* Encrypt the given value.
*
* @param mixed $value
* @throws \Exception
*/
public function encrypt($value, bool $serialize = true): string
{
$iv = random_bytes(openssl_cipher_iv_length($this->cipher));
// First we will encrypt the value using OpenSSL. After this is encrypted we
// will proceed to calculating a MAC for the encrypted value so that this
// value can be verified later as not having been changed by the users.
$value = \openssl_encrypt(
$serialize ? serialize($value) : $value,
$this->cipher,
$this->key,
0,
$iv
);
if ($value === false) {
throw new EncryptException('Could not encrypt the data.');
}
// Once we get the encrypted value we'll go ahead and base64_encode the input
// vector and create the MAC for the encrypted value so we can then verify
// its authenticity. Then, we'll JSON the data into the "payload" array.
$mac = $this->hash($iv = base64_encode($iv), $value);
$json = json_encode(compact('iv', 'value', 'mac'), JSON_UNESCAPED_SLASHES);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new EncryptException('Could not encrypt the data.');
}
return base64_encode($json);
}
/**
* Encrypt a string without serialization.
*
* @param string $value
* @throws \Exception
* @return string
*/
public function encryptString($value)
{
return $this->encrypt($value, false);
}
/**
* Decrypt the given value.
*
* @return mixed
*/
public function decrypt(string $payload, bool $unserialize = true)
{
$payload = $this->getJsonPayload($payload);
$iv = base64_decode($payload['iv']);
// Here we will decrypt the value. If we are able to successfully decrypt it
// we will then unserialize it and return it out to the caller. If we are
// unable to decrypt this value we will throw out an exception message.
$decrypted = \openssl_decrypt(
$payload['value'],
$this->cipher,
$this->key,
0,
$iv
);
if ($decrypted === false) {
throw new DecryptException('Could not decrypt the data.');
}
return $unserialize ? unserialize($decrypted) : $decrypted;
}
/**
* Decrypt the given string without unserialization.
*
* @param string $payload
* @return string
*/
public function decryptString($payload)
{
return $this->decrypt($payload, false);
}
/**
* Get the encryption key.
*
* @return string
*/
public function getKey()
{
return $this->key;
}
/**
* Create a MAC for the given value.
*
* @param string $iv
* @param mixed $value
* @return string
*/
protected function hash($iv, $value)
{
return hash_hmac('sha256', $iv . $value, $this->key);
}
/**
* Get the JSON array from the given payload.
*
* @param string $payload
* @return array
*/
protected function getJsonPayload($payload)
{
$payload = json_decode(base64_decode($payload), true);
// If the payload is not valid JSON or does not have the proper keys set we will
// assume it is invalid and bail out of the routine since we will not be able
// to decrypt the given value. We'll also check the MAC for this encryption.
if (! $this->validPayload($payload)) {
throw new DecryptException('The payload is invalid.');
}
if (! $this->validMac($payload)) {
throw new DecryptException('The MAC is invalid.');
}
return $payload;
}
/**
* Verify that the encryption payload is valid.
*
* @param mixed $payload
* @return bool
*/
protected function validPayload($payload)
{
return is_array($payload) && isset($payload['iv'], $payload['value'], $payload['mac']) &&
strlen(base64_decode($payload['iv'], true)) === openssl_cipher_iv_length($this->cipher);
}
/**
* Determine if the MAC for the given payload is valid.
*
* @return bool
*/
protected function validMac(array $payload)
{
$calculated = $this->calculateMac($payload, $bytes = random_bytes(16));
return hash_equals(
hash_hmac('sha256', $payload['mac'], $bytes, true),
$calculated
);
}
/**
* Calculate the hash of the given payload.
*
* @param array $payload
* @param string $bytes
* @return string
*/
protected function calculateMac($payload, $bytes)
{
return hash_hmac(
'sha256',
$this->hash($payload['iv'], $payload['value']),
$bytes,
true
);
}
}

View File

@ -1,38 +0,0 @@
<?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\Encryption;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Encryption\Contract\EncrypterInterface;
use Psr\Container\ContainerInterface;
class EncrypterFactory
{
/**
* @var ConfigInterface
*/
protected $config;
public function __construct(ContainerInterface $container)
{
$this->config = $container->get(ConfigInterface::class);
}
public function get(string $name): EncrypterInterface
{
$encryption = $this->config->get('encryption.' . $name, []);
$key = $encryption['key'] ?? '';
$cipher = $encryption['cipher'] ?? 'AES-128-CBC';
return new Encrypter($key, $cipher);
}
}

View File

@ -1,24 +0,0 @@
<?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\Encryption;
use Psr\Container\ContainerInterface;
class EncrypterInvoker
{
public function __invoke(ContainerInterface $container)
{
$factory = $container->get(EncrypterFactory::class);
return $factory->get('default');
}
}

View File

@ -1,16 +0,0 @@
<?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\Encryption\Exception;
class DecryptException extends \RuntimeException
{
}

View File

@ -1,16 +0,0 @@
<?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\Encryption\Exception;
class EncryptException extends \RuntimeException
{
}

View File

@ -1,16 +0,0 @@
<?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\Encryption\Exception;
class InvalidArgumentException extends \InvalidArgumentException
{
}

View File

@ -1,74 +0,0 @@
<?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 HyperfTest\Encryption;
use Hyperf\Config\Config;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Encryption\Contract\EncrypterInterface;
use Hyperf\Encryption\EncrypterFactory;
use Hyperf\Encryption\EncrypterInvoker;
use Hyperf\Utils\ApplicationContext;
use Mockery;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
/**
* @internal
* @coversNothing
*/
class EncrypterTest extends TestCase
{
protected function tearDown(): void
{
Mockery::close();
}
public function mockContainer(): ContainerInterface
{
$container = Mockery::mock(ContainerInterface::class);
ApplicationContext::setContainer($container);
$config = new Config([
'encryption' => [
'default' => [
'key' => '123456',
'cipher' => 'AES-256-CBC',
],
],
]);
$container->shouldReceive('get')->with(ConfigInterface::class)->andReturn($config);
$container->shouldReceive('get')->with(EncrypterFactory::class)->andReturnUsing(function () use ($container) {
return new EncrypterFactory($container);
});
$container->shouldReceive('get')->with(EncrypterInterface::class)->andReturnUsing(function () use ($container) {
return (new EncrypterInvoker())($container);
});
return $container;
}
public function testEncodeString()
{
$input = 'Hello Word.';
$container = $this->mockContainer();
$encrypter = $container->get(EncrypterInterface::class);
$encrypt = $encrypter->encryptString($input);
$this->assertSame($input, $encrypter->decryptString($encrypt));
}
public function testEncodeObject()
{
$input = range(1, 3);
$container = $this->mockContainer();
$encrypter = $container->get(EncrypterInterface::class);
$encrypt = $encrypter->encrypt($input);
$this->assertSame($input, $encrypter->decrypt($encrypt));
}
}

View File

@ -29,6 +29,7 @@ class WatchCommand extends Command
$this->setDescription('watch command');
$this->addOption('file', 'F', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '', []);
$this->addOption('dir', 'D', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '', []);
$this->addOption('no-restart', 'N', InputOption::VALUE_NONE, 'Whether no need to restart server');
}
public function handle()
@ -36,6 +37,7 @@ class WatchCommand extends Command
$option = make(Option::class, [
'dir' => $this->input->getOption('dir'),
'file' => $this->input->getOption('file'),
'restart' => ! $this->input->getOption('no-restart'),
]);
$watcher = make(Watcher::class, [

View File

@ -46,7 +46,12 @@ class Option
*/
protected $scanInterval = 2000;
public function __construct(ConfigInterface $config, array $dir, array $file)
/**
* @var bool
*/
protected $restart = true;
public function __construct(ConfigInterface $config, array $dir, array $file, bool $restart = true)
{
$options = $config->get('watcher', []);
@ -59,6 +64,7 @@ class Option
$this->watchDir = array_unique(array_merge($this->watchDir, $dir));
$this->watchFile = array_unique(array_merge($this->watchFile, $file));
$this->restart = $restart;
}
public function getDriver(): string
@ -90,4 +96,9 @@ class Option
{
return $this->scanInterval > 0 ? $this->scanInterval : 2000;
}
public function isRestart(): bool
{
return $this->restart;
}
}

View File

@ -150,6 +150,9 @@ class Watcher
public function restart($isStart = true)
{
if (! $this->option->isRestart()) {
return;
}
$file = $this->config->get('server.settings.pid_file');
if (empty($file)) {
throw new FileNotFoundException('The config of pid_file is not found.');