Skip to content

Commit 0ef9c27

Browse files
authored
Merge pull request #7 from SDPM-lab/work_1107
Work _1107
2 parents 1a1033a + 7616673 commit 0ef9c27

15 files changed

Lines changed: 530 additions & 43 deletions

README_zh-TW.md

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
# Codeigniter4-Roadrunner
2+
3+
<p align="center">
4+
<img src="https://i.imgur.com/sCjeSTo.png" alt="logo" width="500" />
5+
</p>
6+
7+
[![Latest Stable Version](https://poser.pugx.org/sdpmlab/codeigniter4-roadrunner/v)](//packagist.org/packages/sdpmlab/codeigniter4-roadrunner) [![Total Downloads](https://poser.pugx.org/sdpmlab/codeigniter4-roadrunner/downloads)](//packagist.org/packages/sdpmlab/codeigniter4-roadrunner) [![Latest Unstable Version](https://poser.pugx.org/sdpmlab/codeigniter4-roadrunner/v/unstable)](//packagist.org/packages/sdpmlab/codeigniter4-roadrunner) [![License](https://poser.pugx.org/sdpmlab/codeigniter4-roadrunner/license)](//packagist.org/packages/sdpmlab/codeigniter4-roadrunner)
8+
9+
Codeigniter4-RoadRunner 提供的是 Roadrunner-Worker 與 Codeigniter4 在 Request 與 Response 物件上的同步,因為 Codeigniter4 並沒有完整實作 [PSR-7 規範](https://codeigniter.tw/user_guide/intro/psr.html)。所以你需要使用這個程式庫才能讓你的 Codeigniter4 專案透過 RoadRunner Server 運作。
10+
11+
> 這個程式庫目前處於開發版,功能尚未穩定。請不要將它運用在正式環境中。
12+
13+
## 安裝
14+
15+
### 需求
16+
1. CodeIgniter Framework 4.*
17+
2. Composer
18+
3. 安裝並開啟 php-curl 擴充套件
19+
4. 安裝並開啟 php-zip 擴充套件
20+
21+
### Composer 安裝
22+
23+
於專案根目錄下,使用 Composer 下載程式庫與其所需之依賴。
24+
25+
```
26+
composer require sdpmlab/codeigniter4-roadrunner "v1.0.0-beta.2"
27+
```
28+
29+
使用程式庫提供的內建指令初始化 Roadrunner 與其所需的檔案。
30+
31+
```
32+
php spark ciroad:init
33+
```
34+
35+
## 運作伺服器
36+
37+
你可以選擇使用不同的指令在專案根目錄中運作 RoadRunner Server:
38+
39+
1. 使用 Codeigniter4 spark 指令
40+
```
41+
php spark ciroad:start -v -d
42+
```
43+
2. 在 Windows-cmd 中直使用 RoadRunner 指令
44+
```
45+
rr.exe serve -v -d
46+
```
47+
3. 在 MacOS/Linux 中直接使用 RoadRunner 指令
48+
```
49+
./rr serve -v -d
50+
```
51+
52+
## 伺服器組態設定
53+
54+
伺服器組態設定應置於專案根目錄下,並命名為 `.rr.yaml` 。程式庫初始化後產出的預設檔案看起來會像這樣子:
55+
56+
```yaml
57+
http:
58+
address: 0.0.0.0:8080
59+
workers:
60+
command: "php psr-worker.php"
61+
# pool:
62+
# numWorkers: 50
63+
# maxJobs: 10
64+
65+
static:
66+
enable: true
67+
dir: "public"
68+
forbid: [".php", ".htaccess"]
69+
```
70+
71+
當然,你可以參考 [Roadrunner 手冊](https://roadrunner.dev/docs/intro-config) 建立符合專案需求的組態設定檔。
72+
73+
## 開發建議
74+
75+
### 自動重新載入
76+
77+
RoadRunner 預設的情況下,必須在每次修改 php 檔案後重啟伺服器,你所做的修改才會生效,這在開發上似乎不那麼友善。
78+
79+
你可以修改你的 `.rr.yaml` 組態設定檔案,加入以下設定後以 `-v -d` 開發模式啟動 RoadRunner Server,它將會自動偵測 PHP 檔案是否修改,並即時重新載入 Worker 。
80+
81+
```yaml
82+
# reload can reset rr servers when files change
83+
reload:
84+
# refresh interval (default 1s)
85+
interval: 1s
86+
# file extensions to watch, defaults to [.php]
87+
patterns: [".php"]
88+
```
89+
90+
`reload` 是非常耗費資源的,請不要在正式環境中打開這個選項。
91+
92+
### 使用 Codeigniter4 Request 與 Response 物件
93+
94+
Codeigniter4 並沒有實作完整的 [HTTP message 介面](https://www.php-fig.org/psr/psr-7/),所以這個程式庫著重於 `PSR-7 介面` 與 `Codeigniter4 HTTP 介面` 的同步。
95+
96+
基於上述原因,在開發上,你應該使用 Codeigniter4 所提供的 `$this->request` 或是使用全域函數 `\Config\Services::('request')` 取得正確的 request 物件;使用 `$this->response` 或是 `\Config\Services::('response')` 取得正確的 response 物件。
97+
98+
請注意,在建構給予使用者的響應時,不論是 `header` 或 `set-cookies` 應該避免使用 PHP 內建的方法進行設定。而是使用 [Codeigniter4 響應物件](https://codeigniter.tw/user_guide/outgoing/response.html) 提供的 `setHeader()` 與 `setCookie()` 進行設定。
99+
100+
### 以 return 結束控制器邏輯
101+
102+
在 Controller 中,盡量使用 return 結束程式邏輯,不論是視圖的響應或是 API 響應,減少使用 `echo` 輸出內容可以避免很多錯誤,就像這個樣子。
103+
104+
```php
105+
<?php namespace App\Controllers;
106+
107+
use CodeIgniter\API\ResponseTrait;
108+
109+
class Home extends BaseController
110+
{
111+
use ResponseTrait;
112+
113+
public function index()
114+
{
115+
//Don't use :
116+
//echo view('welcome_message');
117+
return view('welcome_message');
118+
}
119+
120+
/**
121+
* send header
122+
*/
123+
public function sendHeader()
124+
{
125+
$this->response->setHeader("X-Set-Auth-Token",uniqid());
126+
return $this->respond(["status"=>true]);
127+
}
128+
129+
}
130+
```
131+
132+
### 使用內建 Session 程式庫
133+
134+
我們只針對 Codeigniter4 內建 [Session 程式庫](https://codeigniter.tw/user_guide/libraries/sessions.html) 進行支援,並不保證使用 `session_start()` 與 `$_SESSION` 是否能照常運作。所以,你應該避免使用 PHP 內建的 Session 方法,而是以 Codeigniter4 框架內建的程式庫為主。
135+
136+
### 在只有一個 Worker 的環境中開發與除錯
137+
138+
因為 RoadRunner 與其他伺服器軟體(Nginx、Apache)有著根本上的不同,每個 Codeigniter4 將會以 Worker 的形式持久化於記憶體中,HTTP 的請求會重複利用到這些 Worker 進行處裡。所以,我們最好在只有單個 Worker 的情況下開發軟體並測試是否穩定,以證明在多個 Woker 的實際環境中能正常運作。
139+
140+
你可以參考以下 `.rr.yaml` 設定將 Worker 的數量降到最低:
141+
142+
```yaml
143+
http:
144+
address: 0.0.0.0:8080
145+
workers:
146+
command: "php psr-worker.php"
147+
pool:
148+
numWorkers: 1
149+
maxJobs: 1
150+
```
151+
152+
## 全域方法
153+
154+
我們提供了一些全域方法,幫助你更加順利地開發你的專案。
155+
156+
### 處裡檔案上傳
157+
158+
因為 RoadRunner Worker 無法傳遞正確的 `$_FILES` 內容,所以 Codeingiter4 的 [檔案上傳類別](https://codeigniter.tw/user_guide/libraries/uploaded_files.html) 將無法正確運作。對此,我們提供了符合 PSR-7 規範的檔案上傳類別,讓你可以正確地在 RoadRunner 中處理檔案上傳。就算你將專案切換到了其他伺服器環境(spark serve、Apache、Nginx)運作,這個類別依舊可以正常使用,並且不需要修改任何程式碼。
159+
160+
你可以在控制器(或任何地方),以 `SDPMlab\Ci4Roadrunner\UploadedFileBridge::getPsr7UploadedFiles()` 取得使用者上傳的檔案。這個方法將回傳以 Uploaded File 物件組成的陣列。此物件可用的方法與 [PSR-7 Uploaded File Interface](https://www.php-fig.org/psr/psr-7/#36-psrhttpmessageuploadedfileinterface) 中規範的一樣。
161+
162+
```php
163+
<?php namespace App\Controllers;
164+
165+
use CodeIgniter\API\ResponseTrait;
166+
use SDPMlab\Ci4Roadrunner\UploadedFileBridge;
167+
168+
class FileUploadTest extends BaseController
169+
{
170+
use ResponseTrait;
171+
172+
protected $format = "json";
173+
/**
174+
* form-data
175+
*/
176+
public function fileUpload(){
177+
$files = UploadedFileBridge::getPsr7UploadedFiles();
178+
$data = [];
179+
foreach ($files as $file) {
180+
$fileEx = array_pop(
181+
explode('.', $file->getClientFilename())
182+
);
183+
$newFileName = uniqid(rand()).".".$fileEx;
184+
$newFilePath = WRITEPATH.'uploads'.DIRECTORY_SEPARATOR.$newFileName;
185+
$file->moveTo($newFilePath);
186+
$data[$file->getClientFilename()] = md5_file($newFilePath);
187+
}
188+
return $this->respondCreated($data);
189+
}
190+
191+
/**
192+
* form-data multiple upload
193+
*/
194+
public function fileMultipleUpload(){
195+
$files = UploadedFileBridge::getPsr7UploadedFiles()["data"];
196+
$data = [];
197+
foreach ($files as $file) {
198+
$fileEx = array_pop(
199+
explode('.', $file->getClientFilename())
200+
);
201+
$newFileName = uniqid(rand()).".".$fileEx;
202+
$newFilePath = WRITEPATH.'uploads'.DIRECTORY_SEPARATOR.$newFileName;
203+
$file->moveTo($newFilePath);
204+
$data[$file->getClientFilename()] = md5_file($newFilePath);
205+
}
206+
return $this->respondCreated($data);
207+
}
208+
```
209+
210+
### 處理錯誤拋出
211+
212+
如果你在 `-v -d` 開發模式中碰到了一些需要確認的變數、或物件內容,無論在程式的何處,你都可以使用全域函數 `dump()` 來將錯誤拋出到終端機上。
213+
214+
```php
215+
/**
216+
* Dump given value into target output.
217+
*
218+
* @param mixed $value Variable
219+
* @param string $target Possible options: OUTPUT, RETURN, ERROR_LOG, LOGGER.
220+
* @return string|null
221+
*/
222+
function dump($value,string $target = "ERROR_LOG") : ?string;
223+
```
224+
225+
## 可用指令
226+
227+
### ciroad:init
228+
229+
初始化 Roadrunner 與其所需的檔案。
230+
231+
* Use
232+
```
233+
$ php spark ciroad:init
234+
```
235+
236+
### ciroad:start
237+
238+
啟動 RoadRunner 伺服器。
239+
240+
* Use
241+
```
242+
$ php spark ciroad:start [Options]
243+
```
244+
245+
* Options:
246+
```
247+
-d 在除錯模式中執行,HTTP 請求詳情將會羅列在終端機。
248+
-b 在背景中執行。
249+
-v 輸出詳細資料。
250+
```
251+
252+
### ciroad:stop
253+
254+
停止在背景運作的 RoadRunner 伺服器。
255+
256+
* Use
257+
```
258+
$ php spark ciroad:stop
259+
```
260+
261+
### ciroad:reset
262+
263+
強制重新載入所有 HTTP Worker。
264+
265+
* Use
266+
```
267+
$ php spark ciroad:reset
268+
```
269+
270+
### ciroad:status
271+
272+
查閱目前 Worker 運作狀態。
273+
274+
* Use
275+
```
276+
$ php spark ciroad:status [Options]
277+
```
278+
279+
* Options:
280+
```
281+
-i 每秒持續輸出狀態。
282+
```

src/Commands/Reset.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php namespace SDPMlab\Ci4Roadrunner\Commands;
2+
3+
use CodeIgniter\CLI\BaseCommand;
4+
use CodeIgniter\CLI\CLI;
5+
6+
class Reset extends BaseCommand
7+
{
8+
protected $group = 'ciroad';
9+
protected $name = 'ciroad:reset';
10+
protected $description = 'Force RoadRunner service to reload its HTTP workers.';
11+
protected $usage = 'ciroad:reset';
12+
13+
public function run(array $params): void
14+
{
15+
$command = $this->getCommand();
16+
system($command);
17+
}
18+
19+
protected function getCommand(): string
20+
{
21+
$command = "";
22+
if (substr(php_uname(), 0, 7) =="Windows") {
23+
$command .= ROOTPATH."rr.exe http:reset -c ".ROOTPATH.".rr.yaml";
24+
}else{
25+
$command .= "cd ".ROOTPATH.";./rr http:reset";
26+
}
27+
return $command;
28+
}
29+
30+
}
31+
?>

src/Commands/Start.php

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,62 @@ class Start extends BaseCommand
77
{
88
protected $group = 'ciroad';
99
protected $name = 'ciroad:start';
10-
protected $description = 'Strat Roadrunner server.';
10+
protected $description = 'Strat RoadRunner server.';
11+
protected $usage = 'ciroad:start [Options]';
12+
protected $options = [
13+
'-d' => 'Run RoadRunner Server in debug mode to view all incoming requests',
14+
'-b' => 'Run RoadRunner Server in background.',
15+
'-v' => 'Verbose output'
16+
];
1117

12-
public function run(array $params)
18+
public function run(array $params): void
19+
{
20+
$isDev = CLI::getOption("d");
21+
$isBack = CLI::getOption("b");
22+
$isVer = CLI::getOption("v");
23+
24+
$command = $this->getCommand($isDev,$isVer);
25+
$msg = $this->getMessage($isDev,$isBack);
26+
27+
if($isBack){
28+
$this->execInBackground($command);
29+
sleep(1);
30+
CLI::write($msg);
31+
}else{
32+
CLI::write($msg);
33+
exec($command);
34+
}
35+
}
36+
37+
protected function getCommand($isDev,$isVer): string
1338
{
14-
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
15-
$command = "&&rr.exe serve -v -d";
39+
$command = "";
40+
if (substr(php_uname(), 0, 7) =="Windows") {
41+
$command .= ROOTPATH."rr.exe serve -c ".ROOTPATH.".rr.yaml";
1642
}else{
17-
$command = ";./rr serve -v -d";
43+
$command .= "cd ".ROOTPATH.";./rr serve";
44+
}
45+
if($isDev) $command .= " -d";
46+
if($isVer) $command .= " -v";
47+
return $command;
48+
}
49+
50+
protected function getMessage($isDev,$isBack): string
51+
{
52+
$msg = CLI::color("\nCodeigniter4 RoadRunner Server Starting.\n","green");
53+
if($isDev) $msg .= CLI::color("Development mode.\n","yellow");
54+
if($isBack) $msg .= CLI::color("Bbackground mode.\n","yellow");
55+
return $msg;
56+
}
57+
58+
protected function execInBackground($cmd)
59+
{
60+
if (substr(php_uname(), 0, 7) =="Windows"){
61+
pclose(popen("start /B ".$cmd,"r"));
62+
}else {
63+
exec($cmd ."> /dev/null &");
1864
}
19-
CLI::write(
20-
CLI::color("\nCodeigniter4 Roadrunner Server Starting.\n","green")
21-
);
22-
popen("cd ".ROOTPATH.$command, 'r');
2365
}
66+
2467
}
2568
?>

0 commit comments

Comments
 (0)