上手,打統統用功能使用障礙,swoole相關錯誤調試。php
《黃朝暉:Hyperf從入門到精通系列》html
__invoke(): 類被函數式調用時觸發執行node
普通函數請求和響應對象:git
public function index(RequestInterface $request, ResponseInterface $response){}
實際實例化的對象:github
HyperfHttpServerRequest
-- Context::get(ServerRequestInterface::class)
這些對象存於協程上下文資源,即時釋放。注意公共對象資源的修改!數據庫
@AutoController 的路由信息注入 @inject 的簡單對象注入 [ == 經過構造方法注入]
# 抽象對象注入 因爲 Hyperf\HttpServer\ConfigProvider.dependencies [ RequestInterface::class => Request::class ResponseInterface::class => Response::class ] 在控制器接收方法裏,直接使用抽象對象即依賴自實例對象
AOP(Aspect Oriented Programming):面向切片編程編程
App\Annotation\FooAnnotation.php /** * @Annotation * @Target({"CLASS","METHOD","PROPERTY"}) */ class FooAnnotation extends AbstractAnnotation{} -- 添加到註解樹 @Annotation -把類路徑、方法、屬性添加到註解樹 註解是添做使用類(當前類)的一部分
App\Aspect\FooSpect.php /** * @Aspect */ class FooSpect extends AbstractAspect{ //定義切入類 public $classes = []; /** 註解引入、重寫切片「環繞」處理方法 * @param ProceedingJoinPoint $proceedingJoinPoint * @return mixed|void */ public function process(ProceedingJoinPoint $proceedingJoinPoint){} } -- 添加切片類、方法 [註解方式] 到切面樹 -重寫定義切入類 $classes -ProceedingJoinPoint $proceedingJoinPoint
App\Controller\FooController.php /** * @AutoController() * @FooAnnotation(bar="123", calc=11) */ class FooController{} --修改註解屬性 @FooAnnotation(bar="123", calc=11) curl -v http://127.0.0.1:9501/foo/index 或 test
震驚:parallel():2個閉包函數的協程!!json
namespace App\Controller; use Hyperf\Di\Annotation\Inject; use Hyperf\Guzzle\ClientFactory; use Hyperf\HttpServer\Annotation\AutoController; use Hyperf\HttpServer\Contract\RequestInterface; class CoroutineController { /** * @Inject() * @var ClientFactory */ private $clientFactory; public function sleep(RequestInterface $request) { $sec = $request->query('second',1); sleep($sec); return $sec; } /** Parallel 特性 * 便捷版 WaitGroup * @return array */ public function testCo33() { $time = (float) time() + floatval(microtime()); $result = parallel([ function () { $client = $this->clientFactory->create(); $client->get('127.0.0.1:9501/Coroutine/sleep?second=1'); return '123: '. \Hyperf\Utils\Coroutine::id(); }, function () { $client = $this->clientFactory->create(); $client->get('127.0.0.1:9501/Coroutine/sleep?second=2'); return '321: '. \Hyperf\Utils\Coroutine::id(); } ]); return [__FUNCTION__.' ok: '. round((float) time() + floatval(microtime()) - $time,4), $result]; } /** Concurrent 協程運行控制 * 高級控制版 WaitGroup * @return array */ public function testCo9() { $time = (float) time() + floatval(microtime()); $concurrent = new \Hyperf\Utils\Coroutine\Concurrent(5); $result = []; for ($i = 0; $i < 15; ++$i) { $concurrent->create(function () use ($i, &$result) { $client = $this->clientFactory->create(); $client->get('127.0.0.1:9501/Coroutine/sleep?second='. ($i%5+1)); $result[] = [$i. ': '. \Hyperf\Utils\Coroutine::id()]; return 1; }); } //因爲並行機制 句柄移交的緣由, 這裏result結果輸出數是 15-5=12 個 return [__FUNCTION__.' ok: '. round((float) time() + floatval(microtime()) - $time,4), $result]; } }
本文實例:CoroutineController.phpswoole
與切片同理,閉包
/* 添加中間件多個、一個 * @Middlewares({ * @Middleware(Test1MIddleware::class), * @Middleware(Test2Middleware::class) * }) */ class MidController{}
修改傳值注意:保存到 Context會話對象
。
Hyperf\Utils\Context::set(ServerRequestInterface::class, $request->withAttribute()...) App\Controller\MidController.php /** curl -v http://127.0.0.1:9501/mid/index * b. 方法中間件 * @Middleware(FooMiddleware::class) */ public function index(RequestInterface $request){} App\Middleware\FooMiddleware.php class FooMiddleware implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { // $request 和 $response 爲修改後的對象 $request = Context::set(ServerRequestInterface::class, $request->withAttribute('foo', 'test2 set into value a...')); $response = $handler->handle($request); $body = $response->getBody()->getContents(); echo __CLASS__ .__LINE__.PHP_EOL; return $response->withBody(new SwooleStream($body. PHP_EOL. ' in func see Foo deal.')); } }
執行順序是:先入、先執行、後出。
composer create-project hyperf/hyperf-skeleton rpc-server cp rpc-server rpc-client -r
[文件夾 rpc-server]
App\Rpc\CalculatorService.php: use Hyperf\RpcServer\Annotation\RpcService; /** * @RpcService(name="CalculatorService", protocol="jsonrpc-http", server="rpc", publishTo="consul") */ class CalculatorService implements CalculatorServiceInterface { public function add(int $a, int $b): int { return $a + $b; } public function minus(int $a, int $b): int { return $a - $b; } }
@RpcService 引用RpcService類
這裏protocol="jsonrpc-http/tcp", server配置在config/autoload/server.php的servers中,publishTo="consul"註冊到consul。
在編輯器中右鍵,Refactor導出接口CalculatorServiceInterface.php[本例到相同目錄]。
server.php: "servers"=> #添加或替換 [ 'name' => 'rpc', //與CalculatorService.php:server="rpc"相同 'type' => Server::SERVER_HTTP, 'host' => '0.0.0.0', 'port' => 9600, 'sock_type' => SWOOLE_SOCK_TCP, 'callbacks' => [ SwooleEvent::ON_REQUEST => [\Hyperf\JsonRpc\HttpServer::class, 'onRequest'], ], ],
添加依賴後,接口的對象自動實例化
config/autoload/dependencies.php: /** * 接口的實體化依賴 */ App\Rpc\CalculatorServiceInterface::class => App\Rpc\CalculatorService::class
[文件夾 rpc-client]
接口文件一樣放到 appRpc 下
class CalcController extends AbstractController { /** * @Inject() * @var CalculatorServiceInterface */ private $calcService; public function add(){ return $this->calcService->add(12, 56); } public function minus(){ return $this->calcService->minus(23, 78); } }
添加 config/autoload/services.php 參考官方添加:
'consumers'=>[ 'name' => 'CalculatorService', 'service' => \App\Rpc\CalculatorServiceInterface::class, 'registry' => [ 'protocol' => 'consul', 'address' => 'http://172.10.1.11:8500', //服務羣端主節點 ], 'nodes' => [ ['host' => '172.10.1.22', 'port' => 8500], //備用:客戶羣羣端節點 ], ]
php rpc-server/bin/hyperf.php start [9600,發佈到8500] php rpc-client/bin/hyperf.php start [9501,註冊到8500] curl -v http://127.0.0.1:9501/calc/add //和minus
命令行生成模板文件:
php bin/hyperf.php list php bin/hyperf.php gen:SendSmsListener
/** * @Inject() * @var EventDispatcherInterface */ private $eventDispatcher; //引入事件監聽分發類 //用戶註冊以前 $beforeRegister = new BeforeRegister(); $this->eventDispatcher->dispatch($beforeRegister); if($beforeRegister->shouldRegister){ //註冊用戶 $userId = rand(1,99999); } //註冊成功後 if($userId){ $this->eventDispatcher->dispatch(new UserRegistered($userId)); }
Listener:
/** 權重默認是1 * @Listener(priority=2) */ class SendSmsListener implements ListenerInterface { public function listen(): array { return [ UserRegistered::class ]; } /** * @param UserRegistered $event */ public function process(object $event) { echo '發送短信給'. $event->userId .PHP_EOL; } } class VaildRegisterListener implements ListenerInterface { public function listen(): array { return [ BeforeRegister::class ]; } /** * @param BeforeRegister $event */ public function process(object $event) { $event->shouldRegister = (bool) rand(0,2); echo '註冊身份驗證'. ($event->shouldRegister ? '經過' : '失敗') .PHP_EOL; } }
Event:
class UserRegistered { public $userId; public function __construct(int $userId) { $this->userId = $userId; } } class BeforeRegister { public $shouldRegister = false; }
Controller:
class ListenController { /** * @Inject() * @var UserService */ public $userService; public function test() { return $this->userService->register(); } }
實例代碼上傳:
https://github.com/cffycls/cl...
文件上傳、接收,[這裏文件類型判斷須要啓用fileinfo擴展]:
客戶端 $body = [ 'multipart' => [ [ 'name' => 'data', 'contents' => '{"field_1":"Test","field_2":"Test","field_3":"Test"}', 'headers' => [ 'Content-Type' => 'application/json', ], ], [ 'name' => 'file', 'filename' => 'README.md', 'Mime-Type' => 'application/text', 'contents' => file_get_contents('./README.md'), ] ] ]; $res = (new GuzzleHttp\Client())->request('POST', 'http://127.0.0.1:9501/guzzle_client/write', $body); 服務端: $files = $this->request->getUploadedFiles(); //var_dump($files); foreach ($files as $f => $fileObj){ //2者等效,同一對象 $file = $this->request->file($f); var_dump($file->getMimeType()); $fileInfo = $file->toArray(); var_dump($fileInfo); echo '如下是接收的文件內容: '. PHP_EOL; var_dump( file_get_contents($fileInfo['tmp_file']) ); if(file_exists('/tmp/README.md.tmp')){ echo '文件已存在: '. PHP_EOL; }else{ $file->moveTo('/tmp/README.md.tmp'); //保存文件 // 經過 isMoved(): bool 方法判斷方法是否已移動 if ($file->isMoved()) { echo $fileInfo['name'] .'文件已上傳 '. PHP_EOL; unlink('/tmp/README.md.tmp'); return $fileInfo; } } }
路由,事件,日誌,命令,數據庫,依賴注入容器,服務,客戶端,消息隊列,配置中心,RPC,服務治理,定時任務,ID 生成器,文檔生成,Graphql,熱更新/熱重載,Swoole,開發調試,權限認證,第三方 SDK
移步官網 組件列表