wesmiler 5 лет назад
Родитель
Сommit
2ed3be0bba
48 измененных файлов с 1457 добавлено и 0 удалено
  1. 17 0
      .env.example
  2. 13 0
      .gitignore
  3. 57 0
      .gitlab-ci.yml
  4. 91 0
      .php_cs
  5. 60 0
      Dockerfile
  6. 36 0
      README.md
  7. 26 0
      app/Constants/ErrorCode.php
  8. 38 0
      app/Controller/AbstractController.php
  9. 26 0
      app/Controller/IndexController.php
  10. 28 0
      app/Exception/BusinessException.php
  11. 43 0
      app/Exception/Handler/AppExceptionHandler.php
  12. 39 0
      app/Exception/Handler/TokenValidExceptionHandler.php
  13. 61 0
      app/Listener/DbQueryExecutedListener.php
  14. 79 0
      app/Listener/QueueHandleListener.php
  15. 21 0
      app/Model/Model.php
  16. 22 0
      app/Process/AsyncQueueConsumer.php
  17. 23 0
      bin/hyperf.php
  18. 86 0
      composer.json
  19. 21 0
      config/autoload/annotations.php
  20. 22 0
      config/autoload/apollo.php
  21. 13 0
      config/autoload/aspects.php
  22. 21 0
      config/autoload/async_queue.php
  23. 18 0
      config/autoload/cache.php
  24. 13 0
      config/autoload/commands.php
  25. 5 0
      config/autoload/crontab.php
  26. 49 0
      config/autoload/databases.php
  27. 13 0
      config/autoload/dependencies.php
  28. 44 0
      config/autoload/devtool.php
  29. 19 0
      config/autoload/exceptions.php
  30. 0 0
      config/autoload/jwt.php
  31. 13 0
      config/autoload/listeners.php
  32. 30 0
      config/autoload/logger.php
  33. 15 0
      config/autoload/middlewares.php
  34. 49 0
      config/autoload/opentracing.php
  35. 13 0
      config/autoload/processes.php
  36. 27 0
      config/autoload/redis.php
  37. 45 0
      config/autoload/server.php
  38. 29 0
      config/autoload/services.php
  39. 31 0
      config/config.php
  40. 24 0
      config/container.php
  41. 18 0
      config/routes.php
  42. 30 0
      deploy.test.yml
  43. 10 0
      phpstan.neon
  44. 21 0
      phpunit.xml
  45. 27 0
      test/Cases/ExampleTest.php
  46. 42 0
      test/HttpTestCase.php
  47. 29 0
      test/bootstrap.php
  48. 0 0
      新建文本文档.txt

+ 17 - 0
.env.example

@@ -0,0 +1,17 @@
+APP_NAME=skeleton
+APP_ENV=dev
+
+DB_DRIVER=mysql
+DB_HOST=localhost
+DB_PORT=3306
+DB_DATABASE=hyperf
+DB_USERNAME=root
+DB_PASSWORD=
+DB_CHARSET=utf8mb4
+DB_COLLATION=utf8mb4_unicode_ci
+DB_PREFIX=
+
+REDIS_HOST=localhost
+REDIS_AUTH=(null)
+REDIS_PORT=6379
+REDIS_DB=0

+ 13 - 0
.gitignore

@@ -0,0 +1,13 @@
+.buildpath
+.settings/
+.project
+*.patch
+.idea/
+.git/
+runtime/
+vendor/
+.phpintel/
+.env
+.DS_Store
+*.lock
+.phpunit*

+ 57 - 0
.gitlab-ci.yml

@@ -0,0 +1,57 @@
+# usermod -aG docker gitlab-runner
+
+stages:
+  - build
+  - deploy
+
+variables:
+  PROJECT_NAME: hyperf
+  REGISTRY_URL: registry-docker.org
+
+build_test_docker:
+  stage: build
+  before_script:
+#    - git submodule sync --recursive
+#    - git submodule update --init --recursive
+  script:
+    - docker build . -t $PROJECT_NAME
+    - docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:test
+    - docker push $REGISTRY_URL/$PROJECT_NAME:test
+  only:
+    - test
+  tags:
+    - builder
+
+deploy_test_docker:
+  stage: deploy
+  script:
+    - docker stack deploy -c deploy.test.yml --with-registry-auth $PROJECT_NAME
+  only:
+    - test
+  tags:
+    - test
+
+build_docker:
+  stage: build
+  before_script:
+#    - git submodule sync --recursive
+#    - git submodule update --init --recursive
+  script:
+    - docker build . -t $PROJECT_NAME
+    - docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:$CI_COMMIT_REF_NAME
+    - docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:latest
+    - docker push $REGISTRY_URL/$PROJECT_NAME:$CI_COMMIT_REF_NAME
+    - docker push $REGISTRY_URL/$PROJECT_NAME:latest
+  only:
+    - tags
+  tags:
+    - builder
+
+deploy_docker:
+  stage: deploy
+  script:
+    - echo SUCCESS
+  only:
+    - tags
+  tags:
+    - builder

