فلر از کتابخانه bramus/router برای مدیریت مسیرها (Routes) استفاده میکند. این کتابخانه امکان تعریف مسیرهای ساده و پویا را با پشتیبانی از متدهای مختلف HTTP فراهم میکند.
برای تعریف یک مسیر، از متد match
استفاده میشود که ترکیبی از یک یا چند متد HTTP و یک الگوی مسیر (pattern) را میپذیرد:
$router->match('GET|POST', 'pattern', function() {
// ...
});
کتابخانه bramus/router از متدهای GET، POST، PUT، PATCH، DELETE، HEAD و OPTIONS پشتیبانی میکند. میتوانید یک متد تکی یا چند متد را با | جدا کرده و ارسال کنید.
وقتی یک مسیر با URL فعلی (مثلاً $_SERVER['REQUEST_URI']
) مطابقت داشته باشد، تابع متصل به آن مسیر اجرا خواهد شد. تنها اولین مسیری که تطابق داشته باشد اجرا میشود. اگر هیچ مسیری پیدا نشود، هندلر 404 فعال میشود.
برای راحتی بیشتر، میانبرهایی برای متدهای تکی فراهم شده است:
$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();
تعریف شوند.
الگوهای مسیر میتوانند استاتیک یا داینامیک باشند:
الگوهای استاتیک رشتههایی هستند که مستقیماً با مسیر URL مقایسه میشوند.
مثالها:
نمونه استفاده:
// این مسیر فقط زمانی اجرا میشود که آدرس /about باز شود
$router->get('/about', function() {
echo 'About Page Contents';
});
الگوهای داینامیک شامل بخشهای متغیر هستند که با عبارات با قاعده (Regex) تعریف شدهاند.
مثالها:
زیرالگوهای رایج:
\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 استخراج شده و به تابع پردازش مسیر منتقل میشود.
$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) {
// منطق پردازش بر اساس ورودیها
});
با این تعریف، مسیرهای زیر پشتیبانی میشوند:
نکته مهم: بهتر است زیرالگوهای اختیاری را به صورت زنجیروار (داخل هم) بنویسید تا تطبیق دقیقتر انجام شود:
$router->get('/blog(/\d4(/\d2(/\d2(/[a-z0-9_-]+)?)?)?)?', function($year = null, $month = null, $day = null, $slug = null) {
// ...
});
برای گروهبندی مجموعهای از مسیرها تحت یک مسیر پایه میتوانید از 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) نیز باشند.
در 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;
}
// ...
});
با تعریف میانجیهای مخصوص مسیر، میتوانید قبل از پردازش مسیرها اقداماتی انجام دهید:
$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();});
برای میانجیهای سراسری که روی همه درخواستها اجرا میشوند:
$router->before('GET', '/.*', function() {
// این کد برای همه درخواستهای GET اجرا میشود
});
پس از پردازش مسیرها، میتوانید یک میانجی نهایی را اجرا کنید:
$router->run(function() {
// عملیات نهایی پس از اجرا
});
exit()
صدا زده شود، این تابع اجرا نخواهد شد.
با استفاده از هدر 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();
});
در PHP متغیر $_PUT
به صورت پیشفرض وجود ندارد. برای دریافت دادههای PUT باید آن را شبیهسازی کنید:
$router->put('/movies/(\d+)', function($id) {
$_PUT = [];
parse_str(file_get_contents('php://input'), $_PUT);
// ...
});
برای درخواستهای HEAD خروجی حذف میشود تا مطابق با استاندارد RFC2616 باشد.
bramus/router
دارای تستهای واحد با PHPUnit است. میتوانید با اجرای phpunit
یا vendor/bin/phpunit
تستها را اجرا کنید.
همچنین میتوانید با دستور phpunit --coverage-html ./tests-report
گزارش کد کاوریج تولید کنید (نیاز به XDebug دارد).