Réponse

La réponse est en fait un objet support\Response. Pour faciliter la création de cet objet, Webman propose plusieurs fonctions d'assistance.

Retourner une réponse arbitraire

Exemple

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function hello(Request $request)
    {
        return response('hello webman');
    }
}

La fonction response est implémentée comme suit :

function response($body = '', $status = 200, $headers = array())
{
    return new Response($status, $headers, $body);
}

Vous pouvez également créer un objet response vide au préalable, puis utiliser $response->cookie() $response->header() $response->withHeaders() $response->withBody() pour définir le contenu de la réponse à l'endroit approprié.

public function hello(Request $request)
{
    // Créer un objet
    $response = response();

    // .... Logique métier omise

    // Définir le cookie
    $response->cookie('foo', 'value');

    // .... Logique métier omise

    // Définir les en-têtes http
    $response->header('Content-Type', 'application/json');
    $response->withHeaders([
                'X-Header-One' => 'Header Value 1',
                'X-Header-Tow' => 'Header Value 2',
            ]);

    // .... Logique métier omise

    // Définir les données à retourner
    $response->withBody('Les données retournées');
    return $response;
}

Retourner json

Exemple

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function hello(Request $request)
    {
        return json(['code' => 0, 'msg' => 'ok']);
    }
}

La fonction json est implémentée comme suit :

function json($data, $options = JSON_UNESCAPED_UNICODE)
{
    return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
}

Retourner xml

Exemple

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function hello(Request $request)
    {
        $xml = <<<XML
               <?xml version='1.0' standalone='yes'?>
               <values>
                   <truevalue>1</truevalue>
                   <falsevalue>0</falsevalue>
               </values>
               XML;
        return xml($xml);
    }
}

La fonction xml est implémentée comme suit :

function xml($xml)
{
    if ($xml instanceof SimpleXMLElement) {
        $xml = $xml->asXML();
    }
    return new Response(200, ['Content-Type' => 'text/xml'], $xml);
}

Retourner une vue

Créez un fichier app/controller/FooController.php comme suit :

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function hello(Request $request)
    {
        return view('foo/hello', ['name' => 'webman']);
    }
}

Créez un fichier app/view/foo/hello.html comme suit :

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>webman</title>
</head>
<body>
hello <?=htmlspecialchars($name)?>
</body>
</html>

Redirection

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function hello(Request $request)
    {
        return redirect('/user');
    }
}

La fonction redirect est implémentée comme suit :

function redirect($location, $status = 302, $headers = [])
{
    $response = new Response($status, ['Location' => $location]);
    if (!empty($headers)) {
        $response->withHeaders($headers);
    }
    return $response;
}

Paramètres d'en-tête

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function hello(Request $request)
    {
        return response('hello webman', 200, [
            'Content-Type' => 'application/json',
            'X-Header-One' => 'Header Value' 
        ]);
    }
}

Vous pouvez également utiliser les méthodes header et withHeaders pour définir un ou plusieurs en-têtes.

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function hello(Request $request)
    {
        return response('hello webman')
        ->header('Content-Type', 'application/json')
        ->withHeaders([
            'X-Header-One' => 'Header Value 1',
            'X-Header-Tow' => 'Header Value 2',
        ]);
    }
}

Vous pouvez également définir les en-têtes à l'avance et, à la fin, définir les données à retourner.

public function hello(Request $request)
{
    // Créer un objet
    $response = response();

    // .... Logique métier omise

    // Définir les en-têtes http
    $response->header('Content-Type', 'application/json');
    $response->withHeaders([
                'X-Header-One' => 'Header Value 1',
                'X-Header-Tow' => 'Header Value 2',
            ]);

    // .... Logique métier omise

    // Définir les données à retourner
    $response->withBody('Les données retournées');
    return $response;
}

Définir un cookie

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function hello(Request $request)
    {
        return response('hello webman')
        ->cookie('foo', 'value');
    }
}

Vous pouvez également définir le cookie à l'avance, puis définir les données à retourner.

public function hello(Request $request)
{
    // Créer un objet
    $response = response();

    // .... Logique métier omise

    // Définir le cookie
    $response->cookie('foo', 'value');

    // .... Logique métier omise

    // Définir les données à retourner
    $response->withBody('Les données retournées');
    return $response;
}

Les paramètres complets de la méthode cookie sont les suivants :

cookie($name, $value = '', $max_age = 0, $path = '', $domain = '', $secure = false, $http_only = false)