+ 91 - 0
.php_cs

@@ -0,0 +1,91 @@
+<?php
+
+$header = <<<'EOF'
+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
+EOF;
+
+return PhpCsFixer\Config::create()
+    ->setRiskyAllowed(true)
+    ->setRules([
+        '@PSR2' => true,
+        '@Symfony' => true,
+        '@DoctrineAnnotation' => true,
+        '@PhpCsFixer' => true,
+        'header_comment' => [
+            'commentType' => 'PHPDoc',
+            'header' => $header,
+            'separate' => 'none',
+            'location' => 'after_declare_strict',
+        ],
+        'array_syntax' => [
+            'syntax' => 'short'
+        ],
+        'list_syntax' => [
+            'syntax' => 'short'
+        ],
+        'concat_space' => [
+            'spacing' => 'one'
+        ],
+        'blank_line_before_statement' => [
+            'statements' => [
+                'declare',
+            ],
+        ],
+        'general_phpdoc_annotation_remove' => [
+            'annotations' => [
+                'author'
+            ],
+        ],
+        'ordered_imports' => [
+            'imports_order' => [
+                'class', 'function', 'const',
+            ],
+            'sort_algorithm' => 'alpha',
+        ],
+        'single_line_comment_style' => [
+            'comment_types' => [
+            ],
+        ],
+        'yoda_style' => [
+            'always_move_variable' => false,
+            'equal' => false,
+            'identical' => false,
+        ],
+        'phpdoc_align' => [
+            'align' => 'left',
+        ],
+        'multiline_whitespace_before_semicolons' => [
+            'strategy' => 'no_multi_line',
+        ],
+        'constant_case' => [
+            'case' => 'lower',
+        ],
+        'class_attributes_separation' => true,
+        'combine_consecutive_unsets' => true,
+        'declare_strict_types' => true,
+        'linebreak_after_opening_tag' => true,
+        'lowercase_static_reference' => true,
+        'no_useless_else' => true,
+        'no_unused_imports' => true,
+        'not_operator_with_successor_space' => true,
+        'not_operator_with_space' => false,
+        'ordered_class_elements' => true,
+        'php_unit_strict' => false,
+        'phpdoc_separation' => false,
+        'single_quote' => true,
+        'standardize_not_equals' => true,
+        'multiline_comment_opening_closing' => true,
+    ])
+    ->setFinder(
+        PhpCsFixer\Finder::create()
+            ->exclude('public')
+            ->exclude('runtime')
+            ->exclude('vendor')
+            ->in(__DIR__)
+    )
+    ->setUsingCache(false);

+ 60 - 0
Dockerfile

@@ -0,0 +1,60 @@
+# Default Dockerfile
+#
+# @link     https://www.hyperf.io
+# @document https://hyperf.wiki
+# @contact  group@hyperf.io
+# @license  https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
+
+FROM hyperf/hyperf:7.4-alpine-v3.11-cli
+LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"
+
+##
+# ---------- env settings ----------
+##
+# --build-arg timezone=Asia/Shanghai
+ARG timezone
+
+ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
+    COMPOSER_VERSION=1.10.10 \
+    APP_ENV=prod \
+    SCAN_CACHEABLE=(true)
+
+# update
+RUN set -ex \
+    # install composer
+    && cd /tmp \
+    && wget https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar \
+    && chmod u+x composer.phar \
+    && mv composer.phar /usr/local/bin/composer \
+    # show php version and extensions
+    && php -v \
+    && php -m \
+    && php --ri swoole \
+    #  ---------- some config ----------
+    && cd /etc/php7 \
+    # - config PHP
+    && { \
+        echo "upload_max_filesize=128M"; \
+        echo "post_max_size=128M"; \
+        echo "memory_limit=1G"; \
+        echo "date.timezone=${TIMEZONE}"; \
+    } | tee conf.d/99_overrides.ini \
+    # - config timezone
+    && ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
+    && echo "${TIMEZONE}" > /etc/timezone \
+    # ---------- clear works ----------
+    && rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
+    && echo -e "\033[42;37m Build Completed :).\033[0m\n"
+
+WORKDIR /opt/www
+
+# Composer Cache
+# COPY ./composer.* /opt/www/
+# RUN composer install --no-dev --no-scripts
+
+COPY . /opt/www
+RUN composer install --no-dev -o && php bin/hyperf.php
+
+EXPOSE 9501
+
+ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]

+ 36 - 0
README.md

