PHP - Rota Sistemi
05-07-2015 Okuma Modu
Patron, uzmanı olmadığı alana müdahale ederse kendi işini zorlaştırır. Bu yüzden patron kararları müdürüne bırakıp, şirketinin gidişatını izleye durur. Rota sisteminde de durum aynıdır. PHP, Apache Server'dan tam yetki alıp tüm yönlendirme kontrollerini kendi içinde yaparak kodlarda düzen ve kolaylık sağlar.
MVC tabanlı bir framework ile çalıştıysanız, router dosyasını bilmemeniz imkansızdır. Daha önce framework kullanmamış arkadaşlara kısaca tanıtımını yapayım. Rota Sistemi: link yapısını belirlediğimiz dosyadır. .htaccess
dosyasının gelişmiş versiyonu olarak düşünebilirsiniz.
Konu neydi?
Konuyu çok dallandırıp budaklandırdığımın farkındayım. Bu yazımda sizlere basit bir Route sınıfını anlatacağım. İşleme başlamadan mal sahibi Apache Server'dan tam yetki almamız gerekiyor.
Kök dizininizde .htaccess
dosyası oluşturun varsa düzenleyin.
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
RewriteEngine On
# Tüm istekleri index.php dosyasına yönlendir.
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
Artık tüm linkler index.php
dosyasına gidiyor. Tüm güç php'de olduğuna göre kontrolü de ele almaya başlayalım.
Benim projemin dizin yapısı aşağıdaki gibidir.
- vendor
- src
- rizagunes
- Router
- Macaw.php
- Router
- rizagunes
- .htaccess
- composer.json
- index.php
composer.json
dosyasına aşağıdaki autoload tanımlamasını yaptıktan sonra, sınıfımızı incelemeye başlayalım.
{
"autoload": {
"psr-4": {
"rizagunes\\": "src/rizagunes/"
}
}
}
Bu işlemi yaptıktan sonra composer dump-autoload
komutunu çalıştırmayı ve ardından index.php dosyanızda require_once "vendor/autoload.php";
kod satırı ile projenize sınıfınızını dahil etmeyi unutmayın.
Örneği paylaşmadan önce nasıl çalıştığından kısaca bahsedeyim. $_SERVER['REQUEST_URI']
değişkeni ile sayfanın bulunduğu adres çekilir. Rotalarımızı kaydettiğimiz dizi ile bu değişken karşılaştılır. Cevap true ise rotanın sahip olduğu anonim fonksiyon yada belirtilen sınıf çağrılır.
Macaw.php
<?php
namespace rizagunes\Router;
class Macaw
{
public static $routes = [];
public static $methods = [];
public static $callbacks = [];
//Aşırı Yükleme
public static function __callstatic($method, $params)
{
// Rotaları saklar.
array_push(self::$routes, $params[0]);
// Http yöntemlerini saklar. GET, POST, PUT ...
array_push(self::$methods, strtoupper($method));
//Geri çağırım dizisi, sınıf isimlerini ve anonim fonksiyon parametrelerini saklar.
array_push(self::$callbacks, $params[1]);
}
public static function dispatch()
{
/**
* Eğer projeniz kök dizinde değilse, onun düzeltmesini uygular.
*
* Örneğin: Projeniz projem klasörü altındaysa ve sayfa1 adında rota tanımlarsanız.
* Rotanız /sayfa1, $_SERVER['REQUEST_URI'] değişkeninin karşılığ /projem/sayfa1 olacaktır.
* if(/sayfa1 == /projem/sayfa1) sorgusu false döneceği için rotanız çalışmayacaktır.
* Aşağıdaki kod kümesi $path değişkenine istediğimiz formatı göndercektir.
*/
$req_uri = $_SERVER['REQUEST_URI'];
if (substr($req_uri, 0, strlen(\ROOTFOLDER)+1) === \ROOTFOLDER.'/') {
$path = substr($req_uri, strlen(\ROOTFOLDER));
} else {
$path = $req_uri;
}
$uri = urldecode(parse_url($path, PHP_URL_PATH));
foreach (self::$routes as $routeKey => $route) {
// 404 - Sayfa bulunamadı kontrolü için
$found_route = false;
// preg_match(); Bir düzenli ifadeyi(Regex) eşleştirmeye çalışır.
if ( preg_match('~^'.$route.'$~', $uri, $matched) && self::$methods[$routeKey] == $_SERVER['REQUEST_METHOD']) {
$found_route = true;
$routingDefinition = self::$callbacks[$routeKey];
$parts = explode('/',$matched[0]);
array_shift($parts);
//Anonim fonksiyonu çağırır.
call_user_func_array($routingDefinition, $parts);
//Döngüden Çıkar.
break;
}
}
if ($found_route == false) {
echo "404 - Page Not Found";
}
}
}
index.php
<?php
// Fixleme için gerekli
define("ROOTFOLDER", dirname($_SERVER['SCRIPT_NAME']));
require_once "vendor/autoload.php";
use rizagunes\Router\Macaw as Router;
Router::get('/', function() {
echo 'Hello world!';
});
Router::get('/sayfa/([a-zA-Z]+)', function($isim) {
echo "Gelen değer: ". $isim;
});
Router::get('/uyelik/.*', 'KayitOl');
// Rota kontrol metotunu çağırır.
Router::dispatch();
Bu sınıf bana ait değildir. Yapımcısı sanıyorum bu arkadaştır.
Örneğin iyi anlaşılabilmesi için Macaw sınıfında gördüğüm fazla kodları temizledim ve hataları düzelttim. Size de bir süpriz yumurta bırakarak Controller tanımlamayı yani sınıf tanımlamayı boş bıraktım. Eğer bu zamana kadar yazılarımı okuduysanız kendinizi test etmek için güzel bir an. Ufak da bir ipucu vereyim sınıf içinde new anahtar kelimesini kullanacaksınız.
Hadi kendi framework'ümüzü yazalım !
Router'ımız tamam model view controller katmanını yazdığımızda kendi MVC framework'ümüzü oluşturabiliriz. Fakat buna hiç gerek yok. Çünkü bizim yazacağımız framework baba framework'lerin (Symfony, Laravel, Zend ....) yanında gereksiz bir uğraş olacaktır. Kendiniz yada ekibiniz dışında kimse kullanmayacak, zaten çöplük olan php dünyasını kirletmeye devam edecektir.Bırakın hem kendiniz hem ekip arkadaşlarınız standart haline gelmeye başlamış bu baba framework'lerden birini kullansın.
Lanet olsun sebebi neydi ki?
Hem framework kullanın ısrarı hemde konu anlatımım çelişkiye düşmesin diye açıklama yapayım. Siz framework'lerin nasıl çalıştığını, yapısını kesinlikle öğrenin. Ezber programcı olmayın. Hatta bolca sınıflar geliştirin, baba framework'lerin gelişmesine yardımcı olsun. Laravel bile route sınıfında, symfony'den yararlanmıştır. Daha iyisini yazabileceğinize inandığınız sınıfları yazın fakat bir şeyin kötü kopyasını yapmayın..
Kendinize iyi bakın :)