مسیریابی در Flare Framework

فلر از کتابخانه bramus/router برای مدیریت مسیرها (Routes) استفاده می‌کند. این کتابخانه امکان تعریف مسیرهای ساده و پویا را با پشتیبانی از متدهای مختلف HTTP فراهم می‌کند.

مسیریابی پایه (Routing)

برای تعریف یک مسیر، از متد match استفاده می‌شود که ترکیبی از یک یا چند متد HTTP و یک الگوی مسیر (pattern) را می‌پذیرد:


$router->match('GET|POST', 'pattern', function() {
    // ...
});
    

کتابخانه bramus/router از متدهای GET، POST، PUT، PATCH، DELETE، HEAD و OPTIONS پشتیبانی می‌کند. می‌توانید یک متد تکی یا چند متد را با | جدا کرده و ارسال کنید.

وقتی یک مسیر با URL فعلی (مثلاً $_SERVER['REQUEST_URI']) مطابقت داشته باشد، تابع متصل به آن مسیر اجرا خواهد شد. تنها اولین مسیری که تطابق داشته باشد اجرا می‌شود. اگر هیچ مسیری پیدا نشود، هندلر 404 فعال می‌شود.

میانبرهای مسیریابی (Routing Shorthands)

برای راحتی بیشتر، میانبرهایی برای متدهای تکی فراهم شده است:


$router->get('pattern', function() {
    // ...
});

$router->post('pattern', function() {
    // ...
});

$router->put('pattern', function() {
    // ...
});

$router->delete('pattern', function() {
    // ...
});

$router->options('pattern', function() {
    // ...
});

$router->patch('pattern', function() {
    // ...
});
    

برای مسیری که با هر متدی قابل دسترسی باشد، می‌توانید از متد all استفاده کنید:


$router->all('pattern', function() {
    // ...
});
    

نکته: تمام مسیرها باید قبل از فراخوانی $router->run(); تعریف شوند.

الگوهای مسیر (Route Patterns)

الگوهای مسیر می‌توانند استاتیک یا داینامیک باشند:

  • الگوهای استاتیک: رشته‌های ساده‌ای که باید دقیقاً با مسیر URL فعلی مطابقت داشته باشند.
  • الگوهای داینامیک: بخش‌هایی متغیر دارند که با استفاده از عبارات با قاعده (PCRE) یا جایگزین‌کننده‌ها تعریف می‌شوند.

الگوهای مسیر استاتیک (Static Route Patterns)

الگوهای استاتیک رشته‌هایی هستند که مستقیماً با مسیر URL مقایسه می‌شوند.

مثال‌ها:

  • /about
  • /contact

نمونه استفاده:


// این مسیر فقط زمانی اجرا می‌شود که آدرس /about باز شود
$router->get('/about', function() {
    echo 'About Page Contents';
});
    

الگوهای مسیر داینامیک (Dynamic PCRE-based Route Patterns)

الگوهای داینامیک شامل بخش‌های متغیر هستند که با عبارات با قاعده (Regex) تعریف شده‌اند.

مثال‌ها:

  • /movies/(\d+)
  • /profile/(\w+)

زیرالگوهای رایج:

  • \d+ = یک یا چند عدد (0-9)
  • \w+ = یک یا چند کاراکتر کلمه (a-z, 0-9, _)
  • [a-z0-9_-]+ = حروف کوچک، اعداد، خط فاصله یا آندرلاین
  • .* = هر کاراکتری، صفر یا بیشتر
  • [^/]+ = هر چیزی بجز /، یک یا چند بار

نمونه استفاده درست:


// نادرست (نبود پرانتز)
$router->get('/hello/\w+', function($name) {
    echo 'Hello ' . htmlentities($name);
});

// درست (وجود پرانتز)
$router->get('/hello/(\w+)', function($name) {
    echo 'Hello ' . htmlentities($name);
});
    

نکته: اگر چند زیرالگو تعریف شود، پارامترها به ترتیب به تابع مسیریابی ارسال می‌شوند:


$router->get('/movies/(\d+)/photos/(\d+)', function($movieId, $photoId) {
    echo 'Movie #' . $movieId . ', photo #' . $photoId;
});
    

الگوهای مسیر داینامیک با جایگزین‌های ساده

نوع دیگری از تعریف مسیرهای داینامیک در bramus/router استفاده از جایگزین‌های ساده (Placeholders) است. در این حالت به جای استفاده از عبارات باقاعده (Regex)، بخش‌های متغیر مسیر با علامت { } مشخص می‌شوند.

مثال‌ها:


$router->get('/movies/{movieId}/photos/{photoId}', function($movieId, $photoId) {
    echo 'Movie #' . $movieId . ', photo #' . $photoId;
});
    

در اینجا مقدار {movieId} و {photoId} از URL استخراج شده و به تابع پردازش مسیر منتقل می‌شود.

توجه: نام placeholder ها لازم نیست دقیقاً با پارامترهای تابع همخوانی داشته باشند.

$router->get('/movies/{foo}/photos/{bar}', function($movieId, $photoId) {
    echo 'Movie #' . $movieId . ', photo #' . $photoId;
});
    

زیرالگوهای اختیاری در مسیرها

با استفاده از علامت ? می‌توانید بخش‌هایی از مسیر را اختیاری کنید. این ویژگی برای مسیرهایی مانند وبلاگ بسیار کاربردی است:


$router->get('/blog(/\d+)?(/\d+)?(/\d+)?(/[a-z0-9_-]+)?', function($year = null, $month = null, $day = null, $slug = null) {
    // منطق پردازش بر اساس ورودی‌ها
});
    

با این تعریف، مسیرهای زیر پشتیبانی می‌شوند:

  • /blog
  • /blog/2025
  • /blog/2025/04
  • /blog/2025/04/26
  • /blog/2025/04/26/my-article

نکته مهم: بهتر است زیرالگوهای اختیاری را به صورت زنجیروار (داخل هم) بنویسید تا تطبیق دقیق‌تر انجام شود:


$router->get('/blog(/\d4(/\d2(/\d2(/[a-z0-9_-]+)?)?)?)?', function($year = null, $month = null, $day = null, $slug = null) {
    // ...
});
    

زیرمسیریابی (Subrouting) و گروه‌بندی مسیرها

برای گروه‌بندی مجموعه‌ای از مسیرها تحت یک مسیر پایه می‌توانید از mount() استفاده کنید. این روش سازماندهی کد را ساده‌تر می‌کند.


$router->mount('/movies', function() use ($router) {

    // '/movies/'
    $router->get('/', function() {
        echo 'Movies overview';
    });

    // '/movies/id'
    $router->get('/(\d+)', function($id) {
        echo 'Movie id ' . htmlentities($id);
    });

});
    

زیرمسیریابی‌ها می‌توانند تو در تو (nested) نیز باشند.

مسیر به متد کلاس (Class@Method)

در bramus/router می‌توانید به راحتی مسیرها را به متدهای مشخص یک کلاس هدایت کنید:


$router->get('/(\d+)', 'Home@index');
    

در این حالت متد showProfile کلاس User اجرا می‌شود. پارامترهای مسیر به صورت آرگومان به متد ارسال می‌شوند.

توجه: متد می‌تواند استاتیک (توصیه شده) یا غیر استاتیک (توصیه نمی‌شود) باشد.

برای راحتی بیشتر می‌توانید نام‌فضای پیش‌فرض را تنظیم کنید:


$router->setNamespace('Controllers');
$router->get('/users/(\d+)', 'User@showProfile');
$router->get('/cars/(\d+)', 'Car@showProfile');
    

تعریف صفحه ۴۰۴ سفارشی

برای تغییر رفتار پیش‌فرض در صورت عدم تطبیق مسیر، می‌توانید هندلر ۴۰۴ را بازنویسی کنید:


$router->set404(function() {
    header('HTTP/1.1 404 Not Found');
    echo 'Page not found!';
});
    