@@ -0,0 +1,36 @@
+# Introduction
+
+This is a skeleton application using the Hyperf framework. This application is meant to be used as a starting place for those looking to get their feet wet with Hyperf Framework.
+
+# Requirements
+
+Hyperf has some requirements for the system environment, it can only run under Linux and Mac environment, but due to the development of Docker virtualization technology, Docker for Windows can also be used as the running environment under Windows.
+
+The various versions of Dockerfile have been prepared for you in the [hyperf\hyperf-docker](https://github.com/hyperf/hyperf-docker) project, or directly based on the already built [hyperf\hyperf](https://hub.docker.com/r/hyperf/hyperf) Image to run.
+
+When you don't want to use Docker as the basis for your running environment, you need to make sure that your operating environment meets the following requirements:  
+
+ - PHP >= 7.2
+ - Swoole PHP extension >= 4.4,and Disabled `Short Name`
+ - OpenSSL PHP extension
+ - JSON PHP extension
+ - PDO PHP extension (If you need to use MySQL Client)
+ - Redis PHP extension (If you need to use Redis Client)
+ - Protobuf PHP extension (If you need to use gRPC Server of Client)
+
+# Installation using Composer
+
+The easiest way to create a new Hyperf project is to use Composer. If you don't have it already installed, then please install as per the documentation.
+
+To create your new Hyperf project:
+
+$ composer create-project hyperf/hyperf-skeleton path/to/install
+
+Once installed, you can run the server immediately using the command below.
+
+$ cd path/to/install
+$ php bin/hyperf.php start
+
+This will start the cli-server on port `9501`, and bind it to all network interfaces. You can then visit the site at `http://localhost:9501/`
+
+which will bring up Hyperf default home page.

+ 26 - 0
app/Constants/ErrorCode.php

@@ -0,0 +1,26 @@
+<?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 App\Constants;
+
+use Hyperf\Constants\AbstractConstants;
+use Hyperf\Constants\Annotation\Constants;
+
+/**
+ * @Constants
+ */
+class ErrorCode extends AbstractConstants
+{
+    /**
+     * @Message("Server Error!")
+     */
+    const SERVER_ERROR = 500;
+}

+ 38 - 0
app/Controller/AbstractController.php

@@ -0,0 +1,38 @@
+<?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 App\Controller;
+
+use Hyperf\Di\Annotation\Inject;
+use Hyperf\HttpServer\Contract\RequestInterface;
+use Hyperf\HttpServer\Contract\ResponseInterface;
+use Psr\Container\ContainerInterface;
+
+abstract class AbstractController
+{
+    /**
+     * @Inject
+     * @var ContainerInterface
+     */
+    protected $container;
+
+    /**
+     * @Inject
+     * @var RequestInterface
+     */
+    protected $request;
+
+    /**
+     * @Inject
+     * @var ResponseInterface
+     */
+    protected $response;
+}

+ 26 - 0
app/Controller/IndexController.php

@@ -0,0 +1,26 @@
+<?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 App\Controller;
+
+class IndexController extends AbstractController
+{
+    public function index()
+    {
+        $user = $this->request->input('user', 'Hyperf');
+        $method = $this->request->getMethod();
+
+        return [
+            'method' => $method,
+            'message' => "Hello {$user}.",
+        ];
+    }
+}

+ 28 - 0
app/Exception/BusinessException.php

@@ -0,0 +1,28 @@
+<?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 App\Exception;
+
+use App\Constants\ErrorCode;
+use Hyperf\Server\Exception\ServerException;
+use Throwable;
+
+class BusinessException extends ServerException
+{
+    public function __construct(int $code = 0, string $message = null, Throwable $previous = null)
+    {
+        if (is_null($message)) {
+            $message = ErrorCode::getMessage($code);
+        }
+
+        parent::__construct($message, $code, $previous);
+    }
+}

+ 43 - 0
app/Exception/Handler/AppExceptionHandler.php

@@ -0,0 +1,43 @@
+<?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 App\Exception\Handler;
+
+use Hyperf\Contract\StdoutLoggerInterface;
+use Hyperf\ExceptionHandler\ExceptionHandler;
+use Hyperf\HttpMessage\Stream\SwooleStream;
+use Psr\Http\Message\ResponseInterface;
+use Throwable;
+
+class AppExceptionHandler extends ExceptionHandler
+{
+    /**
+     * @var StdoutLoggerInterface
+     */
+    protected $logger;
+
+    public function __construct(StdoutLoggerInterface $logger)
+    {
+        $this->logger = $logger;
+    }
+
+    public function handle(Throwable $throwable, ResponseInterface $response)
+    {
+        $this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile()));
+        $this->logger->error($throwable->getTraceAsString());
+        return $response->withHeader('Server', 'Hyperf')->withStatus(500)->withBody(new SwooleStream('Internal Server Error.'));
+    }
+
+    public function isValid(Throwable $throwable): bool
+    {
+        return true;
+    }
+}

+ 39 - 0
app/Exception/Handler/TokenValidExceptionHandler.php

@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * JWT token valid
+ * @author wesmiler
+ */
+namespace App\Exception\Handler;
+
+use Hyperf\Contract\StdoutLoggerInterface;
+use Hyperf\ExceptionHandler\ExceptionHandler;
+use Hyperf\HttpMessage\Stream\SwooleStream;
+use Psr\Http\Message\ResponseInterface;
+use Throwable;
+
+class TokenValidExceptionHandler extends ExceptionHandler
+{
+    /**
+     * @var StdoutLoggerInterface
+     */
+    protected $logger;
+
+    public function __construct(StdoutLoggerInterface $logger)
+    {
+        $this->logger = $logger;
+    }
+
+    public function handle(Throwable $throwable, ResponseInterface $response)
+    {
+        $this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile()));
+        $this->logger->error($throwable->getTraceAsString());
+        return $response->withHeader('Server', 'Hyperf')->withStatus(500)->withBody(new SwooleStream('Internal Server Error.'));
+    }
+
+    public function isValid(Throwable $throwable): bool
+    {
+        return true;
+    }
+}

+ 61 - 0
app/Listener/DbQueryExecutedListener.php

@@ -0,0 +1,61 @@
+<?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 App\Listener;
+
+use Hyperf\Database\Events\QueryExecuted;
+use Hyperf\Event\Annotation\Listener;
+use Hyperf\Event\Contract\ListenerInterface;
+use Hyperf\Logger\LoggerFactory;
+use Hyperf\Utils\Arr;
+use Hyperf\Utils\Str;
+use Psr\Container\ContainerInterface;
+use Psr\Log\LoggerInterface;
+
+/**
+ * @Listener
+ */
+class DbQueryExecutedListener implements ListenerInterface
+{
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    public function __construct(ContainerInterface $container)
+    {
+        $this->logger = $container->get(LoggerFactory::class)->get('sql');
+    }
+
+    public function listen(): array
+    {
+        return [
+            QueryExecuted::class,
+        ];
+    }
+
+    /**
+     * @param QueryExecuted $event
+     */
+    public function process(object $event)
+    {
+        if ($event instanceof QueryExecuted) {
+            $sql = $event->sql;
+            if (! Arr::isAssoc($event->bindings)) {
+                foreach ($event->bindings as $key => $value) {
+                    $sql = Str::replaceFirst('?', "'{$value}'", $sql);
+                }
+            }
+
+            $this->logger->info(sprintf('[%s] %s', $event->time, $sql));
+        }
+    }
+}

+ 79 - 0
app/Listener/QueueHandleListener.php

@@ -0,0 +1,79 @@
+<?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 App\Listener;
+
+use Hyperf\AsyncQueue\Event\AfterHandle;
+use Hyperf\AsyncQueue\Event\BeforeHandle;
+use Hyperf\AsyncQueue\Event\Event;
+use Hyperf\AsyncQueue\Event\FailedHandle;
+use Hyperf\AsyncQueue\Event\RetryHandle;
+use Hyperf\Event\Annotation\Listener;
+use Hyperf\Event\Contract\ListenerInterface;
+use Hyperf\ExceptionHandler\Formatter\FormatterInterface;
+use Hyperf\Logger\LoggerFactory;
+
+/**
+ * @Listener
+ */
+class QueueHandleListener implements ListenerInterface
+{
+    /**
+     * @var \Psr\Log\LoggerInterface
+     */
+    protected $logger;
+
+    /**
+     * @var FormatterInterface
+     */
+    protected $formatter;
+
+    public function __construct(LoggerFactory $loggerFactory, FormatterInterface $formatter)
+    {
+        $this->logger = $loggerFactory->get('queue');
+        $this->formatter = $formatter;
+    }
+
+    public function listen(): array
+    {
+        return [
+            AfterHandle::class,
+            BeforeHandle::class,
+            FailedHandle::class,
+            RetryHandle::class,
+        ];
+    }
+
+    public function process(object $event)
+    {
+        if ($event instanceof Event && $event->message->job()) {
+            $job = $event->message->job();
+            $jobClass = get_class($job);
+            $date = date('Y-m-d H:i:s');
+
+            switch (true) {
+                case $event instanceof BeforeHandle:
+                    $this->logger->info(sprintf('[%s] Processing %s.', $date, $jobClass));
+                    break;
+                case $event instanceof AfterHandle:
+                    $this->logger->info(sprintf('[%s] Processed %s.', $date, $jobClass));
+                    break;
+                case $event instanceof FailedHandle:
+                    $this->logger->error(sprintf('[%s] Failed %s.', $date, $jobClass));
+                    $this->logger->error($this->formatter->format($event->getThrowable()));
+                    break;
+                case $event instanceof RetryHandle:
+                    $this->logger->warning(sprintf('[%s] Retried %s.', $date, $jobClass));
+                    break;
+            }
+        }
+    }
+}

+ 21 - 0
app/Model/Model.php

@@ -0,0 +1,21 @@
+<?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 App\Model;
+
+use Hyperf\DbConnection\Model\Model as BaseModel;
+use Hyperf\ModelCache\Cacheable;
+use Hyperf\ModelCache\CacheableInterface;
+
+abstract class Model extends BaseModel implements CacheableInterface
+{
+    use Cacheable;
+}