Retourner un flux de fichier

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function hello(Request $request)
    {
        return response()->file(public_path() . '/favicon.ico');
    }
}
  • Webman supporte l'envoi de très gros fichiers
  • Pour les gros fichiers (plus de 2 Mo), Webman ne lira pas tout le fichier en mémoire d'un coup, mais lira et enverra le fichier par morceaux au bon moment
  • Webman optimisera la vitesse de lecture et d'envoi des fichiers en fonction de la vitesse de réception du client, garantissant l'envoi le plus rapide tout en réduisant l'occupation mémoire au minimum
  • L'envoi de données est non-bloquant, ce qui n'affecte pas le traitement d'autres requêtes
  • La méthode file ajoutera automatiquement l'en-tête if-modified-since et vérifiera cet en-tête lors de la prochaine requête ; si le fichier n'a pas été modifié, elle renverra directement un 304 pour économiser de la bande passante
  • Le fichier envoyé utilisera automatiquement l'en-tête Content-Type approprié envoyé au navigateur
  • Si le fichier n'existe pas, une réponse 404 sera automatiquement renvoyée

Télécharger un fichier

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function hello(Request $request)
    {
        return response()->download(public_path() . '/favicon.ico', 'nom_du_fichier.ico');
    }
}

La méthode download est fondamentalement similaire à la méthode file, les différences sont :

  1. Le nom du fichier de téléchargement est défini et le fichier sera téléchargé au lieu d'être affiché dans le navigateur
  2. La méthode download ne vérifiera pas l'en-tête if-modified-since

Obtenir la sortie

Certaines bibliothèques impriment directement le contenu du fichier dans la sortie standard, ce qui signifie que les données seront imprimées dans le terminal de commande et ne seront pas envoyées au navigateur. Dans ce cas, nous devons capturer les données dans une variable en utilisant ob_start(); ob_get_clean();, puis envoyer les données au navigateur, par exemple :

<?php

namespace app\controller;

use support\Request;

class ImageController
{
    public function get(Request $request)
    {
        // Créer une image
        $im = imagecreatetruecolor(120, 20);
        $text_color = imagecolorallocate($im, 233, 14, 91);
        imagestring($im, 1, 5, 5,  'Une chaîne de texte simple', $text_color);

        // Commencer à capturer la sortie
        ob_start();
        // Sortir l'image
        imagejpeg($im);
        // Obtenez le contenu de l'image
        $image = ob_get_clean();

        // Envoyer l'image
        return response($image)->header('Content-Type', 'image/jpeg');
    }
}

Réponse en morceaux

Parfois, nous voulons envoyer la réponse en morceaux, vous pouvez vous référer à l'exemple ci-dessous.

<?php

namespace app\controller;

use support\Request;
use support\Response;
use Workerman\Protocols\Http\Chunk;
use Workerman\Timer;

class IndexController
{
    public function index(Request $request): Response
    {
        // Obtenir la connexion
        $connection = $request->connection;
        // Envoyer le corps http à intervalles réguliers
        $timer = Timer::add(1, function () use ($connection, &$timer) {
            static $i = 0;
            if ($i++ < 10) {
                // Envoyer le corps http
                $connection->send(new Chunk($i));
            } else {
                // Supprimer le minuteur non utilisé pour éviter les fuites de mémoire
                Timer::del($timer);
                // Envoyer un paquet Chunk vide pour notifier le client de la fin de la réponse
                $connection->send(new Chunk(''));
            }
        });
        // D'abord, renvoyer un en-tête http avec Transfer-Encoding: chunked, le corps http est envoyé de manière asynchrone
        return response()->withHeaders([
            "Transfer-Encoding" => "chunked",
        ]);
    }

}

Si vous appelez un modèle de grande taille, vous pouvez vous référer à l'exemple ci-dessous.

composer require webman/openai
<?php
namespace app\controller;
use support\Request;

use Webman\Openai\Chat;
use Workerman\Protocols\Http\Chunk;

class ChatController
{
    public function completions(Request $request)
    {
        $connection = $request->connection;
        // https://api.openai.com peut ne pas être accessible depuis le pays, vous pouvez utiliser l'adresse https://api.openai-proxy.com à la place
        $chat = new Chat(['apikey' => 'sk-xx', 'api' => 'https://api.openai.com']);
        $chat->completions(
            [
                'model' => 'gpt-3.5-turbo',
                'stream' => true,
                'messages' => [['role' => 'user', 'content' => 'hello']],
            ], [
            'stream' => function($data) use ($connection) {
                // Lorsqu'OpenAI renvoie des données, les transférer au navigateur
                $connection->send(new Chunk(json_encode($data, JSON_UNESCAPED_UNICODE) . "\n"));
            },
            'complete' => function($result, $response) use ($connection) {
                // À la fin de la réponse, vérifiez s'il y a des erreurs
                if (isset($result['error'])) {
                    $connection->send(new Chunk(json_encode($result, JSON_UNESCAPED_UNICODE) . "\n"));
                }
                // Renvoie un chunk vide pour indiquer la fin de la réponse
                $connection->send(new Chunk(''));
            },
        ]);
        // D'abord renvoyer un en-tête http, les données suivantes sont renvoyées de manière asynchrone
        return response()->withHeaders([
            "Transfer-Encoding" => "chunked",
        ]);
    }
}