Przejdź do głównej treści
Grafika przedstawia ukryty obrazek

Budowa własnego systemu middleware na przykładzie Koseven Framework

Budowa własnego systemu middleware na przykładzie Koseven Framework

Middleware to wzorzec programistyczny używany głównie w aplikacjach webowych, który pozwala na przechwycenie i przetworzenie żądania HTTP przed (lub czasem po) jego obsłużeniem przez kontroler.

Co to dokładnie znaczy?

Middleware działa jak warstwa pośrednia między użytkownikiem a aplikacją. Zanim żądanie trafi do kontrolera (czyli np. logiki, która zwraca stronę), middleware może:

  • analizować to żądanie,
  • zmieniać je,
  • zablokować,
  • przekierować,
  • albo dodać coś do odpowiedzi.

Do czego służy middleware?

Typowe zastosowania middleware:

Zastosowanie Opis
Autoryzacja Sprawdza, czy użytkownik jest zalogowany lub ma odpowiednie uprawnienia.
Logowanie żądań Zapisuje logi z każdego żądania (np. do pliku, bazy danych).
Obsługa CORS Ustawia nagłówki, które pozwalają na żądania z innych domen.
Ochrona przed CSRF Sprawdza tokeny bezpieczeństwa w formularzach.
Rate limiting Ogranicza liczbę żądań od danego użytkownika (np. 100/h).
Czyszczenie danych Modyfikuje dane wejściowe (np. trim, sanitize).

Przykład działania (schemat)

[Przeglądarka] --> [Middleware: Auth] --> [Middleware: Log] --> [Kontroler] --> [Odpowiedź]

Każda warstwa może:

  • przepuścić dalej żądanie,
  • zmienić je,
  • albo je przerwać (np. zwracając redirect do logowania).

W praktyce

  • W Laravelu: $this->middleware('auth')
  • W Symfony: event subscribers/listeners
  • W Express.js (Node.js): app.use(middlewareFunction)
  • W Koseven/Kohana – trzeba middleware zbudować samemu

Globalny system middleware

W Koseven (fork Kohana 3.3), system middleware nie jest domyślnie wspierany w taki sposób jak np. w Laravelu czy Symfony, ale można go łatwo zaimplementować poprzez modyfikację procesu żądań w pliku bootstrap.php. Poniżej znajdziesz przykład prostego systemu middleware, który działa globalnie i jest zdefiniowany w bootstrap.php.

Krok 1: Utwórz klasę Middleware

Utwórz katalog np. classes/Middleware i stwórz tam pliki klas middleware.

Przykład: classes/Middleware/Auth.php

<?php defined('SYSPATH') or die('No direct script access.');

class Middleware_Auth {
    public static function handle(Request $request)
    {
        // Przykład: prosty auth check
        if ($request->uri() === 'admin' && !Auth::instance()->logged_in()) {
            // Przekieruj nieautoryzowanego użytkownika
            HTTP::redirect('/login');
        }
    }
}

Możesz dodać wiele klas, np. Middleware/Log.php, Middleware/Cors.php, itp.

Krok 2: Zdefiniuj i uruchom middleware w bootstrap.php

W pliku application/bootstrap.php, zaraz przed wywołaniem $response = Request::factory(...)->execute()->send_headers()->body();, dodaj logikę do ładowania middleware:

// application/bootstrap.php

// ...

// Create the request instance
$request = Request::factory();

// Lista middleware do wykonania (kolejność ma znaczenie)
$middlewares = [
    'Middleware_Auth',
    // 'Middleware_Log',
    // 'Middleware_Cors',
];

foreach ($middlewares as $middleware) {
    if (class_exists($middleware) && method_exists($middleware, 'handle')) {
        $middleware::handle($request);
    }
}

// Kontynuuj jak zwykle
$response = $request->execute()
    ->send_headers()
    ->body();

Co to daje?

  • Middleware są definiowane centralnie w jednym miejscu.
  • Można łatwo dodawać/usuwać middleware.
  • Middleware działa niezależnie od kontrolerów.
  • Middleware mogą modyfikować żądanie lub odpowiedź (zwracać redirect, ustawiać nagłówki itp.).

Opcjonalnie: Middleware z możliwością zatrzymania wykonania

Jeśli chcesz, by middleware mogło przerwać dalsze wykonywanie, możesz zmodyfikować handle() tak, by zwracało np. Response, np.:

$response = null;

foreach ($middlewares as $middleware) {
    $result = $middleware::handle($request);

    if ($result instanceof Response) {
        // Middleware zakończyło przetwarzanie
        $response = $result;
        break;
    }
}

if (!$response) {
    $response = $request->execute();
}

$response->send_headers()->body();

Obsługa routingu

