ویژگی تصویر

ساخت Router اختصاصی در PHP

  /  PHP   /  ساخت Router اختصاصی در PHP
بنر تبلیغاتی الف
آموزش PHP

ساخت یک Router اختصاصی در PHP به شما کنترل کامل روی نحوهٔ ثبت، مطابقت و اجرا کردن مسیرها (routes) می‌دهد. برای پروژه‌های کوچک و متوسط، یک Router سبک، سریع و قابل توسعه می‌تواند جایگزین فریم‌ورک‌های سنگین شود. در این مقاله به طراحی، پیاده‌سازی، بهینه‌سازی و نمونه‌های عملی از یک Router اختصاصی می‌پردازیم.

مفاهیم پایه‌ای

  • مسیر (Route): الگوی URL که با درخواست تطبیق می‌یابد.
  • Dispatcher: کدی که بعد از مطابقت مسیر، کنترل را به تابع/کلاس مربوطه می‌دهد.
  • پارامترهای پویا: مقادیری که از URL استخراج می‌شوند (/user/{id}).
  • میان‌افزار (Middleware): توابعی که قبل/بعد از کنترلر اجرا می‌شوند.

پیاده‌سازی ساده: Router پایه

class Router {
    protected $routes = [];

    public function add($method, $path, $handler) {
        $method = strtoupper($method);
        $this->routes[$method][$path] = $handler;
    }

    public function dispatch($method, $uri) {
        $method = strtoupper($method);
        $uri = parse_url($uri, PHP_URL_PATH);
        if (isset($this->routes[$method][$uri])) {
            return call_user_func($this->routes[$method][$uri]);
        }
        http_response_code(404);
        echo "404 Not Found";
    }
}

// استفاده:
$router = new Router();
$router->add('GET', '/', function(){ echo "Home"; });
$router->add('GET', '/about', function(){ echo "About"; });
$router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);

این کد یک Router خیلی ساده را نشان می‌دهد که فقط مسیرهای دقیق (exact match) را پشتیبانی می‌کند. برای هر روش HTTP یک آرایه نگه می‌دارد و در صورت پیدا نکردن مسیر، 404 برمی‌گرداند. این نسخه برای کاربردهای پایه‌ای مناسب است اما قابلیت مسیرهای پویا یا پارامترها را ندارد.

افزودن پارامترهای پویا و الگوها (Regex)

class Router {
    protected $routes = [];

    public function add($method, $path, $handler) {
        $method = strtoupper($method);
        $pattern = preg_replace('#{([w]+)}#', '(?P[^/]+)', $path);
        $pattern = '#^' . $pattern . '$#';
        $this->routes[$method][] = ['pattern' => $pattern, 'handler' => $handler, 'path' => $path];
    }

    public function dispatch($method, $uri) {
        $method = strtoupper($method);
        $uri = parse_url($uri, PHP_URL_PATH);
        if (!isset($this->routes[$method])) {
            return $this->notFound();
        }
        foreach ($this->routes[$method] as $route) {
            if (preg_match($route['pattern'], $uri, $matches)) {
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                return call_user_func_array($route['handler'], [$params]);
            }
        }
        return $this->notFound();
    }

    protected function notFound() {
        http_response_code(404);
        echo "404 Not Found";
    }
}

// استفاده:
$router = new Router();
$router->add('GET', '/user/{id}', function($params){ echo "User: " . $params['id']; });
$router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);

در این نسخه از preg_replace و الگوهای منظم برای تبدیل الگوهای {param} به گروه‌های نام‌دار استفاده شده است. وقتی مسیر تطبیق می‌یابد، پارامترها در آرایه‌ای با کلیدهای متنی قرار می‌گیرند و به هندلر پاس داده می‌شوند. این ساختار انعطاف‌پذیر است و امکان قرار دادن چند پارامتر را می‌دهد.

واسطه‌ها (Middleware) و چینش اجرای توابع

class Router {
    protected $routes = [];
    protected $middleware = [];

    public function add($method, $path, $handler, $middleware = []) {
        $method = strtoupper($method);
        $pattern = preg_replace('#{([w]+)}#', '(?P[^/]+)', $path);
        $pattern = '#^' . $pattern . '$#';
        $this->routes[$method][] = ['pattern' => $pattern, 'handler' => $handler, 'middleware' => $middleware];
    }

