PHP非同步程式設計: 基於 PHP 實(chao)現(xi) NODEJS web框架 KOA

naka1205發表於2018-12-25

PHP非同步程式設計: 基於 PHP 實(chao)現(xi) NODEJS web框架KOA

說明

偶然間在 GITHUB 上看到有贊官方倉庫的 手把手教你實現co與Koa 。由於此前用過 KOA ,對於 KOA 的洋蔥模型歎為觀止。不由得心血來潮的看完了整個文件,接著 CTRL+CCTRL+V 讓程式碼跑了起來。 文件中是基於 swoole 擴充套件進行開發,而 swooleWINDOWS 並不友好,向來習慣在 WINDOWS 下開發的我一鼓作氣,將Workerman 改寫併相容了此專案。

體驗

PHPKoa Demo 是使用 PHPKoa 開發 HTTP SERVER 的一個簡單示例!

安裝

composer require naka1205/phpkoa
複製程式碼

使用

Hello World

<?php
require __DIR__ . '/vendor/autoload.php';

use Naka507\Koa\Application;
use Naka507\Koa\Context;

$app = new Application();

$app->υse(function(Context $ctx) {
    $ctx->status = 200;
    $ctx->body = "<h1>Hello World</h1>";
});

$app->listen(3000);

複製程式碼

Error

<?php
require __DIR__ . '/vendor/autoload.php';

use Naka507\Koa\Application;
use Naka507\Koa\Context;
use Naka507\Koa\Error;
use Naka507\Koa\Timeout;
use Naka507\Koa\Router;
use Naka507\Koa\NotFound;

$app = new Application();

$app->υse(new Error());
$app->υse(new NotFound());
$app->υse(new Timeout(3)); //設定3秒超時

$router = new Router();

//正常訪問
$router->get('/hello', function(Context $ctx, $next) {
    $ctx->status = 200;
    $ctx->body = "<h1>Hello World</h1>";
});

//訪問超時
$router->get('/timeout', function(Context $ctx, $next) {
    yield async_sleep(5);
});

//訪問出錯
$router->get('/error', function(Context $ctx, $next) {
    $ctx->thrοw(500, "Internal Error");
    yield;
});

$app->υse($router->routes());

$app->listen(3000);

複製程式碼

Router

<?php
require __DIR__ . '/vendor/autoload.php';

define('DS', DIRECTORY_SEPARATOR);

use Naka507\Koa\Application;
use Naka507\Koa\Context;
use Naka507\Koa\Error;
use Naka507\Koa\Timeout;
use Naka507\Koa\Router;

$app = new Application();
$app->υse(new Error());
$app->υse(new Timeout(5));

$router = new Router();
$router->get('/demo1', function(Context $ctx, $next) {
    $ctx->body = "demo1";
});
$router->get('/demo2', function(Context $ctx, $next) {
    $ctx->body = "demo2";
});
$router->get('/demo3/(\d+)', function(Context $ctx, $next, $vars) {
    $ctx->status = 200;
    $ctx->body = "demo3={$vars[0]}";
});
$router->get('/demo4', function(Context $ctx, $next) {
    $ctx->redirect("/demo2");
});

//RESTful API
$router->post('/demo3/(\d+)', function(Context $ctx, $next, $vars) {
    //設定 session
    $ctx->setSession('demo3',$vars[0]);
    //設定 cookie
    $ctx->setCookie('demo3',$vars[0]);
    $ctx->status = 200;
    $ctx->body = "post:demo3={$vars[0]}";
});
$router->put('/demo3/(\d+)', function(Context $ctx, $next, $vars) {

    //獲取單個 cookie
    $cookie_demo3 = $ctx->getCookie('demo3');
    //或者
    $cookies = $ctx->cookies['demo3'];

    //獲取單個 session
    $session_demo3 = $ctx->getSession('demo3');
    //或者
    $session = $ctx->session['demo3'];

    $ctx->status = 200;
    $ctx->body = "put:demo3={$vars[0]}";
});
$router->delete('/demo3/(\d+)', function(Context $ctx, $next, $vars) {
    //清除所有 cookie
    $ctx->clearCookie();
    //清除所有 session
    $ctx->clearSession();
    $ctx->status = 200;
    $ctx->body = "delete:demo3={$vars[0]}";
});

//檔案上傳
$router->post('/files/(\d+)', function(Context $ctx, $next, $vars) {
    $upload_path = __DIR__ . DS .  "uploads" . DS;
    if ( !is_dir($upload_path) ) {
        mkdir ($upload_path , 0777, true);
    }
    $files = [];
    foreach ( $ctx->request->files as $key => $value) {
        if ( !$value['file_name'] || !$value['file_data'] ) {
            continue;
        }
        $file_path = $upload_path . $value['file_name'];
        file_put_contents($file_path, $value['file_data']);
        $value['file_path'] = $file_path;
        $files[] = $value;
    }

    $ctx->status = 200;
    $ctx->body = json_encode($files);
});


$app->υse($router->routes());

$app->listen(3000);

複製程式碼
<?php
//此處已省略 ... 

//使用第三方 HTTP 客戶端類庫,方便測試
use GuzzleHttp\Client;

$router = new Router();

