# 快速入門 為了讓您更快的瞭解 `Hyperf` 的使用,本章節將以 `創建一個 HTTP Server` 為例,通過對路由、控制器的定義實現一個簡單的 `Web` 服務,但 `Hyperf` 不止於此,完善的服務治理、`gRPC` 服務、註解、`AOP` 等功能將由具體的章節闡述。 ## 定義訪問路由 Hyperf 使用 [nikic/fast-route](https://github.com/nikic/FastRoute) 作為默認的路由組件並提供服務,您可以很方便的在 `config/routes.php` 中定義您的路由。 不僅如此,框架還提供了極其強大和方便靈活的 `註解路由` 功能,關於路由的詳情文檔請查閲 [路由](zh-hk/router.md) 章節 ### 通過配置文件定義路由 路由的文件位於 [hyperf-skeleton](https://github.com/hyperf/hyperf-skeleton) 項目的 `config/routes.php` ,下面是一些常用的用法示例。 ```php 使用 `@AutoController` 註解時需 `use Hyperf\HttpServer\Annotation\AutoController;` 命名空間; 駝峯命名的控制器,會自動轉化為蛇形路由,以下為控制器與實際路由的對應關係示例: | 控制器 | 註解 | 訪問路由 | | :--------------: | :-----------------------------: | :------------: | | MyDataController | @AutoController() | /my_data/index | | MydataController | @AutoController() | /mydata/index | | MyDataController | @AutoController(prefix="/data") | /data/index | ```php input('id', 1); return (string)$id; } } ``` ### 通過 `@Controller` 註解定義路由 `@Controller` 為滿足更細緻的路由定義需求而存在,使用 `@Controller` 註解用於表明當前類為一個 `Controller 類`,同時需配合 `@RequestMapping` 註解來對請求方法和請求路徑進行更詳細的定義。 我們也提供了多種快速便捷的 `Mapping 註解`,如 `@GetMapping`、`@PostMapping`、`@PutMapping`、`@PatchMapping`、`@DeleteMapping` 5 種便捷的註解用於表明允許不同的請求方法。 > 使用 `@Controller` 註解時需 `use Hyperf\HttpServer\Annotation\Controller;` 命名空間; > 使用 `@RequestMapping` 註解時需 `use Hyperf\HttpServer\Annotation\RequestMapping;` 命名空間; > 使用 `@GetMapping` 註解時需 `use Hyperf\HttpServer\Annotation\GetMapping;` 命名空間; > 使用 `@PostMapping` 註解時需 `use Hyperf\HttpServer\Annotation\PostMapping;` 命名空間; > 使用 `@PutMapping` 註解時需 `use Hyperf\HttpServer\Annotation\PutMapping;` 命名空間; > 使用 `@PatchMapping` 註解時需 `use Hyperf\HttpServer\Annotation\PatchMapping;` 命名空間; > 使用 `@DeleteMapping` 註解時需 `use Hyperf\HttpServer\Annotation\DeleteMapping;` 命名空間; ```php input('id', 1); return (string)$id; } } ``` ## 處理 HTTP 請求 `Hyperf` 是完全開放的,本質上沒有規定您必須基於某種模式下去實現請求的處理,您可以採用傳統的 `MVC 模式`,亦可以採用 `RequestHandler 模式` 來進行開發。 我們以 `MVC 模式` 來舉個例子: 在 `app` 文件夾內創建一個 `Controller` 文件夾並創建 `IndexController.php` 如下,`index` 方法內從請求中獲取了 `id` 參數,並轉換為 `字符串` 類型返回到客户端。 ```php input('id', 1); // 轉換 $id 為字符串格式並以 plain/text 的 Content-Type 返回 $id 的值給客户端 return (string)$id; } } ``` ## 依賴自動注入 依賴自動注入是 `Hyperf` 提供的一個非常強大的功能,也是保持框架靈活性的根基。 `Hyperf` 提供了兩種注入方式,一種是大家常見的通過構造函數注入,另一種是通過 `@Inject` 註解注入,下面我們舉個例子並分別以兩種方式展示注入的實現; 假設我們存在一個 `\App\Service\UserService` 類,類中存在一個 `getInfoById(int $id)` 方法通過傳遞一個 `id` 並最終返回一個用户實體,由於返回值並不是我們這裏所需要關注的,所以不做過多闡述,我們要關注的是在任意的類中獲取 `UserService` 並調用裏面的方法,一般的方法是通過 `new UserService()` 來實例化該服務類,但在 `Hyperf` 下,我們有更優的解決方法。 ### 通過構造函數注入 只需在構造函數的參數內聲明參數的類型,`Hyperf` 會自動注入對應的對象或值。 ```php userService = $userService; } // /index/info public function info(RequestInterface $request) { $id = $request->input('id', 1); return $this->userService->getInfoById((int)$id); } } ``` ### 通過 `@Inject` 註解注入 只需對對應的類屬性通過 `@var` 聲明參數的類型,並使用 `@Inject` 註解標記屬性 ,`Hyperf` 會自動注入對應的對象或值。 > 使用 `@Inject` 註解時需 `use Hyperf\Di\Annotation\Inject;` 命名空間; ```php input('id', 1); return $this->userService->getInfoById((int)$id); } } ``` 通過上面的示例我們不難發現 `$userService` 在沒有實例化的情況下, 屬性對應的類對象被自動注入了。 不過這裏的案例並未真正體現出依賴自動注入的好處及其強大之處,我們假設一下 `UserService` 也存在很多的依賴,而這些依賴同時又存在很多其它的依賴時,`new` 實例化的方式就需要手動實例化很多的對象並調整好對應的參數位,而在 `Hyperf` 裏我們就無須手動管理這些依賴,只需要聲明一下最終使用的類即可。 而當 `UserService` 需要發生替換等劇烈的內部變化時,比如從一個本地服務替換成了一個 RPC 遠程服務,也只需要通過配置調整依賴中 `UserService` 這個鍵值對應的類為新的 RPC 服務類即可。 ## 啟動 Hyperf 服務 由於 `Hyperf` 內置了協程服務器,也就意味着 `Hyperf` 將以 `CLI` 的形式去運行,所以在定義好路由及實際的邏輯代碼之後,我們需要在項目根目錄並通過命令行運行 `php bin/hyperf.php start` 來啟動服務。 當 `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` 終止服務,再重新執行啟動命令 `php bin/hyperf.php start` 完成啟動和重新加載。 > Tips: 您也可以將啟動 Server 的命令配置在 IDE 上,便可直接通過 IDE 的 `啟動/停止` 操作快捷的完成 `啟動服務` 或 `重啟服務` 的操作。 > 且非視圖開發時可以採用 [TDD(Test-Driven Development)](https://baike.baidu.com/item/TDD/9064369) 測試驅動開發來進行開發,這樣不僅可以省略掉服務重啟和頻繁切換窗口的麻煩,還可保證接口數據的正確性。 > 另外,在文檔 [協程組件庫](zh-hk/awesome-components?id=%e7%83%ad%e6%9b%b4%e6%96%b0%e7%83%ad%e9%87%8d%e8%bd%bd) 一章中提供了多種由社區開發者支持的 熱更新/熱重載 的解決方案,如仍希望採用 熱更新/熱重載 方案可再深入瞭解。 ## 多端口監聽 `Hyperf` 支持監聽多個端口,但因為 `callbacks` 中的對象直接從容器中獲取,所以相同的 `Hyperf\HttpServer\Server::class` 會在容器中被覆蓋。所以我們需要在依賴關係中,重新定義 `Server`,確保對象隔離。 > WebSocket 和 TCP 等 Server 同理。 `config/autoload/dependencies.php` ```php Hyperf\HttpServer\Server::class, ]; ``` `config/autoload/server.php` ```php [ [ 'name' => 'http', 'type' => Server::SERVER_HTTP, 'host' => '0.0.0.0', 'port' => 9501, 'sock_type' => SWOOLE_SOCK_TCP, 'callbacks' => [ Event::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'], ], ], [ 'name' => 'innerHttp', 'type' => Server::SERVER_HTTP, 'host' => '0.0.0.0', 'port' => 9502, 'sock_type' => SWOOLE_SOCK_TCP, 'callbacks' => [ Event::ON_REQUEST => ['InnerHttp', 'onRequest'], ], ], ] ]; ``` 同時 `路由文件`,或者 `註解` 也需要指定對應的 `server`,如下: - 路由文件 `config/routes.php` ```php