Merge remote-tracking branch 'upstream/master' into 1.0-merge

This commit is contained in:
李铭昕 2019-09-11 11:23:46 +08:00
commit 68dc768716
249 changed files with 4220 additions and 849 deletions

View File

@ -5,17 +5,11 @@ sudo: required
matrix:
include:
- php: 7.2
env: SW_VERSION="4.3.6"
- php: 7.2
env: SW_VERSION="4.4.4"
env: SW_VERSION="4.4.5"
- php: 7.3
env: SW_VERSION="4.3.6"
- php: 7.3
env: SW_VERSION="4.4.4"
env: SW_VERSION="4.4.5"
- php: master
env: SW_VERSION="4.3.6"
- php: master
env: SW_VERSION="4.4.4"
env: SW_VERSION="4.4.5"
allow_failures:
- php: master
@ -44,4 +38,5 @@ before_script:
- composer config -g process-timeout 900 && composer update
script:
- composer analyse src/di src/json-rpc
- composer test

View File

@ -1,3 +1,50 @@
# v1.1.0 - TBD
## Added
- [#401](https://github.com/hyperf-cloud/hyperf/pull/401) [#447](https://github.com/hyperf-cloud/hyperf/issues/447) Optimized server and Fixed middleware that user defined does not works.
- [#402](https://github.com/hyperf-cloud/hyperf/pull/402) Added Annotation AsyncQueueMessage.
- [#418](https://github.com/hyperf-cloud/hyperf/pull/418) Allows send WebSocket message to any fd in current server, even the worker process does not hold the fd
- [#420](https://github.com/hyperf-cloud/hyperf/pull/420) Added listener for model.
- [#441](https://github.com/hyperf-cloud/hyperf/pull/441) Automatically close the spare redis client when it is used in low frequency.
- [#500](https://github.com/hyperf-cloud/hyperf/pull/499) Added fluent method calls of `Hyperf\HttpServer\Contract\ResponseInterface`.
- [#523](https://github.com/hyperf-cloud/hyperf/pull/523) Added option `table-mapping` for command `db:model`.
- [#555](https://github.com/hyperf-cloud/hyperf/pull/555) Added global function `swoole_hook_flags` to get the hook flags by constant `SWOOLE_HOOK_FLAGS`, and you could define in `bin/hyperf.php` via `! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);` to define the constant.
## Changed
- [#437](https://github.com/hyperf-cloud/hyperf/pull/437) Changed `Hyperf\Testing\Client` handle exception handlers instead of throw an exception directly.
- [#463](https://github.com/hyperf-cloud/hyperf/pull/463) Simplify `container.php` and improve annotation caching mechanism.
- [#523](https://github.com/hyperf-cloud/hyperf/pull/523) Generate the singular class of an plural table.
config/container.php
```php
<?php
use Hyperf\Di\Container;
use Hyperf\Di\Definition\DefinitionSourceFactory;
use Hyperf\Utils\ApplicationContext;
$container = new Container((new DefinitionSourceFactory(true))());
if (! $container instanceof \Psr\Container\ContainerInterface) {
throw new RuntimeException('The dependency injection container is invalid.');
}
return ApplicationContext::setContainer($container);
```
- [#486](https://github.com/hyperf-cloud/hyperf/pull/486) Changed `getParsedBody` can return JSON formatted data normally.
## Deleted
- [#401](https://github.com/hyperf-cloud/hyperf/pull/401) Deleted class `Hyperf\JsonRpc\HttpServerFactory`, `Hyperf\HttpServer\ServerFactory`, `Hyperf\GrpcServer\ServerFactory`.
- [#402](https://github.com/hyperf-cloud/hyperf/pull/402) Deleted deprecated method `AsyncQueue::delay`.
## Fixed
- [#448](https://github.com/hyperf-cloud/hyperf/pull/448) Fixed TCP Server does not works when HTTP Server or WebSocket Server exists.
# v1.0.16 - TBD
# v1.0.15 - 2019-09-11

View File

@ -13,7 +13,7 @@ In addition to providing `MySQL coroutine client` and `Redis coroutine client`,
# Original intention
Although there are many new PHP frameworks have been appeared, but we still has not seen a perfect framework which has the coexistence of elegant design and ultra-high performance, nor would we find a framework that really paves the way for PHP microservices. For the original intention of Hyperf and its team members, we will continue to invest to it, and you are welcome to join us to participate in open source construction.
Although many new PHP frameworks have appeared, we still haven't seen a comprehensive framework, which introduces an elegant design and ultra-high performance, suitable for PHP microservices and as an evangelist of PHP microservices. For the original intention of Hyperf and its team members, we will continue to invest in it, and you are welcome to join us to participate in open source development.
# Design concept

View File

@ -9,7 +9,7 @@ then
fi
NOW=$(date +%s)
CURRENT_BRANCH="1.0"
CURRENT_BRANCH="master"
VERSION=$1
BASEPATH=$(cd `dirname $0`; cd ../src/; pwd)

View File

@ -3,7 +3,7 @@
set -e
set -x
CURRENT_BRANCH="1.0"
CURRENT_BRANCH="master"
BASEPATH=$(cd `dirname $0`; cd ../src/; pwd)
REPOS=$@

View File

@ -3,7 +3,7 @@
set -e
set -x
CURRENT_BRANCH="1.0"
CURRENT_BRANCH="master"
BASEPATH=$(cd `dirname $0`; cd ../src/; pwd)
REPOS=$@

View File

@ -11,5 +11,6 @@ declare(strict_types=1);
*/
! defined('BASE_PATH') && define('BASE_PATH', __DIR__);
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);
require_once BASE_PATH . '/vendor/autoload.php';

View File

@ -45,8 +45,9 @@
"malukenho/docheader": "^0.1.6",
"mockery/mockery": "^1.0",
"php-di/php-di": "^6.0",
"phpstan/phpstan": "^0.11.15",
"phpunit/phpunit": "^7.0.0",
"swoft/swoole-ide-helper": "^4.3",
"swoft/swoole-ide-helper": "dev-master",
"symfony/property-access": "^4.3",
"symfony/serializer": "^4.3"
},
@ -143,6 +144,7 @@
"Hyperf\\Logger\\": "src/logger/src/",
"Hyperf\\Memory\\": "src/memory/src/",
"Hyperf\\ModelCache\\": "src/model-cache/src/",
"Hyperf\\ModelListener\\": "src/model-listener/src/",
"Hyperf\\Paginator\\": "src/paginator/src/",
"Hyperf\\Pool\\": "src/pool/src/",
"Hyperf\\Process\\": "src/process/src/",
@ -156,6 +158,7 @@
"Hyperf\\Snowflake\\": "src/snowflake/src/",
"Hyperf\\Swagger\\": "src/swagger/src/",
"Hyperf\\SwooleEnterprise\\": "src/swoole-enterprise/src/",
"Hyperf\\SwooleTracker\\": "src/swoole-tracker/src/",
"Hyperf\\Task\\": "src/task/src/",
"Hyperf\\Testing\\": "src/testing/src/",
"Hyperf\\Tracer\\": "src/tracer/src/",
@ -173,6 +176,7 @@
"HyperfTest\\Amqp\\": "src/amqp/tests/",
"HyperfTest\\AsyncQueue\\": "src/async-queue/tests/",
"HyperfTest\\Cache\\": "src/cache/tests/",
"HyperfTest\\Command\\": "src/command/tests/",
"HyperfTest\\ConfigAliyunAcm\\": "src/config-aliyun-acm/tests/",
"HyperfTest\\ConfigApollo\\": "src/config-apollo/tests/",
"HyperfTest\\ConfigEtcd\\": "src/config-etcd/tests/",
@ -196,6 +200,7 @@
"HyperfTest\\LoadBalancer\\": "src/load-balancer/tests/",
"HyperfTest\\Logger\\": "src/logger/tests/",
"HyperfTest\\ModelCache\\": "src/model-cache/tests/",
"HyperfTest\\ModelListener\\": "src/model-listener/tests/",
"HyperfTest\\Paginator\\": "src/paginator/tests/",
"HyperfTest\\Pool\\": "src/pool/tests/",
"HyperfTest\\Process\\": "src/process/tests/",
@ -205,6 +210,7 @@
"HyperfTest\\ServiceGovernance\\": "src/service-governance/tests/",
"HyperfTest\\Snowflake\\": "src/snowflake/tests/",
"HyperfTest\\Task\\": "src/task/tests/",
"HyperfTest\\Testing\\": "src/testing/tests/",
"HyperfTest\\Translation\\": "src/translation/tests/",
"HyperfTest\\Utils\\": "src/utils/tests/",
"HyperfTest\\WebSocketClient\\": "src/websocket-client/tests/"
@ -225,6 +231,7 @@
"Hyperf\\ConfigApollo\\ConfigProvider",
"Hyperf\\ConfigEtcd\\ConfigProvider",
"Hyperf\\Config\\ConfigProvider",
"Hyperf\\Constants\\ConfigProvider",
"Hyperf\\Consul\\ConfigProvider",
"Hyperf\\Crontab\\ConfigProvider",
"Hyperf\\DbConnection\\ConfigProvider",
@ -244,6 +251,7 @@
"Hyperf\\Logger\\ConfigProvider",
"Hyperf\\Memory\\ConfigProvider",
"Hyperf\\ModelCache\\ConfigProvider",
"Hyperf\\ModelListener\\ConfigProvider",
"Hyperf\\Paginator\\ConfigProvider",
"Hyperf\\Pool\\ConfigProvider",
"Hyperf\\Process\\ConfigProvider",
@ -256,6 +264,7 @@
"Hyperf\\Snowflake\\ConfigProvider",
"Hyperf\\Swagger\\ConfigProvider",
"Hyperf\\SwooleEnterprise\\ConfigProvider",
"Hyperf\\SwooleTracker\\ConfigProvider",
"Hyperf\\Task\\ConfigProvider",
"Hyperf\\Tracer\\ConfigProvider",
"Hyperf\\Translation\\ConfigProvider",
@ -272,7 +281,8 @@
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml",
"license-check": "docheader check src/ test/",
"cs-fix": "php-cs-fixer fix $1",
"json-fix": "./bin/composer-json-fixer"
"json-fix": "./bin/composer-json-fixer",
"analyse": "phpstan analyse --memory-limit 300M -l 3 -c phpstan.neon"
},
"minimum-stability": "dev",
"prefer-stable": true

View File

@ -2,11 +2,13 @@
Hyperf 是基于 `Swoole 4.3+` 实现的高性能、高灵活性的 PHP 协程框架,内置协程服务器及大量常用的组件,性能较传统基于 `PHP-FPM` 的框架有质的提升,提供超高性能的同时,也保持着极其灵活的可扩展性,标准组件均基于 [PSR 标准](https://www.php-fig.org/psr) 实现,基于强大的依赖注入设计,保证了绝大部分组件或类都是 `可替换``可复用` 的。
框架组件库除了常见的协程版的 `MySQL 客户端`、`Redis 客户端`,还为您准备了协程版的 `Eloquent ORM`、`JSON RPC 服务端及客户端`、`GRPC 服务端及客户端`、`Zipkin (OpenTracing) 客户端`、`Guzzle HTTP 客户端`、`Elasticsearch 客户端`、`Consul 客户端`、`ETCD 客户端`、`AMQP 组件`、`Apollo 配置中心`、`阿里云 ACM 应用配置管理`、`基于令牌桶算法的限流器`、`通用连接池`、`熔断器`、`Swagger 文档生成` 等组件省去了自己实现对应协程版本的麻烦Hyperf 还提供了 `依赖注入`、`注解`、`AOP 面向切面编程`、`中间件`、`自定义进程`、`事件管理器`、`Redis/RabbitMQ 消息队列`、`自动模型缓存` 等非常便捷的功能,满足丰富的技术场景和业务场景,开箱即用。
框架组件库除了常见的协程版的 `MySQL 客户端`、`Redis 客户端`,还为您准备了协程版的 `Eloquent ORM`、`JSON RPC 服务端及客户端`、`GRPC 服务端及客户端`、`WebSocket 服务端和客户端`、`Zipkin (OpenTracing) 客户端`、`Guzzle HTTP 客户端`、`Elasticsearch 客户端`、`Consul 客户端`、`ETCD 客户端`、`AMQP 组件`、`基于 Redis 实现的消息队列`、`Apollo 配置中心`、`ETCD 配置中心`、`阿里云 ACM 配置中心`、`基于令牌桶算法的限流器`、`通用连接池`、`熔断器`、`Swagger 文档自动生成`、`Swoole Tracker (Swoole Enterprise)`、`Blade 和 Smarty 视图引擎` 等组件的提供也省去了自己去实现对应协程版本的麻烦。
Hyperf 还提供了 `依赖注入`、`注解`、`AOP 面向切面编程`、`中间件`、`自定义进程`、`事件管理器`、`自动模型缓存`、`Crontab 秒级定时任务` 等非常便捷的功能,满足丰富的技术场景和业务场景,开箱即用。
# 框架初衷
尽管现在基于 PHP 语言开发的框架处于一个百争鸣的时代,但仍旧未能看到一个优雅的设计与超高性能的共存的完美框架,亦没有看到一个真正为 PHP 微服务铺路的框架,此为 Hyperf 及其团队成员的初衷,我们将持续投入并为此付出努力,也欢迎你加入我们参与开源建设。
尽管现在基于 PHP 语言开发的框架处于一个百争鸣的时代,但仍旧未能看到一个优雅的设计与超高性能的共存的完美框架,亦没有看到一个真正为 PHP 微服务铺路的框架,此为 Hyperf 及其团队成员的初衷,我们将持续投入并为此付出努力,也欢迎你加入我们参与开源建设。
# 设计理念

View File

@ -130,6 +130,7 @@ namespace App\Command;
use Hyperf\Command\Annotation\Command;
use Hyperf\Command\Command as HyperfCommand;
use Symfony\Component\Console\Input\InputArgument;
/**
* @Command

View File

@ -64,7 +64,7 @@ class FooAspect extends AbstractAspect
## 代理类缓存
所有被 AOP 影响的类,都会在 `./runtime/container/proxy/` 文件夹内生成对应的 `代理类缓存`,服务启动时,如果类所对应的代理类缓存存在,则不会重新生成直接使用缓存,即 `Aspect` 的切入范围发生了改变。不存在时,则会自动重新生成新的代理类缓存。
所有被 AOP 影响的类,都会在 `./runtime/container/proxy/` 文件夹内生成对应的 `代理类缓存`,服务启动时,如果类所对应的代理类缓存存在,则不会重新生成直接使用缓存,即使 `Aspect` 的切入范围发生了改变。不存在时,则会自动重新生成新的代理类缓存。
在部署生产环境时,我们可能会希望 Hyperf 提前将所有代理类提前生成,而不是使用时动态的生成,可以通过 `php bin/hyperf.php di:init-proxy` 命令来生成所有代理类,该命令会忽视现有的代理类缓存,全部重新生成。

View File

@ -23,7 +23,7 @@
## 日志
- [hyperf/logger](https://github.com/hyperf-cloud/logger) Hyperf 官方提供的基于 PSR-3 的日志管理器
- [hyperf/logger](https://github.com/hyperf-cloud/logger) Hyperf 官方提供的基于 PSR-3 的日志管理器,一个基于 monolog 的抽象及封装
## 命令
@ -51,27 +51,62 @@
- [hyperf/consul](https://github.com/hyperf-cloud/consul) Hyperf 官方提供的 Consul 协程客户端
- [hyperf/elasticsearch](https://github.com/hyperf-cloud/elasticsearch) Hyperf 官方提供的 Elasticsearch 协程客户端
- [hyperf/grpc-client](https://github.com/hyperf-cloud/grpc-client) Hyperf 官方提供的 GRPC 协程客户端
- [hyperf/etcd](https://github.com/hyperf-cloud/etcd) Hyperf 官方提供的 ETCD 协程客户端
- [hyperf/rpc-client](https://github.com/hyperf-cloud/rpc-client) Hyperf 官方提供的通用 RPC 抽象协程客户端
- [hyperf/guzzle](https://github.com/hyperf-cloud/guzzle) Hyperf 官方提供的 Guzzle 协程客户端
- [hyperf/guzzle](https://github.com/hyperf-cloud/guzzle) Hyperf 官方提供的 Guzzle HTTP 协程客户端
- [hyperf/redis](https://github.com/hyperf-cloud/redis) Hyperf 官方提供的 Redis 协程客户端
- [hyperf/websocket-client](https://github.com/hyperf-cloud/websocket-client) Hyperf 官方提供的 WebSocket 协程客户端
- [hyperf/cache](https://github.com/hyperf-cloud/cache) Hyperf 官方提供的基于 PSR-16 的缓存协程客户端
- [hyperf/cache](https://github.com/hyperf-cloud/cache) Hyperf 官方提供的基于 PSR-16 的缓存协程客户端,支持注解的使用方式
## 消息队列
- [hyperf/amqp](https://github.com/hyperf-cloud/amqp) Hyperf 官方提供的 AMQP 协程组件
- [hyperf/async-queue](https://github.com/hyperf-cloud/async-queue) Hyperf 官方提供的简单的异步队列组件
- [hyperf/async-queue](https://github.com/hyperf-cloud/async-queue) Hyperf 官方提供的简单的基于 Redis 的异步队列组件
## 配置中心
- [hyperf/config-apollo](https://github.com/hyperf-cloud/config-apollo) Hyperf 官方提供的 Apollo 配置中心接入组件
- [hyperf/config-aliyun-acm](https://github.com/hyperf-cloud/config-aliyun-acm) Hyperf 官方提供的阿里云 ACM 应用配置服务接入组件
- [hyperf/config-etcd](https://github.com/hyperf-cloud/config-etcd) Hyperf 官方提供的 ETCD 配置中心接入组件
## RPC
- [hyperf/json-rpc](https://github.com/hyperf-cloud/json-rpc) Hyperf 官方提供的 JSON-RPC 协议组件
## 服务治理
- [hyperf/json-rpc](https://github.com/hyperf-cloud/json-rpc) Hyperf 官方提供的 JSON-RPC 协议组件
- [hyperf/rate-limit](https://github.com/hyperf-cloud/rate-limit) Hyperf 官方提供的基于令牌桶算法的限流组件
- [hyperf/load-balancer](https://github.com/hyperf-cloud/load-balancer) Hyperf 官方提供的负载均衡组件
- [hyperf/service-gevernance](https://github.com/hyperf-cloud/service-gevernance) Hyperf 官方提供的服务治理组件
- [hyperf/tracer](https://github.com/hyperf-cloud/tracer) Hyperf 官方提供的 OpenTracing 分布式调用链追踪组件
- [hyperf/circuit-breaker](https://github.com/hyperf-cloud/circuit-breaker) Hyperf 官方提供的服务熔断组件
## 定时任务
- [hyperf/crontab](https://github.com/hyperf-cloud/crontab) Hyperf 官方提供的秒级定时任务组件
## ID 生成器
- [hyperf/snowflake](https://github.com/hyperf-cloud/snowflake) Hyperf 官方提供的 Snowflake ID 生成器组件 (beta)
## 文档生成
- [hyperf/swagger](https://github.com/hyperf-cloud/swagger) Hyperf 官方提供的 Swagger 文档自动生成组件 (beta)
## Graphql
- [hyperf/graphql](https://github.com/hyperf-cloud/graphql) Hyperf 官方提供的 Graphql 服务端组件 (beta)
## 热更新/热重载
- [ha-ni-cc/hyperf-watch](https://github.com/ha-ni-cc/hyperf-watch) 一个基于 fswatch 实现的通用热更新组件
- [mix-php/swoolefor](https://github.com/mix-php/swoolefor) 一个由 Mixphp 实现的通用热更新组件
- [buexplain/go-watch](https://github.com/buexplain/go-watch) 一个基于 Go 语言实现的通用热更新组件
- [remy/nodemon](https://github.com/remy/nodemon) 一个基于 node.js 实现的通用热更新组件
## Swoole
- - [hyperf/swoole-tracker](https://github.com/hyperf-cloud/swoole-tracker) Hyperf 官方提供的对接 Swoole Tracker 的组件,提供阻塞分析、性能分析、内存泄漏分析、运行状态及调用统计等功能
- - [hyperf/task](https://github.com/hyperf-cloud/task) Hyperf 官方提供的 Task 组件,对 Swoole 的 Task 机制进行了封装及抽象,提供便捷的注解用法
> Warning: 请勿于生产环境使用 `热更新/热重载` 功能

View File

@ -74,7 +74,7 @@ class UserService
### 清理注解缓存
当然,如果我们数据库中的数据改变了,如删除缓存呢?这里就需要用到后面的监听器。下面新建一个 Service 提供一方法,来帮我们处理缓存。
当然,如果我们数据库中的数据改变了,如删除缓存呢?这里就需要用到后面的监听器。下面新建一个 Service 提供一方法,来帮我们处理缓存。
```php
<?php

View File

@ -13,15 +13,15 @@ composer require hyperf/circuit-breaker
## 使用熔断器
熔断器的使用十分简单,只需要加入 `Hyperf\CircuitBreaker\Annotation\CircuitBreaker` 注解,就可以根据规定策略,进行熔断。
比如我们需要到另外服务中查询用户列表,用户列表需要关联很多的表,查询效率较低,但平常并发量不高的时候,相应速度还说得过去。一旦并发量激增,就会导致响应速度变慢,并会使对方服务出现慢查。这个时候,我们只需要配置一下熔断超时时间 `timeout` 为 0.05 秒,失败计数 `failCounter` 超过 1 次后熔断,相应 `fallback``App\UserService` 类的 `searchFallback` 方法。这样当响应超时并触发熔断后,就不会再请求对端的服务了,而是直接将服务降级从当前项目中返回数据,即根据 `fallback` 指定的方法来进行返回。
比如我们需要到另外服务中查询用户列表,用户列表需要关联很多的表,查询效率较低,但平常并发量不高的时候,相应速度还说得过去。一旦并发量激增,就会导致响应速度变慢,并会使对方服务出现慢查。这个时候,我们只需要配置一下熔断超时时间 `timeout` 为 0.05 秒,失败计数 `failCounter` 超过 1 次后熔断,相应 `fallback``App\Service\UserService` 类的 `searchFallback` 方法。这样当响应超时并触发熔断后,就不会再请求对端的服务了,而是直接将服务降级从当前项目中返回数据,即根据 `fallback` 指定的方法来进行返回。
```php
<?php
declare(strict_types=1);
namespace App\Services;
namespace App\Service;
use App\UserServiceClient;
use App\Service\UserServiceClient;
use Hyperf\CircuitBreaker\Annotation\CircuitBreaker;
use Hyperf\Di\Annotation\Inject;
@ -34,7 +34,7 @@ class UserService
private $client;
/**
* @CircuitBreaker(timeout=0.05, failCounter=1, successCounter=1, fallback="App\UserService::searchFallback")
* @CircuitBreaker(timeout=0.05, failCounter=1, successCounter=1, fallback="App\Service\UserService::searchFallback")
*/
public function search($offset, $limit)
{

View File

@ -130,6 +130,7 @@ namespace App\Command;
use Hyperf\Command\Annotation\Command;
use Hyperf\Command\Command as HyperfCommand;
use Symfony\Component\Console\Input\InputArgument;
/**
* @Command

View File

@ -10,4 +10,8 @@
由于微信群无法直接加入,故可先加下方二维码好友,并声明目的,再拉您入群。
## Contributor 群
为了更好的发展 Hyperf 的社区,我们专门为 Hyperf Contributor 提供了一个微信群,以便我们对 Pull Request 或迭代计划进行更详细的探讨,由于微信群无法直接加入,故可先加下方二维码好友,并声明目的且带上您已经被 Merged 的 Pull Request再拉您入群。
![wechat](./imgs/wechat.jpg ':size=375')

View File

@ -0,0 +1,78 @@
# ConfigProvider 机制
ConfigProvider 机制对于 Hyperf 组件化来说是个非常重要的机制,`组件间的解耦` 和 `组件的独立性` 以及 `组件的可重用性` 都是基于这个机制才得以实现。
# 什么是 ConfigProvider 机制
简单来说,就是每个组件都会提供一个 `ConfigProvider`,通常是在组件的根目录提供一个 `ConfigProvider` 的类,`ConfigProvider` 会提供对应组件的所有配置信息,这些信息都会被 Hyperf 框架在启动时加载,最终`ConfigProvider` 内的配置信息会被合并到 `Hyperf\Contract\ConfigInterface` 对应的实现类去,而 `dependencies` 信息则会合并到 `Hyperf\Di\Definition\DefinitionSource` 去,从而实现该组件在 Hyperf 框架下使用时要进行的配置初始化。
`ConfigProvider` 本身不具备任何依赖,不继承任何的抽象类和不要求实现任何的接口,只需提供一个 `__invoke` 方法并返回一个对应配置结构的数组即可。
# 如何定义一个 ConfigProvider
通常来说,`ConfigProvider` 会定义在组件的根目录下,一个 `ConfigProvider` 类通常如下:
```php
<?php
namespace Hyperf\Foo;
class ConfigProvider
{
public function __invoke(): array
{
return [
// 合并到 config/dependencies.php 文件
'dependencies' => [],
// 合并到 config/autoload/annotations.php 文件
'scan' => [
'paths' => [
__DIR__,
],
],
// 默认 Command 的定义,合并到 Hyperf\Contract\ConfigInterface 内,换个方式理解也就是与 config/autoload/commands.php 对应
'commands' => [],
// 与 commands 类似
'listeners' => [],
];
}
}
```
只创建一个类并不会被 Hyperf 自动的加载,您仍需在组件的 `composer.json` 添加一些定义,告诉 Hyperf 这是一个 ConfigProvider 类需要被加载,您需要在组件内的 `composer.json` 文件内增加 `extra.hyperf.config` 配置,并指定对应的 `ConfigProvider` 类的命名空间,如下所示:
```json
{
"name": "hyperf/foo",
"require": {
"php": ">=7.2"
},
"autoload": {
"psr-4": {
"Hyperf\\Foo\\": "src/"
}
},
"extra": {
"hyperf": {
"config": "Hyperf\\Foo\\ConfigProvider"
}
}
}
```
定义了之后需执行 `composer install``composer update``composer dump-autoload` 等会让 Composer 重新生成 `composer.lock` 文件的命令,才能被正常读取。
# ConfigProvider 机制的执行流程
关于 `ConfigProvider` 的配置并非一定就是这样去划分,这是一些约定成俗的格式,实际上最终如何来解析这些配置的决定权也在于用户,用户可通过修改 Skeleton 项目的 `config/container.php` 文件内的代码来调整相关的加载,也就意味着,`config/container.php` 文件决定了 `ConfigProvider` 的扫描和加载。
# 组件设计规范
由于 `composer.json` 内的 `extra` 属性在数据不被利用时无其它作用和影响,故这些组件内的定义在其它框架使用时,不会造成任何的干扰和影响,顾 `ConfigProvider` 是一种仅作用于 Hyperf 框架的机制,对其它没有利用此机制的框架不会造成任何的影响,这也就为组件的复用打下了基础,但这也要求在进行组件设计时,必须遵循以下规范:
- 所有类的设计都必须允许通过标准 `OOP` 的使用方式来使用,所有 Hyperf 专有的功能必须作为增强功能并以单独的类来提供,也就意味着在非 Hyperf 框架下仍能通过标准的手段来实现组件的使用;
- 组件的依赖设计如果可满足 [PSR 标准](https://www.php-fig.org/psr) 则优先满足且依赖对应的接口而不是实现类;如 [PSR 标准](https://www.php-fig.org/psr) 没有包含的功能,则可满足由 Hyperf 定义的契约库 [Hyperf/contract](https://github.com/hyperf-cloud/contract) 内的接口时优先满足且依赖对应的接口而不是实现类;
- 对于实现 Hyperf 专有功能所增加的增强功能类,通常来说也会对 Hyperf 的一些组件有依赖,那么这些组件的依赖不应该写在 `composer.json``require` 项,而是写在 `suggust` 项作为建议项存在;
- 组件设计时不应该通过注解进行任何的依赖注入,注入方式应只使用 `构造函数注入` 的方式,这样同时也能满足在 `OOP` 下的使用;
- 组件设计时不应该通过注解进行任何的功能定义,功能定义应只通过 `ConfigProvider` 来定义;
- 类的设计时应尽可能的不储存状态数据,因为这会导致这个类不能作为长生命周期的对象来提供,也无法很方便的使用依赖注入功能,这样会在一定程度下降低性能,状态数据应都通过 `Hyperf\Utils\Context` 协程上下文来储存;

View File

@ -37,6 +37,6 @@ Hello Hyperf.
## 避免协程间数据混淆
在传统的 PHP-FPM 的框架里,会习惯提供一个 `AbstractController` 或其它命名的 `Controller 抽象父类`,然后定义的 `Controller` 需要基础它用于获取一些请求数据或进行一些返回操作,在 Hyperf 里是 **不能这样做** 的,因为在 Hyperf 内绝大部分的对象包括 `Controller` 都是以 `单例(Singleton)` 形式存在的,这也是为了更好的复用对象,而对于与请求相关的数据在协程下也是需要储存到 `协程上下文(Context)` 内的,所以在编写代码时请务必注意 **不要** 将单个请求相关的数据储存在类属性内,包括非静态属性。
在传统的 PHP-FPM 的框架里,会习惯提供一个 `AbstractController` 或其它命名的 `Controller 抽象父类`,然后定义的 `Controller` 需要继承它用于获取一些请求数据或进行一些返回操作,在 Hyperf 里是 **不能这样做** 的,因为在 Hyperf 内绝大部分的对象包括 `Controller` 都是以 `单例(Singleton)` 形式存在的,这也是为了更好的复用对象,而对于与请求相关的数据在协程下也是需要储存到 `协程上下文(Context)` 内的,所以在编写代码时请务必注意 **不要** 将单个请求相关的数据储存在类属性内,包括非静态属性。
当然如果非要通过类属性来储存请求数据的话,也不是没有办法的,我们可以注意到我们获取 `请求(Request)``响应(Response)` 对象时是通过注入 `Hyperf\HttpServer\Contract\RequestInterface``Hyperf\HttpServer\Contract\ResponseInterface` 来获取的,那对应的对象不也是个单例吗?这里是如何做到协程安全的呢?就 `RequestInterface` 来举例,对应的 `Hyperf\HttpServer\Request` 对象内部在获取 `PSR-7 请求对象` 时,都是从 `协程上下文(Context)` 获取的,所以实际使用的类仅仅是一个代理类,实际调用的都是从 `协程上下文(Context)` 中获取的。

View File

@ -95,7 +95,7 @@ class IndexController
}
```
> 注意调用方也就是 `IndexController` 必须是由 DI 创建的对象才能完成自动注入Controller 默认是由 DI 创建的
> 注意调用方也就是 `IndexController` 必须是由 DI 创建的对象才能完成自动注入Controller 默认是由 DI 创建的,直接 `new` 该对象不会生效;
> 使用 `@Inject` 注解时需 `use Hyperf\Di\Annotation\Inject;` 命名空间;

View File

@ -1,7 +1,7 @@
# 异常处理器
`Hyperf` 里,业务代码都运行在 `Worker进程` 上,也就意味着一旦任意一个请求的业务存在没有捕获处理的异常的话,都会导致对应的 `Worker进程` 被中断退出,虽然被中断的 `Worker进程` 仍会被重新拉起,但对服务而也是不能接受的,且捕获异常并输出合理的报错内容给客户端也是更加友好的。
我们可以通过对各个 `server` 定义不同的 `异常处理器(ExceptionHandler)`,一旦业务流程存在没有捕获的异常,会被传递到已注册的 `异常处理器(ExceptionHandler)` 去处理。
`Hyperf` 里,业务代码都运行在 `Worker进程` 上,也就意味着一旦任意一个请求的业务存在没有捕获处理的异常的话,都会导致对应的 `Worker进程` 被中断退出,虽然被中断的 `Worker进程` 仍会被重新拉起,但对服务而也是不能接受的,且捕获异常并输出合理的报错内容给客户端也是更加友好的。
我们可以通过对各个 `server` 定义不同的 `异常处理器(ExceptionHandler)`,一旦业务流程存在没有捕获的异常,会被传递到已注册的 `异常处理器(ExceptionHandler)` 去处理。
## 自定义一个异常处理
@ -57,7 +57,7 @@ class FooExceptionHandler extends ExceptionHandler
}
// 交给下一个异常处理器
return $respose;
return $response;
// 或者不做处理直接屏蔽异常
}

BIN
doc/zh/imgs/snowflake.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,6 +1,6 @@
# 日志
`hyperf/logger` 组件是基于 [psr/logger](https://github.com/php-fig/logger) 实现的,默认使用 [monolog/monolog](https://github.com/Seldaek/monolog) 作为驱动,在 `hyperf-skeleton` 项目内默认提供了一些日志配置,默认使用 `Monolog\Handler\StreamHandler`, 由于 `Swoole` 已经对 `fopen`, `fwrite` 等函数进行了协程化处理,所以只要不将 `useLocking` 参数设置为 `true`,就是协程安全的。
`hyperf/logger` 组件是基于 [psr/logger](https://github.com/php-fig/log) 实现的,默认使用 [monolog/monolog](https://github.com/Seldaek/monolog) 作为驱动,在 `hyperf-skeleton` 项目内默认提供了一些日志配置,默认使用 `Monolog\Handler\StreamHandler`, 由于 `Swoole` 已经对 `fopen`, `fwrite` 等函数进行了协程化处理,所以只要不将 `useLocking` 参数设置为 `true`,就是协程安全的。
## 安装
@ -58,8 +58,8 @@ class DemoService
public function __construct(LoggerFactory $loggerFactory)
{
// default 对应 config/autoload/logger.php 内的 key
$this->logger = $loggerFactory->get('default');
// 第一个参数对应日志的 name, 第二个参数对应 config/autoload/logger.php 内的 key
$this->logger = $loggerFactory->get('log', 'default');
}
public function method()

View File

@ -52,4 +52,4 @@
虽然 `微服务架构(Microservice)` 好处众多,但 **微服务不是银弹 ** ,您需要面对所有分布式系统都需要面对的复杂性,你可能需要在部署、测试和监控上做很多的工作,在服务间调用、服务的可靠性上做很多工作,甚至您还需要处理类似于 分布式事务 或者与 CAP 相关的问题。尽管 `Hyperf` 已经为您解决了许多的问题,但在实施 `微服务架构(Microservice)` 之前您的团队必须储备足够的分布式系统相关的知识体系,以面对很多您在 `单体架构(Monolithic architecture)` 下可能没有面临过甚至没有考虑过的问题。
*| 本章节部分内容译自 Sam Newman 的 《Building Microservices 》*
*| 本章节部分内容译自 Sam Newman 的 《Building Microservices》*

View File

@ -120,7 +120,7 @@ class IndexController
#### 定义方法级别的中间件
在通过配置文件的方式配置中间件时定义到方法级别上很简单,那么要通过注解的形式定义到方法级别呢?您只需将注解直接定义到方法上即可。
方法级别上的中间件会优先于类级别的中间件,我们通过代码来举例一下:
类级别上的中间件会优先于方法级别的中间件,我们通过代码来举例一下:
```php
<?php
@ -221,7 +221,7 @@ class FooMiddleware implements MiddlewareInterface
## 中间件的执行顺序
我们从上面可以了解到总共有 `3` 种级别的中间件,分别为 `全局中间件`、`类级别中间件`、`方法级别中间件`,如果都定义了这些中间件,执行顺序为:`全局中间件 -> 方法级别中间件 -> 类级别中间件`。
我们从上面可以了解到总共有 `3` 种级别的中间件,分别为 `全局中间件`、`类级别中间件`、`方法级别中间件`,如果都定义了这些中间件,执行顺序为:`全局中间件 -> 类级别中间件 -> 方法级别中间件`。
## 全局更改请求和响应对象

View File

@ -81,7 +81,7 @@ if ($paginator->hasMorePages()) {
}
```
## 获取上一页和下一页的 URL
## 获取对应分页的 URL
```php
<?php
@ -89,12 +89,34 @@ if ($paginator->hasMorePages()) {
$nextPageUrl = $paginator->nextPageUrl();
// 上一页的 URL
$previousPageUrl = $paginator->previousPageUrl();
```
## 获取指定页数的 URL
```php
<?php
// 获取指定 $page 页数的 URL
$url = $paginator->url($page);
```
## 是否处于第一页
```php
<?php
$onFirstPage = $paginator->onFirstPage();
```
## 是否有更多分页
```php
<?php
$hasMorePages = $paginator->hasMorePages();
```
## 每页的数据条数
```php
<?php
$perPage = $paginator->perPage();
```
## 数据总数
```php
<?php
$total = $paginator->total();
```

View File

@ -222,10 +222,11 @@ class IndexController
## 启动 Hyperf 服务
由于 `Hyperf` 内置了协程服务器,也就意味着 `Hyperf` 将以 `CLI` 的形式去运行,所以在定义好路由及实际的逻辑代码之后,我们需要在项目根目录并通过命令行运行 `php bin/hyperf.php start` 来启动服务。
`Console` 界面显示服务启动后便可通过 `cURL` 或 浏览器对服务正常发起访问了,默认情况下上面的例子是访问 `http://127.0.0.1:9501/index/info?id=1`
`Console` 界面显示服务启动后便可通过 `cURL` 或 浏览器对服务正常发起访问了,默认服务会提供一个首页 `http://127.0.0.1:9501/`,对于本章示例引导的情况下,也就是上面的例子所对应的访问地址为 `http://127.0.0.1:9501/index/info?id=1`
## 重新加载代码
由于 `Hyperf` 是持久化的 `CLI` 应用,也就意味着一旦进程启动,已解析的 `PHP` 代码会持久化在进程中,也就意味着启动服务后您再修改的 `PHP` 代码不会改变已启动的服务,如您希望服务重新加载您修改后的代码,您需要通过在启动的 `Console` 中键入 `CTRL + C` 终止服务,再重新执行启动命令完成重启和重新加载。
> Tips: 您也可以将启动 Server 的命令配置在 IDE 上,便可直接通过 IDE 的 `启动/停止` 操作快捷的完成 `启动服务``重启服务` 的操作。
> 且非视图开发时可以采用 [TDD(Test-Driven Development)](https://baike.baidu.com/item/TDD/9064369) 测试驱动开发来进行开发,这样不仅可以省略掉服务重启和频繁切换窗口的麻烦,还可保证接口数据的正确性。

View File

@ -28,17 +28,22 @@ swoole.use_shortname = 'Off'
runtime/container/proxy/
```
清理命令
```
重新生成缓存命令,新缓存会覆盖原目录
```bash
php bin/hyperf.php di:init-proxy
```
所以单测命令可以使用以下代替
删除代理类缓存
```bash
rm -rf ./runtime/container/proxy
```
所以单测命令可以使用以下代替:
```bash
php bin/hyperf.php di:init-proxy && composer test
```
同理,启动命令可以使用以下代替
```
```bash
php bin/hyperf.php di:init-proxy && php bin/hyperf.php start
```

View File

@ -86,7 +86,7 @@ use Hyperf\RateLimit\Annotation\RateLimit;
/**
* @Controller(prefix="rate-limit")
* @RateLimit(limitCallback={RateLimitController::class, 'limitCallback'})
* @RateLimit(limitCallback={RateLimitController::class, "limitCallback"})
*/
class RateLimitController
{

View File

@ -115,7 +115,7 @@ $result = $redis->keys('*');
### 使用工厂类
在每个库对应一个静态的场景时,通过代理类是一种很好的区分的方法,但有时候需求可能会更加的动态,这时候我们可以通过 `Hyperf\Redis\RedisFactory` 工厂类来动态的传递 `poolName` 来获得对应的连接池的客户端,而无需为每个库创建代理类,示例如下:
在每个库对应一个固定的使用场景时,通过代理类是一种很好的区分的方法,但有时候需求可能会更加的动态,这时候我们可以通过 `Hyperf\Redis\RedisFactory` 工厂类来动态的传递 `poolName` 来获得对应的连接池的客户端,而无需为每个库创建代理类,示例如下:
```php
<?php

View File

@ -125,10 +125,10 @@ class IndexController
## 分块传输编码 Chunk
## 返回文件下载
## 文件下载
`Hyperf\HttpServer\Contract\ResponseInterface` 提供了 `download(string $file, string $name = '')` 返回一个已设置下载文件状态的 `Psr7ResponseInterface` 对象。
如果请求中带有 `if-match``if-none-match` 的请求头Hyperf 也会根据协议标准与 `ETag` 进行比较,如果一致则会返回一个 `304` 状态码的响应。
如果请求中带有 `if-match``if-none-match` 的请求头Hyperf 也会根据协议标准与 `ETag` 进行比较,如果一致则会返回一个 `304` 状态码的响应。
`download` 方法:

View File

@ -1,5 +1,31 @@
# Snowflake
## 算法介绍
`Snowflake` 是由 Twitter 提出的一个分布式全局唯一 ID 生成算法,算法生成 `ID` 的结果是一个 `64bit` 大小的长整,标准算法下它的结构如下图:
![snowflake](./imgs/snowflake.jpeg)
- `1位`,不用。
- 二进制中最高位为符号位,我们生成的 `ID` 一般都是正整数,所以这个最高位固定是 0。
- `41位`,用来记录时间戳(毫秒)。
- `41位` 可以表示 `2^41 - 1` 个数字。
- 也就是说 `41位` 可以表示 `2^41 - 1` 个毫秒的值,转化成单位年则是 `(2^41 - 1) / (1000 * 60 * 60 * 24 * 365)` 约为 `69` 年。
- `10位`,用来记录工作机器 `ID`
- 可以部署在 `2^10``1024` 个节点,包括 `5``DatacenterId``5``WorkerId`
- `12位`,序列号,用来记录同毫秒内产生的不同 `id`
- `12位` 可以表示的最大正整数是 `2^12 - 1``4095` 个数字,来表示同一机器同一时间截(毫秒)内产生的 `4095``ID` 序号。
`Snowflake` 可以保证:
- 所有生成的 `ID` 按时间趋势递增。
- 整个分布式系统内不会产生重复 `ID`(因为有 `DatacenterId (5 bits)``WorkerId (5 bits)` 来做区分)。
Hyperf 的 [hyperf/snowflake](https://github.com/hyperf-cloud/snowflake) 组件在设计上提供了很好的可扩展性,允许您通过简单的扩展就能实现其它基于 Snowflake 的变体算法。
## 安装
```

View File

@ -35,6 +35,7 @@
* [命令行](zh/command.md)
* [自动化测试](zh/testing.md)
* [视图](zh/view.md)
* [国际化](zh/translation.md)
* 数据库模型
@ -74,10 +75,11 @@
* [开发者工具](zh/devtool.md)
* [辅助类](zh/utils.md)
* [限流器](zh/rate-limit.md)
* [Swoole Enterprise](zh/swoole-enterprise.md)
* [Swoole Tracker](zh/swoole-tracker.md)
* [定时任务](zh/crontab.md)
* [Task 机制](zh/task.md)
* [枚举类](zh/constants.md)
* [Snowflake](zh/snowflake.md)
* 应用部署
@ -85,6 +87,7 @@
* [DaoCloud Devops 搭建](zh/tutorial/daocloud.md)
* [Supervisor 部署](zh/tutorial/supervisor.md)
* [Nginx 反向代理](zh/tutorial/nginx.md)
* [阿里云日志服务](zh/tutorial/aliyun-logger.md)
* Awesome Hyperf
@ -94,4 +97,4 @@
* [指南前言](zh/component-guide/intro.md)
* [创建新的组件](zh/component-guide/create.md)
* Hyperf 框架流程介入
* [ConfigProvider 机制](zh/component-guide/configprovider.md)

View File

@ -1,6 +1,6 @@
# Swoole Enterprise
# Swoole Tracker
[Swoole Enterprise](https://www.swoole-cloud.com/dashboard.html) 作为 `Swoole` 官方出品的一整套企业级`PHP`和`Swoole`分析调试工具,更专一、更专业。
[Swoole Tracker](https://www.swoole-cloud.com/tracker.html) 作为 `Swoole` 官方出品的一整套企业级 `PHP` `Swoole`分析调试工具,更专一、更专业。曾命名Swoole Enterprise
- 时刻掌握应用架构模型
> 自动发现应用依赖拓扑结构和展示,时刻掌握应用的架构模型
@ -21,7 +21,7 @@
注册完账户后,进入[控制台](https://www.swoole-cloud.com/dashboard/catdemo/),并申请试用,下载对应客户端。
相关文档,请移步 [试用文档](https://www.yuque.com/swoole-wiki/try) 或 [详细文档](https://www.yuque.com/swoole-wiki/dam5n7)
相关文档,请移步 [试用文档](https://www.kancloud.cn/swoole-inc/ee-base-wiki/1214079) 或 [详细文档](https://www.kancloud.cn/swoole-inc/ee-help-wiki/1213080)
> 具体文档地址,以从控制台下载的对应客户端中展示的为准。
@ -38,11 +38,11 @@ php /opt/www/bin/hyperf.php start
```
2. `swoole-plus.ini`
2. `swoole-tracker.ini`
```bash
[swoole_plus]
extension=/opt/swoole_plus.so
[swoole_tracker]
extension=/opt/swoole_tracker.so
apm.enable=1 #打开总开关
apm.sampling_rate=100 #采样率 例如100%
@ -98,8 +98,8 @@ WORKDIR /opt/www/.build
# 这里的地址,以客户端中显示的为准
RUN ./deploy_env.sh www.swoole-cloud.com \
&& chmod 755 entrypoint.sh \
&& cp swoole_plus72.so /opt/swoole_plus.so \
&& cp swoole-plus.ini /etc/php7/conf.d/swoole-plus.ini \
&& cp swoole_tracker72.so /opt/swoole_tracker.so \
&& cp swoole-tracker.ini /etc/php7/conf.d/swoole-tracker.ini \
&& php -m
WORKDIR /opt/www
@ -113,23 +113,34 @@ EXPOSE 9501
ENTRYPOINT ["sh", ".build/entrypoint.sh"]
```
### 安装组件
```bash
composer require hyperf/swoole-enterprise dev-master
```
## 使用
`config/autoload/middlewares.php` 配置文件中注册 `Hyperf\SwooleEnterprise\Middleware\HttpServerMiddleware` 中间件即可,如下:
### 不依赖组件
`Swoole Tracker``v2.5.0` 以上版本支持自动生成应用名称并创建应用,无需修改任何代码。
如果使用 `Swoole``HttpServer` 那么生成的应用名称为`ip:port`
如果使用 `Swoole` 其他的 `Server` 那么生成的应用名称为`ip(hostname):port`
即安装好 `swoole_tracker` 扩展之后就可以正常使用 `Swoole Tracker` 的功能
### 依赖组件
当你需要自定义应用名称时则需要安装组件,使用 `Composer` 安装:
```bash
composer require hyperf/swoole-tracker
```
安装完成后在 `config/autoload/middlewares.php` 配置文件中注册 `Hyperf\SwooleTracker\Middleware\HttpServerMiddleware` 中间件即可,如下:
```php
<?php
return [
'http' => [
Hyperf\SwooleEnterprise\Middleware\HttpServerMiddleware::class
Hyperf\SwooleTracker\Middleware\HttpServerMiddleware::class
],
];
```

View File

@ -105,9 +105,9 @@ $result = $task->handle(Coroutine::id());
Swoole 暂时没有协程化的函数列表
- mysql底层使用 libmysqlclient
- curl:底层使用 libcurl即不能使用CURL驱动的Guzzle
- mongo底层使用 mongo-c-client
- mysql底层使用 libmysqlclient
- curl,底层使用 libcurl在 Swoole 4.4 后底层进行了协程化(beta)
- mongo底层使用 mongo-c-client
- pdo_pgsql
- pdo_ori
- pdo_odbc
@ -143,10 +143,8 @@ class MongoTask
/**
* @Task
* @param string $namespace
* @param array $document
*/
public function insert($namespace, $document)
public function insert(string $namespace, array $document)
{
$writeConcern = new WriteConcern(WriteConcern::MAJORITY, 1000);
$bulk = new BulkWrite();
@ -158,11 +156,8 @@ class MongoTask
/**
* @Task
* @param string $namespace
* @param array $filter
* @param array $options
*/
public function query($namespace, $filter = [], $options = [])
public function query(string $namespace, array $filter = [], array $options = [])
{
$query = new Query($filter, $options);
$cursor = $this->manager()->executeQuery($namespace, $query);

View File

@ -0,0 +1,127 @@
# 阿里云日志服务
`Docker集群` 部署项目时,收集日志会是一个比较麻烦的问题,但阿里云提供了十分好用的 `日志收集系统`,本篇文档就是简略介绍一下阿里云日志收集的使用方法。
* [Docker Swarm 集群搭建](zh/tutorial/docker-swarm.md)
## 开通日志服务
首先第一步便是在阿里云上开通 `日志服务`
[日志服务文档](https://help.aliyun.com/product/28958.html)
以下的教程是一个顺序的操作方式,一步一步讲述如何使用日志服务。
## 安装 Logtail 容器
[标准 Docker 日志采集流程文档](https://help.aliyun.com/document_detail/66659.html)
| 参数 | 说明 |
|:-------------------------------------:|:------------------------------------------:|
| ${your_region_name} | 区域ID 比如华东1区域是 cn-hangzhou |
| ${your_aliyun_user_id} | 用户标识请替换为您的阿里云主账号用户ID。 |
| ${your_machine_group_user_defined_id} | 集群的机器组自定义标识 以下使用 Hyperf |
```
docker run -d -v /:/logtail_host:ro -v /var/run/docker.sock:/var/run/docker.sock \
--env ALIYUN_LOGTAIL_CONFIG=/etc/ilogtail/conf/${your_region_name}/ilogtail_config.json \
--env ALIYUN_LOGTAIL_USER_ID=${your_aliyun_user_id} \
--env ALIYUN_LOGTAIL_USER_DEFINED_ID=${your_machine_group_user_defined_id} \
registry.cn-hangzhou.aliyuncs.com/log-service/logtail
```
## 配置日志收集
### 创建 Project
登录阿里云日志服务,点击 `创建 Project`,填写以下信息
| 参数 | 填写示例 |
|:------------:|:----------------:|
| Project名称 | hyperf |
| 注释 | 用于日志系统演示 |
| 所属区域 | 华东1杭州 |
| 开通服务 | 详细日志 |
| 日志存储位置 | 当前Project |
### 创建 Logstore
除以下参数,按需填写,其他都使用默认即可
| 参数 | 填写示例 |
|:------------:|:---------------:|
| Logstore名称 | hyperf-demo-api |
| 永久保存 | false |
| 数据保存时间 | 60 |
### 接入数据
1. 选择 Docker 文件
2. 创建机器组
如果已经创建过机器组,可以跳过这一步
| 参数 | 填写示例 |
|:-------------:|:-------------:|
| 机器组名称 | Hyperf |
| 机器组标识 | 用户自定义标识 |
| 用户自定义标识 | Hyperf |
3. 配置机器组
应用刚刚创建的机器组
4. 配置 Logtail
`Label` 白名单,这里可以按需填写,以下按照项目名字来配置,而项目名会在 Docker 容器运行时设置。
| 参数 | 填写示例 | 填写示例 |
|:--------------:|:-------------------------------------------------:|:---------------:|
| 配置名称 | hyperf-demo-api | |
| 日志路径 | /opt/www/runtime/logs | *.log |
| Label白名单 | app.name | hyperf-demo-api |
| 模式 | 完整正则模式 | |
| 单行模式 | false | |
| 日志样例 | [2019-03-07 11:58:57] hyperf.WARNING: xxx | |
| 首行正则表达式 | \[\d+-\d+-\d+\s\d+:\d+:\d+\]\s.* | |
| 提取字段 | true | |
| 正则表达式 | \[(\d+-\d+-\d+\s\d+:\d+:\d+)\]\s(\w+)\.(\w+):(.*) | |
| 日志抽取内容 | time name level content | |
5. 查询分析配置
字段索引属性
| 字段名称 | 类型 | 别名 | 中文分词 | 开启统计 |
|:--------:|:----:|:-------:|:--------:|:--------:|
| name | text | name | false | true |
| level | text | level | false | true |
| time | text | time | false | false |
| content | text | content | true | false |
### 运行镜像
运行镜像时,只需要设置 Container `labels` 即可。
| name | value |
|:--------:|:---------------:|
| app.name | hyperf-demo-api |
比如以下 Dockerfile
```Dockerfile
# Default Dockerfile
FROM hyperf/hyperf:7.2-alpine-cli
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="hyperf-demo-api"
# 其它内容省略
```
## 注意事项
- Docker 存储驱动限制:目前只支持 `overlay`、`overlay2`,其他存储驱动需将日志所在目录 `mount` 到本地,然后改为收集宿主机 `~/logtail_host/your_path` 下的日志即可。

View File

@ -8,6 +8,12 @@
curl -sSL https://get.daocloud.io/docker | sh
```
修改文件 `/lib/systemd/system/docker.service`,允许使用 `TCP` 连接 `Docker`
```
ExecStart=/usr/bin/dockerd -H unix:// -H tcp://0.0.0.0:2375
```
## 搭建自己的Gitlab
### 安装Gitlab
@ -100,6 +106,7 @@ docker network create \
--driver overlay \
--subnet 10.0.0.0/24 \
--opt encrypted \
--attachable \
default-network
```
@ -120,6 +127,14 @@ $ docker swarm join --token <token> ip:2377
> 其他与 builder 一致,但是 tag 却不能一样。线上环境可以设置为 tags测试环境设置为 test
## 安装其他应用
以下以 `Mysql` 为例,直接使用上述 `network`,支持容器内使用 name 互调。
```
docker run --name mysql -v /srv/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=xxxx -p 3306:3306 --rm --network default-network -d mysql:5.7
```
## 安装 Portainer
[Portainer](https://github.com/portainer/portainer)
@ -226,6 +241,85 @@ REDIS_DB=0
curl http://127.0.0.1:9501/
```
## 安装 KONG 网关
通常情况下Swarm集群是不会直接对外的所以我们这里推荐使用 `KONG` 作为网关。
还有另外一个原因,那就是 `Swarm``Ingress网络` 设计上有缺陷,所以在连接不复用的情况下,会有并发瓶颈,具体请查看对应 `Issue` [#35082](https://github.com/moby/moby/issues/35082)
`KONG` 作为网关,默认情况下就会复用后端的连接,所以会极大减缓上述问题。
### 安装数据库
```
docker run -d --name kong-database \
--network=default-network \
-p 5432:5432 \
-e "POSTGRES_USER=kong" \
-e "POSTGRES_DB=kong" \
postgres:9.6
```
### 安装网关
初始化数据库
```
docker run --rm \
--network=default-network \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
kong:latest kong migrations bootstrap
```
启动
```
docker run -d --name kong \
--network=default-network \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
-e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
-e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
-p 8000:8000 \
-p 8443:8443 \
-p 8001:8001 \
-p 8444:8444 \
kong:latest
```
### 安装 KONG Dashboard
> 暂时 `Docker` 中没有更新 `v3.6.0` 所以最新版的 `KONG` 可能无法使用
```
docker run --rm --network=default-network -p 8080:8080 -d --name kong-dashboard pgbi/kong-dashboard start \
--kong-url http://kong:8001 \
--basic-auth user1=password1 user2=password2
```
### 配置
接下来只需要把部署 `KONG` 的机器 `IP` 对外,然后配置 `Service` 即可。
如果机器直接对外,最好只开放 `80` `443` 端口,然后把 `Kong` 容器的 `8000``8443` 映射到 `80``443` 上。
当然,如果使用了 `SLB` 等负载均衡,就直接通过负载均衡,把 `80``443` 映射到 `KONG` 所在几台机器的 `8000` `8443` 上。
## 如何使用 Linux Crontab
`Hyperf` 虽然提供了 `crontab` 组件,但是不一定可以满足所有人的需求,这里提供一个 `Linux` 使用的脚本,执行 `Docker` 内的 `Command`
```bash
#!/usr/bin/env bash
basepath=$(cd `dirname $0`; pwd)
docker pull registry-vpc.cn-shanghai.aliyuncs.com/namespace/project:latest
docker run --rm -i -v $basepath/.env:/opt/www/.env \
--entrypoint php registry-vpc.cn-shanghai.aliyuncs.com/namespace/project:latest \
/opt/www/bin/hyperf.php your_command
```
## 意外情况
### fatal: git fetch-pack: expected shallow list

View File

@ -13,7 +13,7 @@ composer require hyperf/view
View 组件的配置文件位于 `config/autoload/view.php`,若配置文件不存在可自行创建,以下为相关配置的说明:
| 配置 | 类型 | 默认值 | 备注 |
|:-----------------:|:------:|:--------------------------------------:|:----------------:|
|:-----------------:|:------:|:-------------------------------------:|:----------------:|
| engine | string | Hyperf\View\Engine\BladeEngine::class | 视图渲染引擎 |
| mode | string | Mode::TASK | 视图渲染模式 |
| config.view_path | string | 无 | 视图文件默认地址 |

92
phpstan.neon Normal file
View File

@ -0,0 +1,92 @@
# Magic behaviour with __get, __set, __call and __callStatic is not exactly static analyser-friendly :)
# Fortunately, You can ingore it by the following config.
#
rules:
- PHPStan\Rules\Arrays\DeadForeachRule
- PHPStan\Rules\Comparison\BooleanOrConstantConditionRule
- PHPStan\Rules\Comparison\ElseIfConstantConditionRule
- PHPStan\Rules\Comparison\IfConstantConditionRule
- PHPStan\Rules\Comparison\TernaryOperatorConstantConditionRule
parameters:
bootstrap: "bootstrap.php"
checkFunctionArgumentTypes: true
checkArgumentsPassedByReference: true
featureToggles:
deadCatchesRule: false
noopRule: false
tooWideTypehints: false
unreachableStatement: false
ignoreErrors:
- "#will always evaluate to false#"
excludes_analyse:
- %currentWorkingDirectory%/src/*/tests/*
conditionalTags:
PHPStan\Rules\Exceptions\DeadCatchRule:
phpstan.rules.rule: %featureToggles.deadCatchesRule%
PHPStan\Rules\DeadCode\NoopRule:
phpstan.rules.rule: %featureToggles.noopRule%
PHPStan\Rules\DeadCode\UnreachableStatementRule:
phpstan.rules.rule: %featureToggles.unreachableStatement%
PHPStan\Rules\TooWideTypehints\TooWideClosureReturnTypehintRule:
phpstan.rules.rule: %featureToggles.tooWideTypehints%
PHPStan\Rules\TooWideTypehints\TooWideFunctionReturnTypehintRule:
phpstan.rules.rule: %featureToggles.tooWideTypehints%
PHPStan\Rules\TooWideTypehints\TooWidePrivateMethodReturnTypehintRule:
phpstan.rules.rule: %featureToggles.tooWideTypehints%
services:
-
class: PHPStan\Rules\Classes\ImpossibleInstanceOfRule
arguments:
checkAlwaysTrueInstanceof: %checkAlwaysTrueInstanceof%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Comparison\ImpossibleCheckTypeFunctionCallRule
arguments:
checkAlwaysTrueCheckTypeFunctionCall: %checkAlwaysTrueCheckTypeFunctionCall%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Comparison\ImpossibleCheckTypeMethodCallRule
arguments:
checkAlwaysTrueCheckTypeFunctionCall: %checkAlwaysTrueCheckTypeFunctionCall%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Comparison\ImpossibleCheckTypeStaticMethodCallRule
arguments:
checkAlwaysTrueCheckTypeFunctionCall: %checkAlwaysTrueCheckTypeFunctionCall%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Comparison\StrictComparisonOfDifferentTypesRule
arguments:
checkAlwaysTrueStrictComparison: %checkAlwaysTrueStrictComparison%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Exceptions\DeadCatchRule
-
class: PHPStan\Rules\DeadCode\NoopRule
-
class: PHPStan\Rules\DeadCode\UnreachableStatementRule
-
class: PHPStan\Rules\TooWideTypehints\TooWideClosureReturnTypehintRule
-
class: PHPStan\Rules\TooWideTypehints\TooWideFunctionReturnTypehintRule
-
class: PHPStan\Rules\TooWideTypehints\TooWidePrivateMethodReturnTypehintRule

View File

@ -31,12 +31,14 @@
<directory suffix="Test.php">./src/logger/tests</directory>
<directory suffix="Test.php">./src/model-cache/tests</directory>
<directory suffix="Test.php">./src/paginator/tests</directory>
<directory suffix="Test.php">./src/pool/tests</directory>
<directory suffix="Test.php">./src/redis/tests</directory>
<directory suffix="Test.php">./src/rpc/tests</directory>
<directory suffix="Test.php">./src/server/tests</directory>
<directory suffix="Test.php">./src/service-governance/tests</directory>
<directory suffix="Test.php">./src/snowflake/tests</directory>
<directory suffix="Test.php">./src/task/tests</directory>
<directory suffix="Test.php">./src/testing/tests</directory>
<directory suffix="Test.php">./src/translation/tests</directory>
<directory suffix="Test.php">./src/utils/tests</directory>
<directory suffix="Test.php">./src/websocket-client/tests</directory>

View File

@ -11,10 +11,10 @@
},
"require": {
"php": ">=7.2",
"hyperf/contract": "~1.0.0",
"hyperf/utils": "~1.0.0",
"hyperf/process": "~1.0.0",
"hyperf/pool": "~1.0.0",
"hyperf/contract": "~1.1.0",
"hyperf/utils": "~1.1.0",
"hyperf/process": "~1.1.0",
"hyperf/pool": "~1.1.0",
"psr/container": "^1.0",
"psr/event-dispatcher": "^1.0",
"psr/log": "^1.0",
@ -22,9 +22,9 @@
"doctrine/instantiator": "^1.2.0"
},
"require-dev": {
"hyperf/di": "~1.0.0",
"hyperf/event": "~1.0.0",
"hyperf/framework": "~1.0.0",
"hyperf/di": "~1.1.0",
"hyperf/event": "~1.1.0",
"hyperf/framework": "~1.1.0",
"malukenho/docheader": "^0.1.6",
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0.0",
@ -49,7 +49,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\Amqp\\ConfigProvider"

View File

@ -13,12 +13,12 @@
"php": ">=7.2",
"psr/container": "^1.0",
"psr/event-dispatcher": "^1.0",
"hyperf/contract": "~1.0.0",
"hyperf/command": "~1.0.0",
"hyperf/utils": "~1.0.0"
"hyperf/contract": "~1.1.0",
"hyperf/command": "~1.1.0",
"hyperf/utils": "~1.1.0"
},
"require-dev": {
"hyperf/process": "~1.0.0",
"hyperf/process": "~1.1.0",
"malukenho/docheader": "^0.1.6",
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0.0",
@ -43,7 +43,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\AsyncQueue\\ConfigProvider"

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\AsyncQueue\Annotation;
use Hyperf\Di\Annotation\AbstractAnnotation;
/**
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class AsyncQueueMessage extends AbstractAnnotation
{
/**
* @var string
*/
public $pool = 'default';
/**
* @var int
*/
public $delay = 0;
}

View File

@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\AsyncQueue;
use Hyperf\Contract\CompressInterface;
use Hyperf\Contract\UnCompressInterface;
use Hyperf\Utils\ApplicationContext;
class AnnotationJob extends Job
{
/**
* @var string
*/
public $class;
/**
* @var string
*/
public $method;
/**
* @var array
*/
public $params = [];
public function __construct(string $class, string $method, array $params)
{
$this->class = $class;
$this->method = $method;
foreach ($params as $key => $value) {
if ($value instanceof CompressInterface) {
$value = $value->compress();
}
$this->params[$key] = $value;
}
}
public function handle()
{
$container = ApplicationContext::getContainer();
$class = $container->get($this->class);
$params = [];
foreach ($this->params as $key => $value) {
if ($value instanceof UnCompressInterface) {
$value = $value->uncompress();
}
$params[$key] = $value;
}
$class->{$this->method}(...$params);
}
}

View File

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\AsyncQueue\Aspect;
use Hyperf\AsyncQueue\Annotation\AsyncQueueMessage;
use Hyperf\AsyncQueue\AnnotationJob;
use Hyperf\AsyncQueue\Driver\DriverFactory;
use Hyperf\AsyncQueue\Environment;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use Psr\Container\ContainerInterface;
/**
* @Aspect
*/
class AsyncQueueAspect extends AbstractAspect
{
public $annotations = [
AsyncQueueMessage::class,
];
/**
* @var ContainerInterface
*/
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function process(ProceedingJoinPoint $proceedingJoinPoint)
{
$env = $this->container->get(Environment::class);
if ($env->isAsyncQueue()) {
$proceedingJoinPoint->process();
return;
}
$class = $proceedingJoinPoint->className;
$method = $proceedingJoinPoint->methodName;
$arguments = $proceedingJoinPoint->getArguments();
$pool = 'default';
$delay = 0;
$metadata = $proceedingJoinPoint->getAnnotationMetadata();
/** @var AsyncQueueMessage $annotation */
$annotation = $metadata->method[AsyncQueueMessage::class] ?? $metadata->class[AsyncQueueMessage::class] ?? null;
if ($annotation instanceof AsyncQueueMessage) {
$pool = $annotation->pool;
$delay = $annotation->delay;
}
$factory = $this->container->get(DriverFactory::class);
$driver = $factory->get($pool);
$driver->push(new AnnotationJob($class, $method, $arguments), $delay);
}
}

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Hyperf\AsyncQueue\Driver;
use Hyperf\AsyncQueue\Environment;
use Hyperf\AsyncQueue\Event\AfterHandle;
use Hyperf\AsyncQueue\Event\BeforeHandle;
use Hyperf\AsyncQueue\Event\FailedHandle;
@ -53,6 +54,8 @@ abstract class Driver implements DriverInterface
public function consume(): void
{
$this->container->get(Environment::class)->setAsyncQueue(true);
while (true) {
[$data, $message] = $this->pop();

View File

@ -21,12 +21,6 @@ interface DriverInterface
*/
public function push(JobInterface $job, int $delay = 0): bool;
/**
* Push a delay job to queue.
* @deprecated v1.1
*/
public function delay(JobInterface $job, int $delay = 0): bool;
/**
* Delete a delay job to queue.
*/

View File

@ -74,20 +74,6 @@ class RedisDriver extends Driver
return $this->redis->zAdd($this->channel->getDelayed(), time() + $delay, $data) > 0;
}
/**
* @deprecated v1.1
*/
public function delay(JobInterface $job, int $delay = 0): bool
{
if ($delay === 0) {
return $this->push($job);
}
$message = new Message($job);
$data = $this->packer->pack($message);
return $this->redis->zAdd($this->channel->getDelayed(), time() + $delay, $data) > 0;
}
public function delete(JobInterface $job): bool
{
$message = new Message($job);

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\AsyncQueue;
class Environment
{
/**
* @var bool
*/
protected $asyncQueue = false;
public function isAsyncQueue(): bool
{
return $this->asyncQueue;
}
public function setAsyncQueue(bool $asyncQueue): self
{
$this->asyncQueue = $asyncQueue;
return $this;
}
}

View File

@ -13,12 +13,12 @@
"php": ">=7.2",
"psr/container": "^1.0",
"psr/simple-cache": "^1.0",
"hyperf/contract": "~1.0.0",
"hyperf/utils": "~1.0.0"
"hyperf/contract": "~1.1.0",
"hyperf/utils": "~1.1.0"
},
"require-dev": {
"hyperf/di": "~1.0.0",
"hyperf/event": "~1.0.0",
"hyperf/di": "~1.1.0",
"hyperf/event": "~1.1.0",
"malukenho/docheader": "^0.1.6",
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0.0",
@ -43,7 +43,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\Cache\\ConfigProvider"

View File

@ -12,12 +12,11 @@ declare(strict_types=1);
namespace Hyperf\Cache;
use Hyperf\Di\MetadataCollector;
use Hyperf\Utils\Traits\Container;
class CacheListenerCollector
class CacheListenerCollector extends MetadataCollector
{
use Container;
/**
* @var array
*/

View File

@ -28,6 +28,9 @@ class ConfigProvider
'paths' => [
__DIR__,
],
'collectors' => [
CacheListenerCollector::class,
]
],
'publish' => [
[

View File

@ -19,7 +19,9 @@ use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Di\Container;
use Hyperf\Pool\Channel;
use Hyperf\Pool\LowFrequencyInterface;
use Hyperf\Pool\PoolOption;
use Hyperf\Redis\Frequency;
use Hyperf\Redis\Pool\PoolFactory;
use Hyperf\Redis\Pool\RedisPool;
use Hyperf\Redis\Redis;
@ -138,6 +140,9 @@ class RedisDriverTest extends TestCase
return new RedisDriver($container, $args['config']);
});
$container->shouldReceive('get')->with(PhpSerializerPacker::class)->andReturn(new PhpSerializerPacker());
$frequency = Mockery::mock(LowFrequencyInterface::class);
$frequency->shouldReceive('isLowFrequency')->andReturn(true);
$container->shouldReceive('make')->with(Frequency::class, Mockery::any())->andReturn($frequency);
$container->shouldReceive('make')->with(RedisPool::class, Mockery::any())->andReturnUsing(function ($class, $args) use ($container) {
return new RedisPool($container, $args['name']);
});

View File

@ -12,11 +12,11 @@
"require": {
"php": ">=7.2",
"psr/container": "^1.0",
"hyperf/utils": "~1.0.0"
"hyperf/utils": "~1.1.0"
},
"require-dev": {
"hyperf/contract": "~1.0.0",
"hyperf/di": "~1.0.0",
"hyperf/contract": "~1.1.0",
"hyperf/di": "~1.1.0",
"malukenho/docheader": "^0.1.6",
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0.0",
@ -40,7 +40,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\CircuitBreaker\\ConfigProvider"

1
src/command/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
/tests export-ignore

View File

@ -15,12 +15,13 @@
},
"autoload-dev": {
"psr-4": {
"HyperfTest\\Command\\": "tests/"
}
},
"require": {
"php": ">=7.2",
"symfony/console": "^4.2",
"hyperf/utils": "~1.0.0"
"hyperf/utils": "~1.1.0"
},
"require-dev": {
"malukenho/docheader": "^0.1.6",
@ -33,7 +34,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
}
},
"scripts": {

View File

@ -13,6 +13,7 @@ declare(strict_types=1);
namespace Hyperf\Command;
use Hyperf\Utils\Contracts\Arrayable;
use Hyperf\Utils\Coroutine;
use Hyperf\Utils\Str;
use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
@ -55,7 +56,12 @@ abstract class Command extends SymfonyCommand
*
* @var bool
*/
protected $coroutine = false;
protected $coroutine = true;
/**
* @var int
*/
protected $hookFlags;
/**
* The mapping between human readable verbosity levels and Symfony's OutputInterface.
@ -76,6 +82,11 @@ abstract class Command extends SymfonyCommand
if (! $name && $this->name) {
$name = $this->name;
}
if (! is_int($this->hookFlags)) {
$this->hookFlags = swoole_hook_flags();
}
parent::__construct($name);
}
@ -370,10 +381,10 @@ abstract class Command extends SymfonyCommand
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($this->coroutine) {
if ($this->coroutine && ! Coroutine::inCoroutine()) {
run(function () {
call([$this, 'handle']);
});
}, $this->hookFlags);
return 0;
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Command\Command;
use Hyperf\Command\Command;
class DefaultSwooleFlagsCommand extends Command
{
public function handle()
{
}
public function getHookFlags(): int
{
return $this->hookFlags;
}
}

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Command\Command;
use Hyperf\Command\Command;
class SwooleFlagsCommand extends Command
{
protected $hookFlags = SWOOLE_HOOK_CURL | SWOOLE_HOOK_ALL;
public function handle()
{
}
public function getHookFlags(): int
{
return $this->hookFlags;
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Command;
use HyperfTest\Command\Command\DefaultSwooleFlagsCommand;
use HyperfTest\Command\Command\SwooleFlagsCommand;
use PHPUnit\Framework\TestCase;
/**
* @internal
* @coversNothing
*/
class CommandTest extends TestCase
{
public function testHookFlags()
{
$command = new DefaultSwooleFlagsCommand('test:demo');
$this->assertSame(SWOOLE_HOOK_ALL, $command->getHookFlags());
$command = new SwooleFlagsCommand('test:demo2');
$this->assertSame(SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL, $command->getHookFlags());
}
}

View File

@ -16,14 +16,14 @@
"require": {
"php": ">=7.2",
"psr/container": "^1.0",
"hyperf/contract": "~1.0.0",
"hyperf/guzzle": "~1.0.0"
"hyperf/contract": "~1.1.0",
"hyperf/guzzle": "~1.1.0"
},
"require-dev": {
"hyperf/config": "~1.0.0",
"hyperf/event": "~1.0.0",
"hyperf/framework": "~1.0.0",
"hyperf/process": "~1.0.0",
"hyperf/config": "~1.1.0",
"hyperf/event": "~1.1.0",
"hyperf/framework": "~1.1.0",
"hyperf/process": "~1.1.0",
"malukenho/docheader": "^0.1.6",
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0.0",
@ -47,7 +47,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\ConfigAliyunAcm\\ConfigProvider"

View File

@ -15,14 +15,14 @@
"require": {
"php": ">=7.2",
"psr/container": "^1.0",
"hyperf/contract": "~1.0.0",
"hyperf/utils": "~1.0.0"
"hyperf/contract": "~1.1.0",
"hyperf/utils": "~1.1.0"
},
"require-dev": {
"hyperf/config": "~1.0.0",
"hyperf/event": "~1.0.0",
"hyperf/framework": "~1.0.0",
"hyperf/process": "~1.0.0",
"hyperf/config": "~1.1.0",
"hyperf/event": "~1.1.0",
"hyperf/framework": "~1.1.0",
"hyperf/process": "~1.1.0",
"malukenho/docheader": "^0.1.6",
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0.0",
@ -46,7 +46,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\ConfigApollo\\ConfigProvider"

View File

@ -21,13 +21,11 @@
},
"require": {
"php": ">=7.2",
"ext-swoole": ">=4.3",
"hyperf/utils": "~1.0.0",
"hyperf/etcd": "~1.0.0"
"hyperf/utils": "~1.1.0",
"hyperf/etcd": "~1.1.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.14",
"hyperf/testing": "1.0.*",
"phpstan/phpstan": "^0.10.5",
"swoft/swoole-ide-helper": "dev-master"
},
@ -41,7 +39,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\ConfigEtcd\\ConfigProvider"

View File

@ -16,13 +16,13 @@
"psr/container": "^1.0",
"vlucas/phpdotenv": "^3.1",
"symfony/finder": "^4.2.8",
"hyperf/contract": "~1.0.0",
"hyperf/utils": "~1.0.0"
"hyperf/contract": "~1.1.0",
"hyperf/utils": "~1.1.0"
},
"require-dev": {
"hyperf/di": "~1.0.0",
"hyperf/event": "~1.0.0",
"hyperf/framework": "~1.0.0",
"hyperf/di": "~1.1.0",
"hyperf/event": "~1.1.0",
"hyperf/framework": "~1.1.0",
"malukenho/docheader": "^0.1.6",
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0.0",
@ -52,7 +52,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\Config\\ConfigProvider"

View File

@ -11,8 +11,8 @@
},
"require": {
"php": ">=7.2",
"hyperf/di": "~1.0.0",
"hyperf/utils": "~1.0.0"
"hyperf/di": "~1.1.0",
"hyperf/utils": "~1.1.0"
},
"require-dev": {
"malukenho/docheader": "^0.1.6",
@ -37,9 +37,10 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\Constants\\ConfigProvider"
}
},
"bin": [

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Constants;
class ConfigProvider
{
public function __invoke(): array
{
return [
'dependencies' => [
],
'scan' => [
'paths' => [],
'collectors' => [
ConstantsCollector::class,
],
],
];
}
}

View File

@ -12,12 +12,10 @@ declare(strict_types=1);
namespace Hyperf\Constants;
use Hyperf\Utils\Traits\Container;
use Hyperf\Di\MetadataCollector;
class ConstantsCollector
class ConstantsCollector extends MetadataCollector
{
use Container;
/**
* @var array
*/

View File

@ -13,7 +13,7 @@
},
"require": {
"php": ">=7.2",
"hyperf/guzzle": "~1.0.0"
"hyperf/guzzle": "~1.1.0"
},
"require-dev": {
"malukenho/docheader": "^0.1.6",
@ -38,7 +38,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\Consul\\ConfigProvider"

View File

@ -34,7 +34,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
}

View File

@ -37,7 +37,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\Crontab\\ConfigProvider"

View File

@ -11,8 +11,8 @@
},
"require": {
"php": ">=7.2",
"hyperf/utils": "~1.0.0",
"hyperf/paginator": "~1.0.0",
"hyperf/utils": "~1.1.0",
"hyperf/paginator": "~1.1.0",
"psr/container": "^1.0",
"nesbot/carbon": "^2.0",
"psr/event-dispatcher": "^1.0"
@ -42,7 +42,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
}
},
"bin": [

View File

@ -94,7 +94,8 @@ class ModelCommand extends Command
->setInheritance($this->getOption('inheritance', 'commands.db:model.inheritance', $pool, 'Model'))
->setUses($this->getOption('uses', 'commands.db:model.uses', $pool, 'Hyperf\DbConnection\Model\Model'))
->setForceCasts($this->getOption('force-casts', 'commands.db:model.force_casts', $pool, false))
->setRefreshFillable($this->getOption('refresh-fillable', 'commands.db:model.refresh_fillable', $pool, false));
->setRefreshFillable($this->getOption('refresh-fillable', 'commands.db:model.refresh_fillable', $pool, false))
->setTableMapping($this->getOption('table-mapping', 'commands.db:model.table_mapping', $pool));
if ($table) {
$this->createModel($table, $option);
@ -114,6 +115,7 @@ class ModelCommand extends Command
$this->addOption('inheritance', 'i', InputOption::VALUE_OPTIONAL, 'The inheritance that you want the Model extends.');
$this->addOption('uses', 'U', InputOption::VALUE_OPTIONAL, 'The default class uses of the Model.');
$this->addOption('refresh-fillable', null, InputOption::VALUE_NONE, 'Whether generate fillable argement for model.');
$this->addOption('table-mapping', 'M', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Table mappings for model.');
}
protected function getSchemaBuilder(string $poolName): MySqlBuilder
@ -144,7 +146,8 @@ class ModelCommand extends Command
$columns = $this->formatColumns($builder->getColumnTypeListing($table));
$project = new Project();
$class = $project->namespace($option->getPath()) . Str::studly($table);
$class = $option->getTableMapping()[$table] ?? Str::studly(Str::singular($table));
$class = $project->namespace($option->getPath()) . $class;
$path = BASE_PATH . '/' . $project->path($class);
if (! file_exists($path)) {
@ -208,7 +211,14 @@ class ModelCommand extends Command
protected function getOption(string $name, string $key, string $pool = 'default', $default = null)
{
$result = $this->input->getOption($name);
$nonInput = in_array($name, ['force-casts', 'refresh-fillable']) ? false : null;
$nonInput = null;
if (in_array($name, ['force-casts', 'refresh-fillable'])) {
$nonInput = false;
}
if (in_array($name, ['table-mapping'])) {
$nonInput = [];
}
if ($result === $nonInput) {
$result = $this->config->get("databases.{$pool}.{$key}", $default);
}

View File

@ -49,6 +49,11 @@ class ModelOption
*/
protected $refreshFillable;
/**
* @var array
*/
protected $tableMapping = [];
public function getPool(): string
{
return $this->pool;
@ -125,4 +130,19 @@ class ModelOption
$this->refreshFillable = $refreshFillable;
return $this;
}
public function getTableMapping(): array
{
return $this->tableMapping;
}
public function setTableMapping(array $tableMapping): ModelOption
{
foreach ($tableMapping as $item) {
[$key, $name] = explode(':', $item);
$this->tableMapping[$key] = $name;
}
return $this;
}
}

View File

@ -42,7 +42,7 @@ abstract class Event implements StoppableEventInterface
public function handle()
{
if (method_exists($this->getModel(), $this->getMethod())) {
return $this->getModel()->{$this->getMethod()}($this);
$this->getModel()->{$this->getMethod()}($this);
}
return $this;

View File

@ -10,7 +10,7 @@ declare(strict_types=1);
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\DbConnection\Listener;
namespace HyperfTest\Database\Stubs;
use Hyperf\Database\Model\Events\Event;
use Hyperf\Event\Annotation\Listener;
@ -19,7 +19,7 @@ use Hyperf\Event\Contract\ListenerInterface;
/**
* @Listener
*/
class ModelEventListener implements ListenerInterface
class ModelEventListenerStub implements ListenerInterface
{
public function listen(): array
{

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Database\Stubs;
use Hyperf\Database\Model\Events\Updating;
class ModelObserverStub
{
public function updating(Updating $event)
{
$event->getModel()->foo = 'bar';
}
}

View File

@ -12,12 +12,12 @@
},
"require": {
"php": ">=7.2",
"hyperf/framework": "~1.0.0",
"hyperf/database": "~1.0.0",
"hyperf/di": "~1.0.0",
"hyperf/event": "~1.0.0",
"hyperf/pool": "~1.0.0",
"hyperf/utils": "~1.0.0"
"hyperf/framework": "~1.1.0",
"hyperf/database": "~1.1.0",
"hyperf/di": "~1.1.0",
"hyperf/model-listener": "~1.1.0",
"hyperf/pool": "~1.1.0",
"hyperf/utils": "~1.1.0"
},
"require-dev": {
"malukenho/docheader": "^0.1.6",
@ -42,7 +42,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\DbConnection\\ConfigProvider"

View File

@ -12,82 +12,8 @@ declare(strict_types=1);
namespace Hyperf\DbConnection;
use Hyperf\Contract\FrequencyInterface;
use Hyperf\Pool\LowFrequencyInterface;
use Hyperf\Pool\Frequency as DefaultFrequency;
class Frequency implements FrequencyInterface, LowFrequencyInterface
class Frequency extends DefaultFrequency
{
/**
* @var array
*/
protected $hits = [];
/**
* How much time do you want to calculate the frequency ?
* @var int
*/
protected $time = 10;
/**
* @var int
*/
protected $lowFrequency = 5;
/**
* @var int
*/
protected $beginTime;
public function __construct()
{
$this->beginTime = time();
}
public function hit(int $number = 1): bool
{
$this->flush();
$now = time();
$hit = $this->hits[$now] ?? 0;
$this->hits[$now] = $number + $hit;
return true;
}
public function frequency(): float
{
$this->flush();
$hits = 0;
$count = 0;
foreach ($this->hits as $hit) {
++$count;
$hits += $hit;
}
return floatval($hits / $count);
}
public function isLowFrequency(): bool
{
return $this->frequency() < $this->lowFrequency;
}
protected function flush(): void
{
$now = time();
$latest = $now - $this->time;
foreach ($this->hits as $time => $hit) {
if ($time < $latest) {
unset($this->hits[$time]);
}
}
if (count($this->hits) < $this->time) {
$beginTime = $this->beginTime < $latest ? $latest : $this->beginTime;
for ($i = $beginTime; $i < $now; ++$i) {
$this->hits[$i] = $this->hits[$i] ?? 0;
}
}
}
}

View File

@ -12,10 +12,10 @@
},
"require": {
"php": ">=7.2",
"hyperf/command": "~1.0.0",
"hyperf/contract": "~1.0.0",
"hyperf/di": "~1.0.0",
"hyperf/utils": "~1.0.0"
"hyperf/command": "~1.1.0",
"hyperf/contract": "~1.1.0",
"hyperf/di": "~1.1.0",
"hyperf/utils": "~1.1.0"
},
"require-dev": {
"malukenho/docheader": "^0.1.6",
@ -39,7 +39,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\Devtool\\ConfigProvider"

View File

@ -19,8 +19,8 @@
"symfony/finder": "^4.1",
"php-di/phpdoc-reader": "^2.0.1",
"doctrine/instantiator": "^1.0",
"hyperf/event": "~1.0.0",
"hyperf/framework": "~1.0.0"
"hyperf/event": "~1.1.0",
"hyperf/framework": "~1.1.0"
},
"require-dev": {
"malukenho/docheader": "^0.1.6",
@ -49,7 +49,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.1-dev"
},
"hyperf": {
"config": "Hyperf\\Di\\ConfigProvider"

View File

@ -21,9 +21,6 @@ use Hyperf\Di\Aop\AroundInterface;
*/
class Aspect extends AbstractAnnotation
{
/**
* {@inheritdoc}
*/
public function collectClass(string $className): void
{
// @TODO Add order property.
@ -33,7 +30,9 @@ class Aspect extends AbstractAnnotation
$instance = $instantitor->instantiate($className);
switch ($instance) {
case $instance instanceof AroundInterface:
AspectCollector::setAround($className, $instance->classes, $instance->annotations);
$classes = property_exists($instance, 'classes') ? $instance->classes : [];
$annotations = property_exists($instance, 'annotations') ? $instance->annotations : [];
AspectCollector::setAround($className, $classes, $annotations);
break;
}
}

View File

@ -28,8 +28,8 @@ class AspectCollector extends MetadataCollector
public static function setAround(string $aspect, array $classes, array $annotations): void
{
$classes && static::set('classes.' . $aspect, $classes);
$annotations && static::set('annotations.' . $aspect, $annotations);
static::set('classes.' . $aspect, $classes);
static::set('annotations.' . $aspect, $annotations);
static::$aspectRules[$aspect] = [
'classes' => $classes,
'annotations' => $annotations,

View File

@ -14,6 +14,7 @@ namespace Hyperf\Di\Aop;
use Closure;
use Hyperf\Di\Annotation\AnnotationCollector;
use Hyperf\Di\Exception\Exception;
class ProceedingJoinPoint
{
@ -43,7 +44,7 @@ class ProceedingJoinPoint
public $originalMethod;
/**
* @var Closure
* @var null|Closure
*/
public $pipe;
@ -61,6 +62,10 @@ class ProceedingJoinPoint
public function process()
{
$closure = $this->pipe;
if (! $closure instanceof Closure) {
throw new Exception('The pipe is not instanceof \Closure');
}
return $closure($this);
}

View File

@ -52,7 +52,7 @@ class ProxyCallVisitor extends NodeVisitorAbstract
];
/**
* @var Identifier
* @var null|Identifier
*/
private $class;
@ -78,7 +78,7 @@ class ProxyCallVisitor extends NodeVisitorAbstract
continue;
}
if (! $namespace instanceof Namespace_) {
return;
break;
}
// Add current class namespace.
$usedNamespace = [
@ -112,6 +112,8 @@ class ProxyCallVisitor extends NodeVisitorAbstract
}
}
}
return null;
}
public function leaveNode(Node $node)
@ -134,7 +136,7 @@ class ProxyCallVisitor extends NodeVisitorAbstract
break;
case $node instanceof StaticPropertyFetch && $this->extends:
// Rewrite parent::$staticProperty to ParentClass::$staticProperty.
if ($node->class && $node->class->toString() === 'parent') {
if ($node->class instanceof Node\Name && $node->class->toString() === 'parent') {
$node->class = new Name($this->extends->toCodeString());
return $node;
}
@ -225,17 +227,17 @@ class ProxyCallVisitor extends NodeVisitorAbstract
$class = $this->class->toString();
$staticCall = new StaticCall(new Name('self'), '__proxyCall', [
// OriginalClass::class
new ClassConstFetch(new Name($class), new Identifier('class')),
new Node\Arg(new ClassConstFetch(new Name($class), new Identifier('class'))),
// __FUNCTION__
new MagicConstFunction(),
new Node\Arg(new MagicConstFunction()),
// self::getParamMap(OriginalClass::class, __FUNCTION, func_get_args())
new StaticCall(new Name('self'), 'getParamsMap', [
new ClassConstFetch(new Name($class), new Identifier('class')),
new MagicConstFunction(),
new FuncCall(new Name('func_get_args')),
]),
new Node\Arg(new StaticCall(new Name('self'), 'getParamsMap', [
new Node\Arg(new ClassConstFetch(new Name($class), new Identifier('class'))),
new Node\Arg(new MagicConstFunction()),
new Node\Arg(new FuncCall(new Name('func_get_args'))),
])),
// A closure that wrapped original method code.
new Closure([
new Node\Arg(new Closure([
'params' => value(function () use ($node) {
// Transfer the variadic variable to normal variable at closure argument. ...$params => $parms
$params = $node->getParams();
@ -253,7 +255,7 @@ class ProxyCallVisitor extends NodeVisitorAbstract
new Variable('__method__'),
],
'stmts' => $node->stmts,
]),
])),
]);
$magicConstFunction = new Expression(new Assign(new Variable('__function__'), new Node\Scalar\MagicConst\Function_()));
$magicConstMethod = new Expression(new Assign(new Variable('__method__'), new Node\Scalar\MagicConst\Method()));

View File

@ -35,7 +35,7 @@ class ProxyClassNameVisitor extends NodeVisitorAbstract
{
// Rewirte the class name and extends the original class.
if ($node instanceof Node\Stmt\Class_ && ! $node->isAnonymous()) {
$node->extends = $node->name;
$node->extends = new Node\Name($node->name->name);
$node->name = new Node\Identifier($this->proxyClassName);
return $node;
}

View File

@ -57,7 +57,7 @@ class InitProxyCommand extends Command
protected function clearRuntime($paths)
{
$finder = new Finder();
$finder->files()->in($paths)->name('*.php');
$finder->files()->in($paths)->name(['*.php', '*.cache']);
/** @var SplFileInfo $file */
foreach ($finder as $file) {
@ -91,7 +91,7 @@ class InitProxyCommand extends Command
{
$scanDirs = $this->getScanDir();
$runtime = BASE_PATH . '/runtime/container/proxy/';
$runtime = BASE_PATH . '/runtime/container/';
if (is_dir($runtime)) {
$this->clearRuntime($runtime);
}

View File

@ -12,6 +12,8 @@ declare(strict_types=1);
namespace Hyperf\Di;
use Hyperf\Di\Annotation\AnnotationCollector;
use Hyperf\Di\Annotation\AspectCollector;
use Hyperf\Di\Command\InitProxyCommand;
use Hyperf\Di\Listener\BootApplicationListener;
@ -33,6 +35,10 @@ class ConfigProvider
'paths' => [
__DIR__,
],
'collectors' => [
AnnotationCollector::class,
AspectCollector::class,
],
],
];
}

View File

@ -17,7 +17,6 @@ use Hyperf\Di\Definition\ObjectDefinition;
use Hyperf\Di\Exception\NotFoundException;
use Hyperf\Di\Resolver\ResolverDispatcher;
use Hyperf\Dispatcher\Exceptions\InvalidArgumentException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
@ -61,11 +60,12 @@ class Container implements ContainerInterface
{
$this->definitionSource = $definitionSource;
$this->definitionResolver = new ResolverDispatcher($this);
$this->proxyFactory = new ProxyFactory($this);
$this->proxyFactory = new ProxyFactory();
// Auto-register the container.
$this->resolvedEntries = [
self::class => $this,
ContainerInterface::class => $this,
ProxyFactory::class => $this->proxyFactory,
];
}
@ -97,8 +97,6 @@ class Container implements ContainerInterface
* Finds an entry of the container by its identifier and returns it.
*
* @param string $name identifier of the entry to look for
* @throws NotFoundExceptionInterface no entry was found for **this** identifier
* @throws ContainerExceptionInterface error while retrieving the entry
* @return mixed entry
*/
public function get($name)

View File

@ -16,6 +16,7 @@ use Hyperf\Di\Annotation\AnnotationCollector;
use Hyperf\Di\Annotation\AspectCollector;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Di\Annotation\Scanner;
use Hyperf\Di\MetadataCacheCollector;
use Hyperf\Di\ReflectionManager;
use ReflectionClass;
use ReflectionFunctionAbstract;
@ -67,13 +68,13 @@ class DefinitionSource implements DefinitionSourceInterface
*/
private $scanner;
public function __construct(array $source, array $scanDir, Scanner $scanner, bool $enableCache = false)
public function __construct(array $source, ScanConfig $scanConfig, bool $enableCache = false)
{
$this->scanner = $scanner;
$this->scanner = new Scanner($scanConfig->getIgnoreAnnotations());
$this->enableCache = $enableCache;
// Scan the specified paths and collect the ast and annotations.
$this->scan($scanDir);
$this->scan($scanConfig->getDirs(), $scanConfig->getCollectors());
$this->source = $this->normalizeSource($source);
}
@ -149,8 +150,7 @@ class DefinitionSource implements DefinitionSourceInterface
}
/**
* @param array|callable|string $definitions
* @param mixed $definition
* @param array|callable|string $definition
*/
private function normalizeDefinition(string $identifier, $definition): ?DefinitionInterface
{
@ -214,18 +214,17 @@ class DefinitionSource implements DefinitionSourceInterface
return $definition;
}
private function scan(array $paths): bool
private function scan(array $paths, array $collectors): bool
{
if (empty($paths)) {
return true;
}
$pathsHash = md5(implode(',', $paths));
$cacher = new MetadataCacheCollector($collectors);
if ($this->hasAvailableCache($paths, $pathsHash, $this->cachePath)) {
$this->printLn('Detected an available cache, skip the scan process.');
[, $annotationMetadata, $aspectMetadata] = explode(PHP_EOL, file_get_contents($this->cachePath));
// Deserialize metadata when the cache is valid.
AnnotationCollector::deserialize($annotationMetadata);
AspectCollector::deserialize($aspectMetadata);
[, $serialized] = explode(PHP_EOL, file_get_contents($this->cachePath));
$cacher->unserialize($serialized);
return false;
}
$this->printLn('Scanning ...');
@ -243,7 +242,8 @@ class DefinitionSource implements DefinitionSourceInterface
mkdir($dirPath, 0755, true);
}
}
$data = implode(PHP_EOL, [$pathsHash, AnnotationCollector::serialize(), AspectCollector::serialize()]);
$data = implode(PHP_EOL, [$pathsHash, $cacher->serialize()]);
file_put_contents($this->cachePath, $data);
return true;
}

View File

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Di\Definition;
use Hyperf\Config\ProviderConfig;
use Hyperf\Di\Exception\Exception;
class DefinitionSourceFactory
{
/**
* @var bool
*/
protected $enableCache = false;
/**
* @var string
*/
protected $baseUri;
public function __construct(bool $enableCache = false)
{
$this->enableCache = $enableCache;
if (! defined('BASE_PATH')) {
throw new Exception('BASE_PATH is not defined.');
}
$this->baseUri = BASE_PATH;
}
public function __invoke()
{
$configDir = $this->baseUri . '/config';
$configFromProviders = [];
if (class_exists(ProviderConfig::class)) {
$configFromProviders = ProviderConfig::load();
}
$serverDependencies = $configFromProviders['dependencies'] ?? [];
if (file_exists($configDir . '/dependencies.php')) {
$definitions = include $configDir . '/dependencies.php';
$serverDependencies = array_replace($serverDependencies, $definitions['dependencies'] ?? []);
}
$scanDirs = $configFromProviders['scan']['paths'] ?? [];
$ignoreAnnotations = [];
$collectors = $configFromProviders['scan']['collectors'] ?? [];
if (file_exists($configDir . '/autoload/annotations.php')) {
$annotations = include $configDir . '/autoload/annotations.php';
$scanDirs = array_merge($scanDirs, $annotations['scan']['paths'] ?? []);
$ignoreAnnotations = $annotations['scan']['ignore_annotations'] ?? [];
$collectors = array_merge($collectors, $annotations['scan']['collectors'] ?? []);
}
$scanConfig = new ScanConfig($scanDirs, $ignoreAnnotations, $collectors);
return new DefinitionSource($serverDependencies, $scanConfig, $this->enableCache);
}
}

View File

@ -20,7 +20,7 @@ interface DefinitionSourceInterface
* Returns the DI definition for the entry name.
*
* @throws InvalidDefinitionException an invalid definition was found
* @return null|array
* @return null|DefinitionInterface
*/
public function getDefinition(string $name);

View File

@ -20,7 +20,7 @@ class FactoryDefinition implements DefinitionInterface
private $name;
/**
* @var callable
* @var callable|string
*/
private $factory;

View File

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Di\Definition;
class ScanConfig
{
/**
* @var array
*/
protected $dirs;
/**
* @var array
*/
protected $ignoreAnnotations;
/**
* @var array
*/
protected $collectors;
public function __construct(array $dirs = [], array $ignoreAnnotations = [], array $collectors = [])
{
$this->dirs = $dirs;
$this->ignoreAnnotations = $ignoreAnnotations;
$this->collectors = $collectors;
}
public function getDirs(): array
{
return $this->dirs;
}
public function setDirs(array $dirs): self
{
$this->dirs = $dirs;
return $this;
}
public function getIgnoreAnnotations(): array
{
return $this->ignoreAnnotations;
}
public function setIgnoreAnnotations(array $ignoreAnnotations): self
{
$this->ignoreAnnotations = $ignoreAnnotations;
return $this;
}
public function getCollectors(): array
{
return $this->collectors;
}
public function setCollectors(array $collectors): self
{
$this->collectors = $collectors;
return $this;
}
}

View File

@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Di;
class MetadataCacheCollector
{
/**
* @var array
*/
protected $collectors = [];
public function __construct(array $collectors)
{
$this->collectors = $collectors;
}
public function addCollector(string $collector)
{
$this->collectors = array_unique(array_merge(
$this->collectors,
[$collector]
));
}
public function clear()
{
$this->collectors = [];
}
public function serialize(): string
{
$metadata = [];
foreach ($this->collectors as $collector) {
if (is_string($collector) && method_exists($collector, 'serialize')) {
$metadata[$collector] = call([$collector, 'serialize']);
}
}
return json_encode($metadata);
}
public function unserialize($serialized): void
{
$metadatas = json_decode($serialized, true) ?? [];
$collectors = [];
foreach ($metadatas as $collector => $metadata) {
if (method_exists($collector, 'deserialize')) {
call([$collector, 'deserialize'], [$metadata]);
$collectors[] = $collector;
}
}
$this->collectors = $collectors;
}
}

View File

@ -35,4 +35,9 @@ interface MetadataCollectorInterface
* Deserialize the serialized metadata and set the metadata to holder.
*/
public static function deserialize(string $metadata): bool;
/**
* Return all metadata array.
*/
public static function list(): array;
}

View File

@ -13,7 +13,6 @@ declare(strict_types=1);
namespace Hyperf\Di;
use Hyperf\Di\Aop\Ast;
use Hyperf\Di\Definition\FactoryDefinition;
use Hyperf\Di\Definition\ObjectDefinition;
use Hyperf\Utils\Coroutine\Locker as CoLocker;
@ -40,16 +39,11 @@ class ProxyFactory
if (isset(static::$map[$identifier])) {
return static::$map[$identifier];
}
$proxyIdentifier = null;
if ($definition instanceof FactoryDefinition) {
$proxyIdentifier = $definition->getFactory() . '_' . md5($definition->getFactory());
$proxyIdentifier && $definition->setTarget($proxyIdentifier);
$this->loadProxy($definition->getName(), $definition->getFactory());
} elseif ($definition instanceof ObjectDefinition) {
$proxyIdentifier = $definition->getClassName() . '_' . md5($definition->getClassName());
$definition->setProxyClassName($proxyIdentifier);
$this->loadProxy($definition->getClassName(), $definition->getProxyClassName());
}
static::$map[$identifier] = $definition;
return static::$map[$identifier];
}

View File

@ -12,7 +12,6 @@ declare(strict_types=1);
namespace Hyperf\Di\Resolver;
use Hyperf\Di\Container;
use Hyperf\Di\Definition\DefinitionInterface;
use Hyperf\Di\Definition\ObjectDefinition;
use Hyperf\Di\Definition\PropertyInjection;
@ -48,14 +47,12 @@ class ObjectResolver implements ResolverInterface
/**
* ObjectResolver constructor.
*
* @param Container $container
*/
public function __construct(ContainerInterface $container, ResolverInterface $definitionResolver)
{
$this->container = $container;
$this->definitionResolver = $definitionResolver;
$this->proxyFactory = $container->getProxyFactory();
$this->proxyFactory = $container->get(ProxyFactory::class);
$this->parameterResolver = new ParameterResolver($definitionResolver);
}
@ -64,12 +61,18 @@ class ObjectResolver implements ResolverInterface
*
* @param DefinitionInterface $definition object that defines how the value should be obtained
* @param array $parameters optional parameters to use to build the entry
* @throws DependencyException
* @throws InvalidDefinitionException
* @throws DependencyException
* @return mixed value obtained from the definition
*/
public function resolve(DefinitionInterface $definition, array $parameters = [])
{
if (! $definition instanceof ObjectDefinition) {
throw InvalidDefinitionException::create(
$definition,
sprintf('Entry "%s" cannot be resolved: the class is not instanceof ObjectDefinition', $definition->getName())
);
}
return $this->createInstance($definition, $parameters);
}

View File

@ -21,7 +21,7 @@ use ReflectionParameter;
class ParameterResolver
{
/**
* @var DefinitionInterface
* @var ResolverInterface
*/
private $definitionResolver;

View File

@ -23,12 +23,12 @@ use RuntimeException;
class ResolverDispatcher implements ResolverInterface
{
/**
* @var ObjectResolver
* @var null|ObjectResolver
*/
protected $objectResolver;
/**
* @var FactoryResolver
* @var null|FactoryResolver
*/
protected $factoryResolver;

View File

@ -12,11 +12,15 @@ declare(strict_types=1);
namespace HyperfTest\Di;
use Hyperf\Di\Annotation\Aspect as AspectAnnotation;
use Hyperf\Di\Aop\Aspect;
use Hyperf\Di\Aop\RewriteCollection;
use HyperfTest\Di\Stub\AnnotationCollector;
use HyperfTest\Di\Stub\AspectCollector;
use HyperfTest\Di\Stub\DemoAnnotation;
use HyperfTest\Di\Stub\Foo;
use HyperfTest\Di\Stub\Foo2Aspect;
use HyperfTest\Di\Stub\FooAspect;
use PHPUnit\Framework\TestCase;
/**
@ -203,4 +207,22 @@ class AopAspectTest extends TestCase
$this->assertFalse(Aspect::isMatch('Foo/Bar/Baz', 'method', $rule));
$this->assertFalse(Aspect::isMatch('Foo/Bar', 'test', $rule));
}
public function testAspectAnnotation()
{
$annotation = new AspectAnnotation();
$annotation->collectClass(FooAspect::class);
$annotation->collectClass(Foo2Aspect::class);
$this->assertSame([
'classes' => [Foo::class],
'annotations' => [DemoAnnotation::class],
], AspectCollector::getRule(FooAspect::class));
$this->assertSame([
'classes' => [Foo::class],
'annotations' => [],
], AspectCollector::getRule(Foo2Aspect::class));
}
}

125
src/di/tests/AstTest.php Normal file
View File

@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Di;
use Hyperf\Di\Aop\Ast;
use HyperfTest\Di\Stub\AspectCollector;
use HyperfTest\Di\Stub\Ast\Bar2;
use HyperfTest\Di\Stub\Ast\Bar3;
use HyperfTest\Di\Stub\Ast\BarAspect;
use HyperfTest\Di\Stub\Ast\Foo;
use PHPUnit\Framework\TestCase;
/**
* @internal
* @coversNothing
*/
class AstTest extends TestCase
{
public function testProxy()
{
$ast = new Ast();
$proxyClass = Foo::class . 'Proxy';
$code = $ast->proxy(Foo::class, $proxyClass);
$this->assertEquals('<?php
declare (strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Di\Stub\Ast;
class FooProxy extends Foo
{
use \Hyperf\Di\Aop\ProxyTrait;
}', $code);
}
public function testParentMethods()
{
$ast = new Ast();
$proxyClass = Bar2::class . 'Proxy';
$code = $ast->proxy(Bar2::class, $proxyClass);
$this->assertEquals('<?php
declare (strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Di\Stub\Ast;
class Bar2Proxy extends Bar2
{
use \Hyperf\Di\Aop\ProxyTrait;
public function __construct(int $id)
{
Bar::__construct($id);
}
public static function build()
{
return Bar::$items;
}
}', $code);
}
public function testRewriteMethods()
{
$aspect = BarAspect::class;
AspectCollector::setAround($aspect, [
Bar3::class,
], []);
$ast = new Ast();
$proxyClass = Bar3::class . 'Proxy';
$code = $ast->proxy(Bar3::class, $proxyClass);
$this->assertEquals('<?php
declare (strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Di\Stub\Ast;
class Bar3Proxy extends Bar3
{
use \Hyperf\Di\Aop\ProxyTrait;
public function getId() : int
{
$__function__ = __FUNCTION__;
$__method__ = __METHOD__;
return self::__proxyCall(Bar3::class, __FUNCTION__, self::getParamsMap(Bar3::class, __FUNCTION__, func_get_args()), function () use($__function__, $__method__) {
return parent::getId();
});
}
}', $code);
}
}

Some files were not shown because too many files have changed in this diff Show More