替換某個類的實現
讓我們實現一個十分簡單的 Service,程式碼如下
<?php
namespace App\Service;
class DemoService
{
public function say(): string
{
return 'I am in ' . static::class;
}
}
然後控制器中呼叫,並返回資料
public function say()
{
return $this->response->success(
$this->container->get(DemoService::class)->say()
);
}
訪問介面,並檢視結果
$ curl http://127.0.0.1:9501/index/say
{"code":0,"data":"I am in App\\Service\\DemoService"}
接下來我們重新寫一個 Demo2Service
<?php
namespace App\Service;
class Demo2Service
{
public function say(): string
{
return 'I am not in ' . DemoService::class;
}
}
然後修改 dependencies.php
配置。
<?php
use App\Service;
return [
Service\DemoService::class => Service\Demo2Service::class,
];
訪問介面,並檢視結果
$ curl http://127.0.0.1:9501/index/say
{"code":0,"data":"I am not in App\\Service\\DemoService"}
可見 DemoService
會被直接替換成 Demo2Service
。
活用 Factory
有時候我們需要使用多個 Middleware
,雖然邏輯大致一樣,但可能還是有一部分差別,這個時候透過上述方式,就顯得有些單薄了,畢竟我需要兩個都要。
讓我們寫一個簡單的 AuthMiddleware
和 UserAuth
。
<?php
namespace App\Middleware;
use App\Service\UserAuth;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class AuthMiddleware implements MiddlewareInterface
{
/**
* @var ContainerInterface
*/
protected $container;
/**
* @var string
*/
protected $pool;
public function __construct(ContainerInterface $container, string $pool)
{
$this->container = $container;
$this->pool = $pool;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
// TODO: 根據 pool 查詢不通的 Redis 等儲存引擎 並驗證許可權
// 將對應的許可權放到 UserAuth 中,這裡模式傳入一個 object,實際可以傳入 Model 等。
UserAuth::instance()->load((object) [
'pool' => $this->pool,
]);
return $handler->handle($request);
}
}
<?php
namespace App\Service;
use Hyperf\Utils\Traits\StaticInstance;
class UserAuth
{
use StaticInstance;
protected $user;
public function load(object $user)
{
$this->user = $user;
}
}
這個時候,如果存在兩個 server,如果只有一份 AuthMiddleware
,顯然是無法滿足的。比如以下配置
<?php
use Hyperf\Server\Server;
use Hyperf\Server\SwooleEvent;
return [
'mode' => SWOOLE_BASE,
'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'],
],
],
[
'name' => 'admin',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9502,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
SwooleEvent::ON_REQUEST => ['AdminServer', 'onRequest'],
],
],
],
'settings' => [
'enable_coroutine' => true,
'worker_num' => 4,
'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,
],
'callbacks' => [
SwooleEvent::ON_BEFORE_START => [Hyperf\Framework\Bootstrap\ServerStartCallback::class, 'beforeStart'],
SwooleEvent::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'],
SwooleEvent::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'],
],
];
這個時候,我們使用 Factory 模式來很方便的將一份 AuthMiddleware 變成兩份。
讓我們建立兩個 Factory
。
namespace App\Middleware\Factory;
use App\Middleware\AuthMiddleware;
use Psr\Container\ContainerInterface;
class AuthMiddlewareFactory
{
public function __invoke(ContainerInterface $container)
{
return new AuthMiddleware($container, 'user');
}
}
class AdminAuthMiddlewareFactory
{
public function __invoke(ContainerInterface $container)
{
return new AuthMiddleware($container, 'admin');
}
}
然後配置 dependencies.php 配置
<?php
use App\Middleware;
use App\Service;
return [
// Hyperf\Contract\StdoutLoggerInterface::class => App\Kernel\Log\LoggerFactory::class,
Service\DemoService::class => Service\Demo2Service::class,
'AdminServer' => Hyperf\HttpServer\Server::class,
'UserAuthMiddleware' => Middleware\Factory\AuthMiddlewareFactory::class,
'AdminUserAuthMiddleware' => Middleware\Factory\AdminAuthMiddlewareFactory::class,
];
然後修改 middlewares.php
配置
<?php
return [
'http' => [
'UserAuthMiddleware',
],
'admin' => [
'AdminUserAuthMiddleware',
],
];
接下來修改我們的介面。
public function pool()
{
$user = UserAuth::instance()->getUser();
return $this->response->success($user->pool);
}
測試
$ curl http://127.0.0.1:9501/index/pool
{"code":0,"data":"user"}
$ curl http://127.0.0.1:9502/index/pool
{"code":0,"data":"admin"}
當然,如果覺得寫兩個 Factory 比較繁瑣,也可以使用更加方便的匿名函式
修改我們的 dependencies.php 配置如下
<?php
use App\Middleware;
use App\Service;
return [
// Hyperf\Contract\StdoutLoggerInterface::class => App\Kernel\Log\LoggerFactory::class,
Service\DemoService::class => Service\Demo2Service::class,
'AdminServer' => Hyperf\HttpServer\Server::class,
// 'UserAuthMiddleware' => Middleware\Factory\AuthMiddlewareFactory::class,
// 'AdminUserAuthMiddleware' => Middleware\Factory\AdminAuthMiddlewareFactory::class,
'UserAuthMiddleware' => function () {
return make(Middleware\AuthMiddleware::class, ['pool' => 'user']);
},
'AdminUserAuthMiddleware' => function () {
return make(Middleware\AuthMiddleware::class, ['pool' => 'admin']);
},
];
高階用法
接下來我們透過以上方式,修改一下框架裡的類,這樣可以在不修改原始碼的情況下,進行某些類的替換
比如,讓我們修改一下 404 的返回。
<?php
namespace App\Kernel\Http;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer;
use Psr\Http\Message\ServerRequestInterface;
class CoreMiddleware extends HttpServer\CoreMiddleware
{
protected function handleNotFound(ServerRequestInterface $request)
{
return $this->response()->withStatus(404)->withBody(new SwooleStream('Not Found.'));
}
}
首先,在不替換的情況下,讓我們訪問一個不存在的路由
$ curl http://127.0.0.1:9501/index/not-found -i
HTTP/1.1 404 Not Found
Server: Hyperf
Connection: keep-alive
Content-Type: text/html
Date: Sun, 19 Jan 2020 06:53:36 GMT
Content-Length: 0
接下來我們修改 dependencies.php
配置
<?php
use App\Kernel\Http;
use App\Middleware;
use App\Service;
return [
// Hyperf\Contract\StdoutLoggerInterface::class => App\Kernel\Log\LoggerFactory::class,
Service\DemoService::class => Service\Demo2Service::class,
'AdminServer' => Hyperf\HttpServer\Server::class,
// 'UserAuthMiddleware' => Middleware\Factory\AuthMiddlewareFactory::class,
// 'AdminUserAuthMiddleware' => Middleware\Factory\AdminAuthMiddlewareFactory::class,
'UserAuthMiddleware' => function () {
return make(Middleware\AuthMiddleware::class, ['pool' => 'user']);
},
'AdminUserAuthMiddleware' => function () {
return make(Middleware\AuthMiddleware::class, ['pool' => 'admin']);
},
Hyperf\HttpServer\CoreMiddleware::class => Http\CoreMiddleware::class,
];
然後再讓我們訪問試一下
$ curl http://127.0.0.1:9501/index/not-found -i
HTTP/1.1 404 Not Found
Server: Hyperf
Connection: keep-alive
Content-Type: text/html
Date: Sun, 19 Jan 2020 06:54:19 GMT
Content-Length: 10
Not Found.
可以看到,我們已經成功替換了對應的 CoreMiddleware
。
寫在最後
Hyperf 是基於 Swoole 4.4+ 實現的高效能、高靈活性的 PHP 協程框架,內建協程伺服器及大量常用的元件,效能較傳統基於 PHP-FPM 的框架有質的提升,提供超高效能的同時,也保持著極其靈活的可擴充套件性,標準元件均基於 PSR 標準 實現,基於強大的依賴注入設計,保證了絕大部分元件或類都是 可替換 與 可複用 的。
框架元件庫除了常見的協程版的 MySQL 客戶端、Redis 客戶端,還為您準備了協程版的 Eloquent ORM、WebSocket 服務端及客戶端、JSON RPC 服務端及客戶端、GRPC 服務端及客戶端、Zipkin/Jaeger (OpenTracing) 客戶端、Guzzle HTTP 客戶端、Elasticsearch 客戶端、Consul 客戶端、ETCD 客戶端、AMQP 元件、Apollo 配置中心、阿里雲 ACM 應用配置管理、ETCD 配置中心、基於令牌桶演算法的限流器、通用連線池、熔斷器、Swagger 文件生成、Swoole Tracker、Blade 和 Smarty 檢視引擎、Snowflake 全域性ID生成器 等元件,省去了自己實現對應協程版本的麻煩。
Hyperf 還提供了 基於 PSR-11 的依賴注入容器、註解、AOP 面向切面程式設計、基於 PSR-15 的中介軟體、自定義程式、基於 PSR-14 的事件管理器、Redis/RabbitMQ 訊息佇列、自動模型快取、基於 PSR-16 的快取、Crontab 秒級定時任務、Translation 國際化、Validation 驗證器 等非常便捷的功能,滿足豐富的技術場景和業務場景,開箱即用。
本作品採用《CC 協議》,轉載必須註明作者和本文連結