    public function use($callable) {
        $this->middleware[] = $callable;
    }

    protected function runMiddleware($middlewareStack, $request, $next) {
        $middleware = array_shift($middlewareStack);
        if (!$middleware) return $next($request);
        return $middleware($request, function($req) use ($middlewareStack, $next) {
            return $this->runMiddleware($middlewareStack, $req, $next);
        });
    }

    public function dispatch($method, $uri) {
        // مشابه مثال قبلی: پیدا کردن route و اجرای middlewareها قبل از handler
    }
}

در این نمونه الگوی اجرای middleware نشان داده شده است: یک صف از توابع که هر کدام می‌توانند درخواست را قبل از رسیدن به هندلر تغییر دهند یا متوقف کنند. در پیاده‌سازی کامل باید هنگام پیدا کردن مسیر، middleware مربوطه و middleware عمومی را با هم ترکیب کنید و سپس مانند الگوی بالا آن‌ها را اجرا کنید.

بهینه‌سازی‌ها و نکات حرفه‌ای

  • کش کردن الگوهای تولید شده: برای پروژه‌های پر تراکنش، تبدیل مسیرها به Regex را یک‌بار انجام دهید و در فایل کش یا APCu ذخیره کنید.
  • روش‌های HTTP: سعی کنید برای هر route متودهای صحیح (GET, POST, PUT, DELETE) را مشخص کنید.
  • نیم‌رخای خطا: صفحهٔ 404، 405 (Method Not Allowed) و 500 را جدی بگیرید و پاسخ JSON برای APIها فراهم کنید.
  • امنیت: پارامترهای ورودی را اعتبارسنجی و در صورت نیاز فیلتر کنید تا از حملات تزریق جلوگیری شود.
  • مسیرهای گروهی و پیشوندها: برای خوانایی و نگهداری، گروه‌هایی با پیشوند مشترک بسازید (مثلاً /admin).

نمونه جدول مقایسه روش‌ها

ویژگیRouter سادهRouter پیشرفته
پشتیبانی از پارامترهاخیربله (Regex)
Middlewareخیربله
کشینگخیرپیشنهادی
سفارشی‌سازیمحدودقابل توسعه

مثال کامل استفاده (Router با پارامتر و middleware)

// فرض: Router کامل ساخته شده مشابه نمونه‌های بالا
$router = new Router();

// middleware سراسری
$router->use(function($req, $next){
    // مثال لاگینگ ساده
    error_log($req['method'] . ' ' . $req['uri']);
    return $next($req);
});

// routes
$router->add('GET', '/', function($params){ echo "Welcome"; });
$router->add('GET', '/post/{slug}', function($params){ echo "Post: " . htmlspecialchars($params['slug']); }, ['authMiddleware']);
$router->add('POST', '/api/data', function($params){ echo json_encode(['status' => 'ok']); });

// dispatch
$router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);

در این قطعه فرض کرده‌ایم Router کامل‌تر امکانات middleware سراسری و اختصاصی را دارد. مثال‌ها نشان می‌دهند چگونه می‌توان مسیر GET برای نمایش پست بر اساس slug و یک مسیر API برای دریافت داده‌ها تعریف کرد. همچنین از htmlspecialchars برای جلوگیری از XSS استفاده شده است.

نتیجه‌گیری و پیشنهادات نهایی

ساخت یک Router اختصاصی در PHP فرصتی است برای درک عمیق‌تر نحوهٔ کار فریم‌ورک‌ها و ایجاد راه‌حل‌های سبک و بهینه برای نیازهای خاص. با افزودن ویژگی‌هایی مثل پارامترهای نام‌دار، middleware، کش و مدیریت خطا می‌توان یک Router حرفه‌ای و قابل توسعه ساخت. اگر پروژهٔ شما رشد کند، می‌توانید از الگوهای تست‌پذیر، PSR-7 برای درخواست/پاسخ و کتابخانه‌های معتبر برای اجزاء مربوط به امنیت و کش بهره ببرید.

نکتهٔ پایانی: قبل از بازنویسی کامل یک Router، بررسی کنید آیا نیاز واقعی به سفارشی‌سازی وجود دارد یا استفاده از یک Router آماده (مثل FastRoute) می‌تواند زمان توسعه و خطاها را کاهش دهد.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: