Just for fun——Slim借力Swoole

原文連接php

Slim

Slim的話,是一個遵循PSR (PSR-7)規範微型的框架,做者這樣說:mysql

Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs. At its core, Slim is a dispatcher that receives an HTTP request, invokes an appropriate callback routine, and returns an HTTP response. That’s it.git

大體意思:slim的核心工做:分發了Http請求,而後調用回調函數,返回一個Http response對象。github

Slim其實就幫咱們作了兩件事web

  1. 路由的分發
  2. 依賴的注入 框架很小,因此別的部分(如db操做、模板引擎)可能須要本身實現,但slim經過依賴注入,讓你能夠很輕鬆的組裝其餘功能到slim中。

快速入門:

<?php
// Create and configure Slim app
$config = ['settings' => [
    'addContentLengthHeader' => false,
]];
$app = new \Slim\App($config);

// Define app routes
$app->get('/hello/{name}', function ($request, $response, $args) {
    return $response->write("Hello " . $args['name']);
});

// Run app
$app->run();
複製代碼

$request表明了當前請求對象,$response表明了當前響應對象,$args是佔位符的鍵值對數組。 訪問**/hello/salamander**就會輸出Hello salamandersql

添加依賴

**DB**是我本身封裝的一個PDO的操做類。docker

$config = ['settings' => [
    'addContentLengthHeader' => false,
]];
$app = new \Slim\App($config);
$container = $app->getContainer();

$container['db'] = function($c) {
    $dbHost = 'localhost';
    $dbName = 'test';

    $dbConf = [
        'dsn' => "mysql:dbname={$dbName};host={$dbHost}",
        'username' => "root",
        'password' => "******",
        'charset' => 'utf8'
    ];
    $db = new \App\Library\DB();
    $db->__setup($dbConf);
    return $db;
};

// Define app routes
$app->get('/user/{uid}', 'App\Controller\IndexController:index');

複製代碼

IndexController類

namespace App\Controller;

class IndexController 
{
    protected $container;
    
    public function __construct(ContainerInterface $container) {
        $this->container = $container;
    }
    
    public function index($request, $response, $args) {
        $info = $this->container['db']->fetch('SELECT name FROM user WHERE uid = :uid', [
            'uid' => $args['uid']
        ]);
        echo "user name is " . $info['name'];
    }
}
複製代碼

IndexController類的是經過composer自動載入的(代碼中沒寫):segmentfault

"autoload": {
    "psr-4": {
        "App\\": "app/"
    }
},
複製代碼

代碼中能夠發現,依賴容器的注入是在類實例化的時候發生的。執行IndexController的index方法時,咱們從$container中取出的db依賴,這時候,註冊的回調函數被調用,返回實例。由於是用到時才實例化,這個叫作延遲實例化數組

結合Swoole

Swoole讓PHP能夠常駐內存,並且它提供了Http Server的功能,因此Slim和Swoole沒什麼衝突。bash

思考

Slim是經過當前路由(譬如/user/2,不帶查詢字符串)和http方法來找到正確的回調函數的。這些量Slim是從哪裏取的呢?確定是$_SERVER。查看slim源碼: run()方法:

public function run($silent = false)
{
    $response = $this->container->get('response');

    try {
        ob_start();
        $response = $this->process($this->container->get('request'), $response);
    } catch (InvalidMethodException $e) {
        $response = $this->processInvalidMethod($e->getRequest(), $response);
    } finally {
        $output = ob_get_clean();
    }

    if (!empty($output) && $response->getBody()->isWritable()) {
        $outputBuffering = $this->container->get('settings')['outputBuffering'];
        if ($outputBuffering === 'prepend') {
            // prepend output buffer content
            $body = new Http\Body(fopen('php://temp', 'r+'));
            $body->write($output . $response->getBody());
            $response = $response->withBody($body);
        } elseif ($outputBuffering === 'append') {
            // append output buffer content
            $response->getBody()->write($output);
        }
    }

    $response = $this->finalize($response);

    if (!$silent) {
        $this->respond($response);
    }

    return $response;
}
複製代碼

發現$request對象是從容器取出來的,那$request是怎麼註冊的呢??,那就看App類的構造函數了,最後發現Container類的構造函數中有registerDefaultServices()方法:

private function registerDefaultServices($userSettings)
{
    $defaultSettings = $this->defaultSettings;

    /**
     * This service MUST return an array or an
     * instance of \ArrayAccess.
     *
     * @return array|\ArrayAccess
     */
    $this['settings'] = function () use ($userSettings, $defaultSettings) {
        return new Collection(array_merge($defaultSettings, $userSettings));
    };

    $defaultProvider = new DefaultServicesProvider();
    $defaultProvider->register($this);
}
複製代碼

查看$defaultProvider->register()方法:

public function register($container)
{
    if (!isset($container['environment'])) {
        /**
         * This service MUST return a shared instance
         * of \Slim\Interfaces\Http\EnvironmentInterface.
         *
         * @return EnvironmentInterface
         */
        $container['environment'] = function () {
            return new Environment($_SERVER);
        };
    }

    if (!isset($container['request'])) {
        /**
         * PSR-7 Request object
         *
         * @param Container $container
         *
         * @return ServerRequestInterface
         */
        $container['request'] = function ($container) {
            return Request::createFromEnvironment($container->get('environment'));
        };
    }
    
    //...
複製代碼

能夠看到$request對象是經過Request::createFromEnvironment方法構造的,它須要從容器中取出environment依賴,而environment依賴是經過構造一個Environment對象得來的,它正好放入了$_SERVER 查看Environment類源碼,能夠發現它繼承了Collection類,Collection的構造函數以下:

public function __construct(array $items = [])
{
    $this->replace($items);
}
複製代碼

從上面咱們能夠得出,咱們主要註冊一個自定義的environment依賴就行,原來$_SERVER的信息能夠從swoole的$request->server中取。

簡單實現

server.php

<?php
use Slim\Http\Environment;

// 定義常量
define("ROOT", getcwd() . '/..');
define('APP', ROOT . '/app');

require ROOT . '/vendor/autoload.php';
// load functions
require APP . '/functions.php';


$http = new swoole_http_server("0.0.0.0", 8888);

$http->on("start", function ($server) {
    echo "Swoole http server is started at http://0.0.0.0:8888\n";
});

$http->on("request", function ($request, $response) {
    // Instantiate the app
    $config = [
        'settings' => [
            'addContentLengthHeader' => false,
        ]
    ];
    $config['environment'] = function () use($request) {
        $server = [];
        foreach ($request->server as $key => $value) {
            $server[strtoupper($key)] = $value;
        }
        return new Environment($server);
    };
    $app = new \Slim\App($config);
    // Register routes
    require APP . '/routes.php';

    // Run app
    $slimResponse = $app->run(true);
    $headers = $slimResponse->getHeaders();
    foreach ($headers as $name => $values) {
        $response->header($name, implode(", ", $values));
    }
    $response->header("X-Powered-By", "Salamander");
    $response->end($slimResponse->getBody());
});

$http->start();
複製代碼

注意**$request->server中key都是小寫的**,因此這裏轉化了一下。

routes.php(在App目錄中)

<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;


$app->get('/', function (Request $request, Response $response) {
    $response->getBody()->write('Hello Salamander');
    return $response;
});


$app->get('/user/{uid}', function (Request $request, Response $response, $args) {
    $response->getBody()->write('Hello User:' . $args['uid']);
    return $response;
});
複製代碼

測試

訪問/

clipboard.png

訪問/user/45

clipboard.png

打包下載測試

百度雲盤 tip:環境基於docker的,運行docker-compose up便可

Github上的討論

Issue

相關文章
相關標籤/搜索