W Koseven (index.php) tworzony jest obiekt Request::factory(), a bootstrap.php służy głównie do konfiguracji (modułów, logów, routingu, itp.). Jeśli chcesz mieć middleware przypisane do konkretnych route’ów (np. tylko do panelu admina), to najlepiej będzie rozszerzyć system routingu tak, by pozwalał na przypisanie middleware do wybranych tras – czyli coś w stylu:

Route::set('admin', 'admin(/<controller>(/<action>(/<id>)))')
    ->middleware(['auth'])
    ->defaults([
        'directory'  => 'Admin',
        'controller' => 'Dashboard',
        'action'     => 'index',
    ]);

Kohana/Koseven nie wspiera ->middleware(...) domyślnie, więc trzeba to dodać samemu.

Rozbudowa systemu routingu o middleware

1. Rozszerz klasę Route

Utwórz plik:
classes/Route.php

<?php defined('SYSPATH') or die('No direct script access.');

class Route extends Kohana_Route {

    protected $_middleware = [];

    public function middleware(array $middleware)
    {
        $this->_middleware = $middleware;
        return $this;
    }

    public function get_middleware()
    {
        return $this->_middleware;
    }
}

Dzięki temu możesz przypisać middleware do każdej trasy.

2. Zmień index.php, by uruchamiał middleware dla dopasowanej trasy

W index.php, po Request::factory() ale przed ->execute(), dodaj uruchamianie middleware:

<?php

require SYSPATH.'classes/Kohana/Core'.EXT;
require APPPATH.'classes/Kohana'.EXT;

// Load the request instance
$request = Request::factory();

// Znajdź dopasowaną trasę
$route = Route::all();
$matched = null;

foreach ($route as $r) {
    if ($r->matches($request)) {
        $matched = $r;
        break;
    }
}

if ($matched && method_exists($matched, 'get_middleware')) {
    foreach ($matched->get_middleware() as $middleware) {
        $class = 'Middleware_' . ucfirst($middleware);
        if (class_exists($class) && method_exists($class, 'handle')) {
            $response = $class::handle($request);
            if ($response instanceof Response) {
                // Middleware może przerwać dalsze przetwarzanie
                $response->send_headers()->body();
                exit;
            }
        }
    }
}

// Wykonaj żądanie jak zwykle
$request->execute()
    ->send_headers()
    ->body();

3. Przykład Middleware

classes/Middleware/Auth.php

<?php defined('SYSPATH') or die('No direct script access.');

class Middleware_Auth {
    public static function handle(Request $request)
    {
        if (!Auth::instance()->logged_in()) {
            return Response::factory()
                ->redirect('/login');
        }
        return null;
    }
}

Użycie: Definicja route’a z middleware

W bootstrap.php:

Route::set('admin', 'admin(/<controller>(/<action>(/<id>)))')
    ->middleware(['auth']) // <-- to działa teraz!
    ->defaults([
        'directory'  => 'Admin',
        'controller' => 'Dashboard',
        'action'     => 'index',
    ]);

Możliwości rozszerzeń

  • Możesz wprowadzić aliasy (->middleware(['auth', 'log']))
  • Możesz dodać kontekst do middleware, np. auth:admin, auth:user
  • Możesz cache’ować dopasowane middleware dla danej trasy

Obsługa middleware w konstruktorze kontrolera

Jeśli chcesz, by middleware (np. auth) był wykonywany na poziomie kontrolera, to realizacja go w konstruktorze kontrolera bazowego to proste i idiomatyczne podejście w Koseven. Poniżej znajdziesz kompletną, czystą i rozszerzalną propozycję dla middleware auth, który działa na poziomie konstruktorów kontrolerów.

Założenia

  • Middleware auth powinien działać tylko dla kontrolerów w Admin_.
  • Kontrolery dziedziczą po wspólnej klasie Controller_Admin_Base.
  • Sprawdzenie logowania użytkownika odbywa się w konstruktorze.

Struktura plików

application/
    classes/
        Controller/
            Admin/
                Base.php        <-- wspólny kontroler z middleware
                Dashboard.php   <-- konkretny kontroler
        Middleware/
            Auth.php            <-- logika auth

1. Middleware/Auth.php – logika middleware

<?php defined('SYSPATH') or die('No direct script access.');

class Middleware_Auth {

    public static function check()
    {
        if (!Auth::instance()->logged_in()) {
            HTTP::redirect('/login');
        }
    }

}

Możesz tu rozszerzyć logikę, np. sprawdzać role, tokeny itp.

2. Controller_Admin_Base – kontroler bazowy z auth middleware

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Admin_Base extends Controller_Template {

    public function before()
    {
        parent::before();

        // Middleware auth
        Middleware_Auth::check();
    }
}

Jeśli chcesz, możesz dołożyć inne middleware jako osobne klasy.