+ 22 - 0
app/Process/AsyncQueueConsumer.php

@@ -0,0 +1,22 @@
+<?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 App\Process;
+
+use Hyperf\AsyncQueue\Process\ConsumerProcess;
+use Hyperf\Process\Annotation\Process;
+
+/**
+ * @Process
+ */
+class AsyncQueueConsumer extends ConsumerProcess
+{
+}

+ 23 - 0
bin/hyperf.php

@@ -0,0 +1,23 @@
+#!/usr/bin/env php
+<?php
+
+ini_set('display_errors', 'on');
+ini_set('display_startup_errors', 'on');
+
+error_reporting(E_ALL);
+
+! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
+! defined('SWOOLE_HOOK_ALL') && define('SWOOLE_HOOK_ALL', null);
+! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);
+
+require BASE_PATH . '/vendor/autoload.php';
+
+// Self-called anonymous function that creates its own scope and keep the global namespace clean.
+(function () {
+    Hyperf\Di\ClassLoader::init();
+    /** @var \Psr\Container\ContainerInterface $container */
+    $container = require BASE_PATH . '/config/container.php';
+
+    $application = $container->get(\Hyperf\Contract\ApplicationInterface::class);
+    $application->run();
+})();

+ 86 - 0
composer.json

@@ -0,0 +1,86 @@
+{
+    "name": "hyperf/hyperf-skeleton",
+    "type": "project",
+    "keywords": [
+        "php",
+        "swoole",
+        "framework",
+        "hyperf",
+        "microservice",
+        "middleware"
+    ],
+    "description": "A coroutine framework that focuses on hyperspeed and flexible, specifically use for build microservices and middlewares.",
+    "license": "Apache-2.0",
+    "require": {
+        "php": ">=7.2",
+        "ext-swoole": ">=4.5",
+        "hyperf/cache": "~2.0.0",
+        "hyperf/command": "~2.0.0",
+        "hyperf/config": "~2.0.0",
+        "hyperf/db-connection": "~2.0.0",
+        "hyperf/framework": "~2.0.0",
+        "hyperf/guzzle": "~2.0.0",
+        "hyperf/http-server": "~2.0.0",
+        "hyperf/logger": "~2.0.0",
+        "hyperf/memory": "~2.0.0",
+        "hyperf/process": "~2.0.0",
+        "hyperf/redis": "~2.0.0",
+        "hyperf/database": "~2.0.0",
+        "hyperf/json-rpc": "~2.0.0",
+        "hyperf/rpc": "~2.0.0",
+        "hyperf/rpc-client": "~2.0.0",
+        "hyperf/rpc-server": "~2.0.0",
+        "hyperf/config-apollo": "~2.0.0",
+        "hyperf/constants": "~2.0.0",
+        "hyperf/async-queue": "~2.0.0",
+        "hyperf/model-cache": "~2.0.0",
+        "hyperf/tracer": "~2.0.0"
+    },
+    "require-dev": {
+        "swoole/ide-helper": "^4.5",
+        "friendsofphp/php-cs-fixer": "^2.14",
+        "mockery/mockery": "^1.0",
+        "phpstan/phpstan": "^0.12",
+        "hyperf/devtool": "~2.0.0",
+        "hyperf/testing": "~2.0.0"
+    },
+    "suggest": {
+        "ext-openssl": "Required to use HTTPS.",
+        "ext-json": "Required to use JSON.",
+        "ext-pdo": "Required to use MySQL Client.",
+        "ext-pdo_mysql": "Required to use MySQL Client.",
+        "ext-redis": "Required to use Redis Client."
+    },
+    "autoload": {
+        "psr-4": {
+            "App\\": "app/"
+        },
+        "files": []
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "HyperfTest\\": "./test/"
+        }
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true,
+    "extra": [],
+    "scripts": {
+        "post-root-package-install": [
+            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
+        ],
+        "post-autoload-dump": [
+            "rm -rf runtime/container"
+        ],
+        "test": "co-phpunit -c phpunit.xml --colors=always",
+        "cs-fix": "php-cs-fixer fix $1",
+        "analyse": "phpstan analyse --memory-limit 300M -l 0 -c phpstan.neon ./app ./config",
+        "start": "php ./bin/hyperf.php start"
+    },
+    "repositories": {
+        "packagist": {
+            "type": "composer",
+            "url": "https://mirrors.aliyun.com/composer/"
+        }
+    }
+}

+ 21 - 0
config/autoload/annotations.php

@@ -0,0 +1,21 @@
+<?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 [
+    'scan' => [
+        'paths' => [
+            BASE_PATH . '/app',
+        ],
+        'ignore_annotations' => [
+            'mixin',
+        ],
+    ],
+];

+ 22 - 0
config/autoload/apollo.php