یا برای مسیرهای خاص مانند API:


$router->set404('/api(/.*)?', function() {
    header('HTTP/1.1 404 Not Found');
    header('Content-Type: application/json');
    echo json_encode(['status' => '404', 'status_text' => 'route not defined']);
});
    

همچنین می‌توانید ۴۰۴ را به متد کلاس هدایت کنید:


$router->set404('Error@notFound');
// یا
$router->set404(function() {
   return View2('404/404') ;
});

    
می‌توانید به صورت دستی با $router->trigger404() هم ۴۰۴ را فعال کنید.

$router->get('/([a-z0-9-]+)', function($id) use ($router) {
    if (!Posts::exists($id)) {
        $router->trigger404();
        return;
    }
    // ...
});
    

میانجی‌ها (Middleware) قبل از پردازش مسیر

Before Route Middleware

با تعریف میانجی‌های مخصوص مسیر، می‌توانید قبل از پردازش مسیرها اقداماتی انجام دهید:


$router->before('GET|POST', '/admin/.*', function() {
    if (!isset($_SESSION['user'])) {
        header('Location: /auth/login');
        exit();
    }
});
// or
$router->before('GET|POST', '/about', function() {\Middlewares\AdminFilter::filter();});
    

Before Router Middleware

برای میانجی‌های سراسری که روی همه درخواست‌ها اجرا می‌شوند:


$router->before('GET', '/.*', function() {
    // این کد برای همه درخواست‌های GET اجرا می‌شود
});
    

After Router Middleware

پس از پردازش مسیرها، می‌توانید یک میانجی نهایی را اجرا کنید:


$router->run(function() {
    // عملیات نهایی پس از اجرا
});
    
توجه: اگر در طول پردازش مسیر، exit() صدا زده شود، این تابع اجرا نخواهد شد.

امکانات پیشرفته

Override کردن متد درخواست

با استفاده از هدر X-HTTP-Method-Override می‌توانید متد درخواست HTTP را در درخواست‌های POST تغییر دهید (مجاز: PUT, DELETE, PATCH).

پشتیبانی از زیرشاخه‌ها

bramus/router به صورت خودکار مسیر را نسبت به محل فایل index.php تنظیم می‌کند. نیازی به mount کردن مسیر دستی نیست.

غیرفعال کردن پشتیبانی زیرشاخه‌ها

در صورت نیاز می‌توانید مسیر پایه را دستی تعیین کنید:


$router->setBasePath('/');
    

با این کار، حتی اگر فایل در زیرشاخه‌ای باشد، مسیرها نسبت به ریشه دامنه بررسی می‌شوند.

ادغام با کتابخانه‌های دیگر

با استفاده از use در توابع مسیر، می‌توانید کتابخانه‌های خارجی را ادغام کنید:


$tpl = new \Acme\Template\Template();

$router->get('/', function() use ($tpl) {
    $tpl->load('home.tpl');
    $tpl->setdata(['name' => 'Bramus!']);
});

$router->run(function() use ($tpl) {
    $tpl->display();
});
    

نکات تکمیلی

کار با متد PUT

در PHP متغیر $_PUT به صورت پیش‌فرض وجود ندارد. برای دریافت داده‌های PUT باید آن را شبیه‌سازی کنید:


$router->put('/movies/(\d+)', function($id) {
    $_PUT = [];
    parse_str(file_get_contents('php://input'), $_PUT);
    // ...
});
    

درخواست‌های HEAD

برای درخواست‌های HEAD خروجی حذف می‌شود تا مطابق با استاندارد RFC2616 باشد.

آزمایش واحد (Unit Testing)

bramus/router دارای تست‌های واحد با PHPUnit است. می‌توانید با اجرای phpunit یا vendor/bin/phpunit تست‌ها را اجرا کنید.

همچنین می‌توانید با دستور phpunit --coverage-html ./tests-report گزارش کد کاوریج تولید کنید (نیاز به XDebug دارد).

\/