3. Przykład: Controller_Admin_Dashboard

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Admin_Dashboard extends Controller_Admin_Base {

    public function action_index()
    {
        $this->response->body('Panel administracyjny – witaj!');
    }

}

Alternatywa: Middleware z warunkami

Jeśli chcesz kontrolować middleware np. tylko dla wybranych akcji:

public function before()
{
    parent::before();

    if ($this->request->action() !== 'public') {
        Middleware_Auth::check();
    }
}

Zalety tego podejścia

  • Nie trzeba zmieniać index.php.
  • Przejrzysta struktura.
  • Middleware działa tylko dla kontrolerów dziedziczących po Controller_Admin_Base.
  • Można łatwo rozbudować o kolejne middleware (Log, ACL, Throttle itp.).

Middleware w stylu Laravela

Jeśli chcesz uzyskać coś w stylu:

$this->middleware('auth');

czyli dynamiczne przypisywanie middleware do konkretnego kontrolera (lub akcji) – tak jak w Laravelu. W Koseven nie ma tego mechanizmu domyślnie, ale możemy go łatwo zasymulować i zaimplementować.

Plan działania

  1. Tworzymy metodę middleware() (np. w klasie bazowej Controller_Base), która rejestruje middleware do wykonania.
  2. W before() wywołujemy zarejestrowane middleware.
  3. Middleware może być przypisany do:

    • wszystkich akcji (global)
    • tylko wybranych (only)
    • z wykluczeniami (except)

1. Klasa bazowa Controller_Base z obsługą middleware()

classes/Controller/Base.php:

<?php defined('SYSPATH') or die('No direct script access.');

abstract class Controller_Base extends Controller_Template {

    protected $_middleware = [];

    public function middleware($name, array $options = [])
    {
        $this->_middleware[] = [
            'name' => $name,
            'only' => $options['only'] ?? [],
            'except' => $options['except'] ?? [],
        ];
    }

    public function before()
    {
        parent::before();

        foreach ($this->_middleware as $entry) {
            $name = $entry['name'];
            $only = $entry['only'];
            $except = $entry['except'];
            $action = $this->request->action();

            if (!empty($only) && !in_array($action, $only)) {
                continue;
            }

            if (!empty($except) && in_array($action, $except)) {
                continue;
            }

            $class = 'Middleware_' . ucfirst($name);

            if (class_exists($class) && method_exists($class, 'handle')) {
                $response = $class::handle($this->request);
                if ($response instanceof Response) {
                    // Przerwij i wyślij odpowiedź natychmiast
                    $response->send_headers()->body();
                    exit;
                }
            }
        }
    }
}

2. Middleware: Middleware/Auth.php

<?php defined('SYSPATH') or die('No direct script access.');

class Middleware_Auth {
    public static function handle(Request $request)
    {
        if (!Auth::instance()->logged_in()) {
            return Response::factory()->redirect('/login');
        }
        return null;
    }
}

3. Użycie w kontrolerze

classes/Controller/Admin/Dashboard.php:

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Admin_Dashboard extends Controller_Base {

    public function before()
    {
        // Middleware 'auth' dla wszystkich akcji
        $this->middleware('auth');

        // Middleware tylko dla konkretnych akcji:
        // $this->middleware('auth', ['only' => ['index', 'edit']]);

        parent::before();
    }

    public function action_index()
    {
        $this->response->body('Panel admina: index');
    }

    public function action_public()
    {
        $this->response->body('Publiczna akcja bez autoryzacji');
    }
}

Bonus: Kolejne middleware

Możesz tworzyć kolejne middleware w katalogu classes/Middleware/, np.:

  • Middleware_Log
  • Middleware_Cors
  • Middleware_Throttle

i rejestrować je podobnie:

$this->middleware('log');
$this->middleware('auth', ['except' => ['login']]);

Zalety tego podejścia

  • Zachowujesz znajomy sposób z Laravela.
  • Middleware są rejestrowane na poziomie kontrolera (więc bardziej granularnie niż w bootstrap.php).
  • Obsługa only i except jak w Laravelu.
  • Bez zmian w rdzeniu frameworka.

Podsumowanie

Middleware to kod, który wykonuje się przed lub po kontrolerze, aby wykonać operacje na żądaniu lub odpowiedzi. Ułatwia oddzielenie logiki technicznej (np. auth, logi, rate-limit) od logiki biznesowej.

16 maja 2025 13

Kategorie

programowanie

Tagi

koseven php

Dziękujemy!
()

Powiązane wpisy


Informacja o cookies

Moja strona internetowa wykorzystuje wyłącznie niezbędne pliki cookies, które są wymagane do jej prawidłowego działania. Nie używam ciasteczek w celach marketingowych ani analitycznych. Korzystając z mojej strony, wyrażasz zgodę na stosowanie tych plików. Możesz dowiedzieć się więcej w mojej polityce prywatności.