@@ -0,0 +1,22 @@
+<?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 [
+    'enable' => false,
+    'server' => env('APOLLO_SERVER', 'http://127.0.0.1:8080'),
+    'appid' => 'Your APP ID',
+    'cluster' => 'default',
+    'namespaces' => [
+        'application',
+    ],
+    'interval' => 5,
+    'strict_mode' => false,
+];

+ 13 - 0
config/autoload/aspects.php

@@ -0,0 +1,13 @@
+<?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 [
+];

+ 21 - 0
config/autoload/async_queue.php

@@ -0,0 +1,21 @@
+<?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' => [
+        'driver' => Hyperf\AsyncQueue\Driver\RedisDriver::class,
+        'channel' => 'queue',
+        'timeout' => 2,
+        'retry_seconds' => 5,
+        'handle_timeout' => 10,
+        'processes' => 1,
+    ],
+];

+ 18 - 0
config/autoload/cache.php

@@ -0,0 +1,18 @@
+<?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' => [
+        'driver' => Hyperf\Cache\Driver\RedisDriver::class,
+        'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class,
+        'prefix' => 'c:',
+    ],
+];

+ 13 - 0
config/autoload/commands.php

@@ -0,0 +1,13 @@
+<?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 [
+];

+ 5 - 0
config/autoload/crontab.php

@@ -0,0 +1,5 @@
+<?php
+return [
+    // 是否开启定时任务
+    'enable' => true,
+];

+ 49 - 0
config/autoload/databases.php

@@ -0,0 +1,49 @@
+<?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' => [
+        'driver' => env('DB_DRIVER', 'mysql'),
+        'host' => env('DB_HOST', 'localhost'),
+        'port' => env('DB_PORT', 3306),
+        'database' => env('DB_DATABASE', 'hyperf'),
+        'username' => env('DB_USERNAME', 'root'),
+        'password' => env('DB_PASSWORD', ''),
+        'charset' => env('DB_CHARSET', 'utf8mb4'),
+        'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
+        'prefix' => env('DB_PREFIX', ''),
+        'pool' => [
+            'min_connections' => 1,
+            'max_connections' => 10,
+            'connect_timeout' => 10.0,
+            'wait_timeout' => 3.0,
+            'heartbeat' => -1,
+            'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
+        ],
+        'cache' => [
+            'handler' => Hyperf\ModelCache\Handler\RedisHandler::class,
+            'cache_key' => '{mc:%s:m:%s}:%s:%s',
+            'prefix' => 'default',
+            'ttl' => 3600 * 24,
+            'empty_model_ttl' => 600,
+            'load_script' => true,
+        ],
+        'commands' => [
+            'gen:model' => [
+                'path' => 'app/Model',
+                'force_casts' => true,
+                'inheritance' => 'Model',
+                'uses' => '',
+                'table_mapping' => [],
+            ],
+        ],
+    ],
+];

+ 13 - 0
config/autoload/dependencies.php

@@ -0,0 +1,13 @@
+<?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 [
+];

+ 44 - 0
config/autoload/devtool.php

@@ -0,0 +1,44 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+return [
+    'generator' => [
+        'amqp' => [
+            'consumer' => [
+                'namespace' => 'App\\Amqp\\Consumer',
+            ],
+            'producer' => [
+                'namespace' => 'App\\Amqp\\Producer',
+            ],
+        ],
+        'aspect' => [
+            'namespace' => 'App\\Aspect',
+        ],
+        'command' => [
+            'namespace' => 'App\\Command',
+        ],
+        'controller' => [
+            'namespace' => 'App\\Controller',
+        ],
+        'job' => [
+            'namespace' => 'App\\Job',
+        ],
+        'listener' => [
+            'namespace' => 'App\\Listener',
+        ],
+        'middleware' => [
+            'namespace' => 'App\\Middleware',
+        ],
+        'Process' => [
+            'namespace' => 'App\\Processes',
+        ],
+    ],
+];

+ 19 - 0
config/autoload/exceptions.php

@@ -0,0 +1,19 @@
+<?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 [
+    'handler' => [
+        'http' => [
+            Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class,
+            App\Exception\Handler\AppExceptionHandler::class,
+        ],
+    ],
+];

新建文本文档 (2).txt → config/autoload/jwt.php


+ 13 - 0
config/autoload/listeners.php

@@ -0,0 +1,13 @@
+<?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 [
+];

+ 30 - 0
config/autoload/logger.php

@@ -0,0 +1,30 @@
+<?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' => [
+        'handler' => [
+            'class' => Monolog\Handler\StreamHandler::class,
+            'constructor' => [
+                'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
+                'level' => Monolog\Logger::DEBUG,
+            ],
+        ],
+        'formatter' => [
+            'class' => Monolog\Formatter\LineFormatter::class,
+            'constructor' => [
+                'format' => null,
+                'dateFormat' => 'Y-m-d H:i:s',
+                'allowInlineLineBreaks' => true,
+            ],
+        ],
+    ],
+];

+ 15 - 0
config/autoload/middlewares.php

@@ -0,0 +1,15 @@
+<?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 [
+    'http' => [
+    ],
+];

+ 49 - 0
config/autoload/opentracing.php

@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+use Zipkin\Samplers\BinarySampler;
+
+return [
+    'default' => env('TRACER_DRIVER', 'zipkin'),
+    'enable' => [
+        'guzzle' => env('TRACER_ENABLE_GUZZLE', false),
+        'redis' => env('TRACER_ENABLE_REDIS', false),
+        'db' => env('TRACER_ENABLE_DB', false),
+        'method' => env('TRACER_ENABLE_METHOD', false),
+    ],
+    'tracer' => [
+        'zipkin' => [
+            'driver' => \Hyperf\Tracer\Adapter\ZipkinTracerFactory::class,
+            'app' => [
+                'name' => env('APP_NAME', 'skeleton'),
+                // Hyperf will detect the system info automatically as the value if ipv4, ipv6, port is null
+                'ipv4' => '127.0.0.1',
+                'ipv6' => null,
+                'port' => 9501,
+            ],
+            'options' => [
+                'endpoint_url' => env('ZIPKIN_ENDPOINT_URL', 'http://localhost:9411/api/v2/spans'),
+                'timeout' => env('ZIPKIN_TIMEOUT', 1),
+            ],
+            'sampler' => BinarySampler::createAsAlwaysSample(),
+        ],
+        'jaeger' => [
+            'driver' => \Hyperf\Tracer\Adapter\JaegerTracerFactory::class,
+            'name' => env('APP_NAME', 'skeleton'),
+            'options' => [
+                'local_agent' => [
+                    'reporting_host' => env('JAEGER_REPORTING_HOST', 'localhost'),
+                    'reporting_port' => env('JAEGER_REPORTING_PORT', 5775),
+                ],
+            ],
+        ],
+    ],
+];

+ 13 - 0
config/autoload/processes.php

@@ -0,0 +1,13 @@
+<?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 [
+];

+ 27 - 0
config/autoload/redis.php

@@ -0,0 +1,27 @@
+<?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' => [
+        'host' => env('REDIS_HOST', 'localhost'),
+        'auth' => env('REDIS_AUTH', null),
+        'port' => (int) env('REDIS_PORT', 6379),
+        'db' => (int) env('REDIS_DB', 0),
+        'pool' => [
+            'min_connections' => 1,
+            'max_connections' => 10,
+            'connect_timeout' => 10.0,
+            'wait_timeout' => 3.0,
+            'heartbeat' => -1,
+            'max_idle_time' => (float) env('REDIS_MAX_IDLE_TIME', 60),
+        ],
+    ],
+];

+ 45 - 0
config/autoload/server.php

@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+use Hyperf\Server\Server;
+use Hyperf\Server\SwooleEvent;
+
+return [
+    'mode' => SWOOLE_PROCESS,
+    'servers' => [
+        [
+            'name' => 'http',
+            'type' => Server::SERVER_HTTP,
+            'host' => '0.0.0.0',
+            'port' => 9501,
+            'sock_type' => SWOOLE_SOCK_TCP,
+            'callbacks' => [
+                SwooleEvent::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
+            ],
+        ],
+    ],
+    'settings' => [
+        'enable_coroutine' => true,
+        'worker_num' => swoole_cpu_num(),
+        'pid_file' => BASE_PATH . '/runtime/hyperf.pid',
+        'open_tcp_nodelay' => true,
+        'max_coroutine' => 100000,
+        'open_http2_protocol' => true,
+        'max_request' => 100000,
+        'socket_buffer_size' => 2 * 1024 * 1024,
+        'buffer_output_size' => 2 * 1024 * 1024,
+    ],
+    'callbacks' => [
+        SwooleEvent::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'],
+        SwooleEvent::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'],
+        SwooleEvent::ON_WORKER_EXIT => [Hyperf\Framework\Bootstrap\WorkerExitCallback::class, 'onWorkerExit'],
+    ],
+];

+ 29 - 0
config/autoload/services.php

@@ -0,0 +1,29 @@
+<?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 [
+    'consumers' => [
+        [
+            // The service name, this name should as same as with the name of service provider.
+            'name' => 'YourServiceName',
+            // The service registry, if `nodes` is missing below, then you should provide this configs.
+            'registry' => [
+                'protocol' => 'consul',
+                'address' => 'Enter the address of service registry',
+            ],
+            // If `registry` is missing, then you should provide the nodes configs.
+            'nodes' => [
+                // Provide the host and port of the service provider.
+                // ['host' => 'The host of the service provider', 'port' => 9502]
+            ],
+        ],
+    ],
+];

+ 31 - 0
config/config.php