//路由分組 
//http://127.0.0.1:5000/curl/get
//http://127.0.0.1:5000/curl/post
//http://127.0.0.1:5000/curl/put
//http://127.0.0.1:5000/curl/delete
$router->mount('/curl', function() use ($router) {
    $client = new Client();
    $router->get('/get', function( Context $ctx, $next ) use ($client) {
        $r = (yield $client->request('GET', 'http://127.0.0.1:3000/demo3/1'));
        $ctx->status = $r->getStatusCode();
        $ctx->body = $r->getBody();
    });

    $router->get('/post', function(Context $ctx, $next ) use ($client){
        $r = (yield $client->request('POST', 'http://127.0.0.1:3000/demo3/2'));
        $ctx->status = $r->getStatusCode();
        $ctx->body = $r->getBody();
    });

    $router->get('/put', function( Context $ctx, $next ) use ($client){
        $r = (yield $client->request('PUT', 'http://127.0.0.1:3000/demo3/3'));
        $ctx->status = $r->getStatusCode();
        $ctx->body = $r->getBody();
    });

    $router->get('/delete', function( Context $ctx, $next ) use ($client){
        $r = (yield $client->request('DELETE', 'http://127.0.0.1:3000/demo3/4'));
        $ctx->status = $r->getStatusCode();
        $ctx->body = $r->getBody();
    });
});

//http://127.0.0.1:5000/files
$router->get('/files', function(Context $ctx, $next ) {
    $client = new Client();
    $r = ( yield $client->request('POST', 'http://127.0.0.1:3000/files/2', [
        'multipart' => [
            [
                'name'     => 'file_name',
                'contents' => fopen( __DIR__ . '/file.txt', 'r')
            ],
            [
                'name'     => 'other_file',
                'contents' => 'hello',
                'filename' => 'filename.txt',
                'headers'  => [
                    'X-Foo' => 'this is an extra header to include'
                ]
            ]
        ]
    ]));
    
    $ctx->status = $r->getStatusCode();
    $ctx->body = $r->getBody();
});

// $router->get('/curl/(\w+)', function(Context $ctx, $next, $vars) {
//     $method = strtoupper($vars[0]);
//     $client = new Client();
//     $r = (yield $client->request($method, 'http://127.0.0.1:3000/demo3/123'));
//     $ctx->status = $r->getStatusCode();
//     $ctx->body = $r->getBody();
// });

$app->υse($router->routes());
$app->listen(5000);
複製程式碼

Template

<body>
    <h1>{title}</h1>
    <p>{time}</p>
</body>
複製程式碼
<?php
require __DIR__ . '/vendor/autoload.php';

use Naka507\Koa\Application;
use Naka507\Koa\Context;
use Naka507\Koa\Error;
use Naka507\Koa\Timeout;
use Naka507\Koa\Router;

$app = new Application();
$app->υse(new Error());
$app->υse(new Timeout(5));

$router = new Router();
$router->get('/hello', function(Context $ctx) {
    $ctx->status = 200;
    $ctx->state["title"] = "HELLO WORLD";
    $ctx->state["time"] = date("Y-m-d H:i:s", time());;
    yield $ctx->render(__DIR__ . "/hello.html");
});
$app->υse($router->routes());

$app->listen(3000);

複製程式碼
<body>
    <p>{title}</p>
    <table border=1>
      <tr><td>Name</td><td>Age</td></tr>
  
      <!-- BEGIN INFO -->
      <tr>
        <td> {name} </td>
        <td> {age} </td>
      </tr>
      <!-- END INFO -->
  
    </table>
</body>
複製程式碼
<?php
//此處已省略 ... 

//一維陣列
$router->get('/info', function(Context $ctx) {
    $info = array("name" => "小明", "age" => 15);
    $ctx->status = 200;
    $ctx->state["title"] = "這是一個學生資訊";
    $ctx->state["info"] = $info;
    yield $ctx->render(__DIR__ . "/info.html");
});
複製程式碼
<body>
    <p>{title}</p>
    <table border=1>
      <tr><td>Name</td><td>Age</td></tr>
  
      <!-- BEGIN TABLE -->
      <tr>
        <td> {name} </td>
        <td> {age} </td>
      </tr>
      <!-- END TABLE -->
  
    </table>
</body>
複製程式碼
<?php
//此處已省略 ... 

//二維陣列
$router->get('/table', function(Context $ctx) {
    $table = array(
        array("name" => "小明", "age" => 15),
        array("name" => "小花", "age" => 13),
        array("name" => "小剛", "age" => 17)
    );
    $ctx->status = 200;
    $ctx->state["title"] = "這是一個學生名單";
    $ctx->state["table"] = $table;
    yield $ctx->render(__DIR__ . "/table.html");
});
複製程式碼

中介軟體

靜態檔案處理 中介軟體 PHPKoa Static

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>PHPkoa Static</title>
    <link rel="stylesheet" href="/css/default.css">
</head>
<body>
    <img src="/images/20264902.jpg" />
</body>
</html>
複製程式碼
<?php
require __DIR__ . '/vendor/autoload.php';

defined('DS') or define('DS', DIRECTORY_SEPARATOR);

use Naka507\Koa\Application;
use Naka507\Koa\Context;
use Naka507\Koa\Error;
use Naka507\Koa\Timeout;
use Naka507\Koa\NotFound;
use Naka507\Koa\Router;
//靜態檔案處理 中介軟體
use Naka507\Koa\StaticFiles; 

$app = new Application();
$app->υse(new Error());
$app->υse(new Timeout(5));
$app->υse(new NotFound()); 
$app->υse(new StaticFiles(__DIR__ . DS .  "static" )); 

$router = new Router();

$router->get('/index', function(Context $ctx, $next) {
    $ctx->status = 200;
    yield $ctx->render(__DIR__ . "/index.html");
});

$app->υse($router->routes());

$app->listen(3000);
複製程式碼

相關文章