@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+use Hyperf\Contract\StdoutLoggerInterface;
+use Psr\Log\LogLevel;
+
+return [
+    'app_name' => env('APP_NAME', 'skeleton'),
+    'app_env' => env('APP_ENV', 'dev'),
+    'scan_cacheable' => env('SCAN_CACHEABLE', false),
+    StdoutLoggerInterface::class => [
+        'log_level' => [
+            LogLevel::ALERT,
+            LogLevel::CRITICAL,
+            LogLevel::DEBUG,
+            LogLevel::EMERGENCY,
+            LogLevel::ERROR,
+            LogLevel::INFO,
+            LogLevel::NOTICE,
+            LogLevel::WARNING,
+        ],
+    ],
+];

+ 24 - 0
config/container.php

@@ -0,0 +1,24 @@
+<?php
+/**
+ * Initialize a dependency injection container that implemented PSR-11 and return the container.
+ */
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+use Hyperf\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);

+ 18 - 0
config/routes.php

@@ -0,0 +1,18 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+use Hyperf\HttpServer\Router\Router;
+
+Router::addRoute(['GET', 'POST', 'HEAD'], '/', 'App\Controller\IndexController@index');
+
+Router::get('/favicon.ico', function () {
+    return '';
+});

+ 30 - 0
deploy.test.yml

@@ -0,0 +1,30 @@
+version: '3.7'
+services:
+  hyperf:
+    image: $REGISTRY_URL/$PROJECT_NAME:test
+    environment:
+      - "APP_PROJECT=hyperf"
+      - "APP_ENV=test"
+    ports:
+      - 9501:9501
+    deploy:
+      replicas: 1
+      restart_policy:
+        condition: on-failure
+        delay: 5s
+        max_attempts: 5
+      update_config:
+        parallelism: 2
+        delay: 5s
+        order: start-first
+    networks:
+      - hyperf_net
+    configs:
+      - source: hyperf_v1.0
+        target: /opt/www/.env
+configs:
+  hyperf_v1.0:
+    external: true
+networks:
+  hyperf_net:
+    external: true

+ 10 - 0
phpstan.neon

@@ -0,0 +1,10 @@
+# Magic behaviour with __get, __set, __call and __callStatic is not exactly static analyser-friendly :)
+# Fortunately, You can ingore it by the following config.
+#
+# vendor/bin/phpstan analyse app --memory-limit 200M -l 0
+#
+parameters:
+  reportUnmatchedIgnoredErrors: false
+  ignoreErrors:
+    - '#Static call to instance method Hyperf\\HttpServer\\Router\\Router::[a-zA-Z0-9\\_]+\(\)#'
+    - '#Static call to instance method Hyperf\\DbConnection\\Db::[a-zA-Z0-9\\_]+\(\)#'

+ 21 - 0
phpunit.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         bootstrap="./test/bootstrap.php"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false">
+    <testsuites>
+        <testsuite name="Tests">
+            <directory suffix="Test.php">./test</directory>
+        </testsuite>
+    </testsuites>
+    <filter>
+        <whitelist processUncoveredFilesFromWhitelist="true">
+            <directory suffix=".php">./app</directory>
+        </whitelist>
+    </filter>
+</phpunit>

+ 27 - 0
test/Cases/ExampleTest.php

@@ -0,0 +1,27 @@
+<?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\Cases;
+
+use HyperfTest\HttpTestCase;
+
+/**
+ * @internal
+ * @coversNothing
+ */
+class ExampleTest extends HttpTestCase
+{
+    public function testExample()
+    {
+        $this->assertTrue(true);
+        $this->assertTrue(is_array($this->get('/')));
+    }
+}

+ 42 - 0
test/HttpTestCase.php

@@ -0,0 +1,42 @@
+<?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;
+
+use Hyperf\Testing\Client;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Class HttpTestCase.
+ * @method get($uri, $data = [], $headers = [])
+ * @method post($uri, $data = [], $headers = [])
+ * @method json($uri, $data = [], $headers = [])
+ * @method file($uri, $data = [], $headers = [])
+ * @method request($method, $path, $options = [])
+ */
+abstract class HttpTestCase extends TestCase
+{
+    /**
+     * @var Client
+     */
+    protected $client;
+
+    public function __construct($name = null, array $data = [], $dataName = '')
+    {
+        parent::__construct($name, $data, $dataName);
+        $this->client = make(Client::class);
+    }
+
+    public function __call($name, $arguments)
+    {
+        return $this->client->{$name}(...$arguments);
+    }
+}

+ 29 - 0
test/bootstrap.php

@@ -0,0 +1,29 @@
+<?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
+ */
+ini_set('display_errors', 'on');
+ini_set('display_startup_errors', 'on');
+
+error_reporting(E_ALL);
+date_default_timezone_set('Asia/Shanghai');
+
+! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
+! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);
+
+Swoole\Runtime::enableCoroutine(true);
+
+require BASE_PATH . '/vendor/autoload.php';
+
+Hyperf\Di\ClassLoader::init();
+
+$container = require BASE_PATH . '/config/container.php';
+
+$container->get(Hyperf\Contract\ApplicationInterface::class);

+ 0 - 0
新建文本文档.txt